-
-
Notifications
You must be signed in to change notification settings - Fork 386
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Webhook notify #1095
Webhook notify #1095
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bunch of minor things here and there, generally looks good. Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comments from previous time, plus I reviewed the test coverage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one more thing, please write about this new notification method for admins in the Readme and present its command-line options in the table there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thx. just a few questions & suggestions
backend/app/cmd/server.go
Outdated
@@ -230,6 +230,12 @@ type NotifyGroup struct { | |||
Token string `long:"token" env:"TOKEN" description:"slack token"` | |||
Channel string `long:"chan" env:"CHAN" description:"slack channel"` | |||
} `group:"slack" namespace:"slack" env-namespace:"SLACK"` | |||
Webhook struct { | |||
WebhookURL string `long:"url" env:"URL" description:"webhook notification URL"` | |||
Headers string `long:"headers" env:"HEADERS" description:"webhook authentication headers in format --notify.webhook.headers=Header1:Value1,Header2:Value2,..."` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if combining headers into a string and splitting it after is such a good idea. Is there a reason not to have Headers as []string ?
btw, this is how i did it in in reproxy https://github.com/umputun/reproxy/blob/master/app/main.go#L36
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like []string
idea. I brought splitAtCommas
from reproxy project. Added it to server.go
. Is that OK?
backend/app/notify/webhook.go
Outdated
// where key is an HTTP header name, and value is a list of header values | ||
// For example parameter string Header1:Value1,Header1:Value2,Header2:Value3 | ||
// will produce a map {Header1: [Value1, Value2], Header2: [Value3]} | ||
func buildHeaders(opt string) webhookHeaders { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it will survive a complex headers, like this one:
Access-Control-Allow-Headers:"DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type"
see the similar issue here umputun/reproxy#100 and the solution proposed and implemented
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's go with splitAtCommas
instead
backend/app/notify/webhook.go
Outdated
) | ||
|
||
const ( | ||
webhookTimeOut = 5 * time.Second |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: probably calling it "default" will clarify the intent better, i.e. webhookDefaultTimeOut
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
backend/app/notify/webhook.go
Outdated
func NewWebhook(params WebhookParams) (*Webhook, error) { | ||
res := &Webhook{WebhookParams: params} | ||
if res.WebhookURL == "" { | ||
return nil, errors.New("webhook webhook URL is required for webhook notifications") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"webhook webhook" looks like dbl by mistake
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely
backend/app/notify/webhook.go
Outdated
} | ||
} | ||
|
||
client := http.Client{Timeout: t.Timeout} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not a big fan of making http client directly. Passing it as dependency will allow to redefine the client with custom transport or even use https://github.com/go-pkgz/requester to provide advanced things like repeater, limiter, circuit breaker and so on. As long as Webhook accept injection of smth with Do(req) interface the client can be enhanced from the outside
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. Makes sense.
backend/app/notify/webhook.go
Outdated
defer resp.Body.Close() | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
respBody, _ := ioutil.ReadAll(resp.Body) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any good reason why the error ignored here? if connection failed in the middle this call may fail
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The request body is no big deal here. It is used only for debugging to make the error messages more informative. How would you suggest handling it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if this is dbg only, probably smth like this?
if resp.StatusCode != http.StatusOK {
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("[DEBUG] can't blah blah, %v", err)
....
}
....
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, missed it.
Just a few minor things
backend/app/cmd/server.go
Outdated
client := &http.Client{Timeout: 5 * time.Second} | ||
webhookHeaders := s.Notify.Webhook.Headers | ||
if len(webhookHeaders) == 0 { | ||
webhookHeaders = splitAtCommas(os.Getenv("HEADER")) // env value may have comma inside "", parsed separately |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this env name seems to be borrowed from the reproxy as-is. In remark42 this likely will be NOTIFY_WEBHOOK_HEADERS to make it similar to all the automatic evn parameters used everywhere. Another thing - this was easy to miss because there is no integration tests for something like TestMain_WithWebhook. I think making such a test should be doable and will be very nice to have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, I copy-pasted the code snippet and forgot to change the var name. Regarding the integration test, I didn't find any example in the project, so please correct me if my implementation looks weird.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all tests in cmd/server_test.go are integration tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I saw them. But idea was to test webhook notifier initialization based on ENV variables, wasn't it? Do you think it doesn't worth it to have such tests in the main
package?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, this test is helpful. Usually, we don't test passing params via env as this is handled by flags library but this case is different due to the custom parsing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's ready
backend/app/notify/webhook.go
Outdated
webhookDefaultTemplate = `{"text": "{{.Text}}"}` | ||
) | ||
|
||
type webhookClient interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is kind of unusual to define a private interface for the injection. I would suggest making this public as it will communicate intent better
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
backend/app/notify/webhook.go
Outdated
res.webhookClient = client | ||
res.webhookTemplate = payloadTmpl | ||
|
||
log.Printf("[DEBUG] create new Webhook notifier for %s", res.WebhookURL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: inconsistent, as in other places it is webhook (lower-case w)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
backend/app/notify/webhook.go
Outdated
return nil | ||
} | ||
|
||
func (t *Webhook) doSend(ctx context.Context, payload io.Reader) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if doSend should be even a function and not part of the Send. Is there a good reason to isolate the code? it is not doing just send anyway as it prepares request too. I'd rather put all of this to Send but this is just a minor nit, up to you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did merge it.
@bakurin pls rebase/squash all the commits from this PR into a single one as you are ready to merge it |
f5ff2f1
to
0896bf4
Compare
The branch has been squashed/rebased and is ready to merge. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. The missing part is some documentation describing the new functionality, but it can be added separately
thx
This is a PR for the requested feature [#924]
There are not many details in the request, so feel free to specify any requirements. Any feedback is appreciated.