mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-47953] Outgoing webhook does not trigger when using multiple callback URLs (#22394)
Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com>
This commit is contained in:
parent
5dbf874d1e
commit
f8fc02791b
@ -95,23 +95,30 @@ func (a *App) handleWebhookEvents(c request.CTX, post *model.Post, team *model.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) TriggerWebhook(c request.CTX, payload *model.OutgoingWebhookPayload, hook *model.OutgoingWebhook, post *model.Post, channel *model.Channel) {
|
func (a *App) TriggerWebhook(c request.CTX, payload *model.OutgoingWebhookPayload, hook *model.OutgoingWebhook, post *model.Post, channel *model.Channel) {
|
||||||
var body io.Reader
|
var jsonBytes []byte
|
||||||
var contentType string
|
var err error
|
||||||
|
|
||||||
|
contentType := "application/x-www-form-urlencoded"
|
||||||
if hook.ContentType == "application/json" {
|
if hook.ContentType == "application/json" {
|
||||||
js, err := json.Marshal(payload)
|
contentType = "application/json"
|
||||||
|
jsonBytes, err = json.Marshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger().Warn("Failed to encode to JSON", mlog.Err(err))
|
c.Logger().Warn("Failed to encode to JSON", mlog.Err(err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
body = bytes.NewReader(js)
|
|
||||||
contentType = "application/json"
|
|
||||||
} else {
|
|
||||||
body = strings.NewReader(payload.ToFormValues())
|
|
||||||
contentType = "application/x-www-form-urlencoded"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
for i := range hook.CallbackURLs {
|
for i := range hook.CallbackURLs {
|
||||||
|
var body io.Reader
|
||||||
|
if hook.ContentType == "application/json" {
|
||||||
|
body = bytes.NewReader(jsonBytes)
|
||||||
|
} else {
|
||||||
|
body = strings.NewReader(payload.ToFormValues())
|
||||||
|
}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
// Get the callback URL by index to properly capture it for the go func
|
// Get the callback URL by index to properly capture it for the go func
|
||||||
url := hook.CallbackURLs[i]
|
url := hook.CallbackURLs[i]
|
||||||
|
|
||||||
|
@ -756,6 +756,101 @@ func TestTriggerOutGoingWebhookWithUsernameAndIconURL(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTriggerOutGoingWebhookWithMultipleURLs(t *testing.T) {
|
||||||
|
getPayload := func(hook *model.OutgoingWebhook, th *TestHelper, channel *model.Channel) *model.OutgoingWebhookPayload {
|
||||||
|
return &model.OutgoingWebhookPayload{
|
||||||
|
Token: hook.Token,
|
||||||
|
TeamId: hook.TeamId,
|
||||||
|
TeamDomain: th.BasicTeam.Name,
|
||||||
|
ChannelId: channel.Id,
|
||||||
|
ChannelName: channel.Name,
|
||||||
|
Timestamp: th.BasicPost.CreateAt,
|
||||||
|
UserId: th.BasicPost.UserId,
|
||||||
|
UserName: th.BasicUser.Username,
|
||||||
|
PostId: th.BasicPost.Id,
|
||||||
|
Text: th.BasicPost.Message,
|
||||||
|
TriggerWord: "Abracadabra",
|
||||||
|
FileIds: strings.Join(th.BasicPost.FileIds, ","),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createOutgoingWebhook := func(channel *model.Channel, testCallBackURLs []string, th *TestHelper) (*model.OutgoingWebhook, *model.AppError) {
|
||||||
|
outgoingWebhook := model.OutgoingWebhook{
|
||||||
|
ChannelId: channel.Id,
|
||||||
|
TeamId: channel.TeamId,
|
||||||
|
CallbackURLs: testCallBackURLs,
|
||||||
|
Username: "some-user-name",
|
||||||
|
IconURL: "http://some-website.com/assets/some-icon.png",
|
||||||
|
DisplayName: "some-display-name",
|
||||||
|
Description: "some-description",
|
||||||
|
CreatorId: th.BasicUser.Id,
|
||||||
|
TriggerWords: []string{"Abracadabra"},
|
||||||
|
ContentType: "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
return th.App.CreateOutgoingWebhook(&outgoingWebhook)
|
||||||
|
}
|
||||||
|
|
||||||
|
chanTs1 := make(chan string, 1)
|
||||||
|
ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
chanTs1 <- "webhook received!"
|
||||||
|
}))
|
||||||
|
defer ts1.Close()
|
||||||
|
|
||||||
|
chanTs2 := make(chan string, 1)
|
||||||
|
ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
chanTs2 <- "webhook received!"
|
||||||
|
}))
|
||||||
|
defer ts2.Close()
|
||||||
|
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost,127.0.0.1"
|
||||||
|
})
|
||||||
|
|
||||||
|
for name, testCase := range map[string]struct {
|
||||||
|
CallBackURLs []string
|
||||||
|
}{
|
||||||
|
"One WebhookURL": {
|
||||||
|
CallBackURLs: []string{ts1.URL},
|
||||||
|
},
|
||||||
|
"Two WebhookURLs": {
|
||||||
|
CallBackURLs: []string{ts1.URL, ts2.URL},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.ServiceSettings.EnableOutgoingWebhooks = true
|
||||||
|
})
|
||||||
|
channel := th.CreateChannel(th.Context, th.BasicTeam)
|
||||||
|
hook, _ := createOutgoingWebhook(channel, testCase.CallBackURLs, th)
|
||||||
|
payload := getPayload(hook, th, channel)
|
||||||
|
|
||||||
|
th.App.TriggerWebhook(th.Context, payload, hook, th.BasicPost, channel)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case webhookResponse := <-chanTs1:
|
||||||
|
require.Equal(t, "webhook received!", webhookResponse)
|
||||||
|
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
require.Fail(t, "Timeout, webhook URL 1 response not received")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(testCase.CallBackURLs) > 1 {
|
||||||
|
select {
|
||||||
|
case webhookResponse := <-chanTs2:
|
||||||
|
require.Equal(t, "webhook received!", webhookResponse)
|
||||||
|
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
require.Fail(t, "Timeout, webhook URL 2 response not received")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type InfiniteReader struct {
|
type InfiniteReader struct {
|
||||||
Prefix string
|
Prefix string
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user