[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:
Sundaran Kumar 2024-04-08 11:25:03 +02:00 committed by GitHub
parent 5dbf874d1e
commit f8fc02791b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 110 additions and 8 deletions

View File

@ -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) {
var body io.Reader
var contentType string
var jsonBytes []byte
var err error
contentType := "application/x-www-form-urlencoded"
if hook.ContentType == "application/json" {
js, err := json.Marshal(payload)
contentType = "application/json"
jsonBytes, err = json.Marshal(payload)
if err != nil {
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
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)
// Get the callback URL by index to properly capture it for the go func
url := hook.CallbackURLs[i]

View File

@ -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 {
Prefix string
}