Skip to content
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

Merged
merged 1 commit into from
Aug 28, 2021
Merged

Webhook notify #1095

merged 1 commit into from
Aug 28, 2021

Conversation

bakurin
Copy link
Contributor

@bakurin bakurin commented Aug 9, 2021

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.

@bakurin bakurin requested a review from umputun as a code owner August 9, 2021 18:43
@umputun umputun requested a review from paskal August 9, 2021 18:44
Copy link
Collaborator

@paskal paskal left a 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!

backend/app/cmd/server.go Outdated Show resolved Hide resolved
backend/app/cmd/server.go Outdated Show resolved Hide resolved
backend/app/cmd/server.go Outdated Show resolved Hide resolved
backend/app/notify/webhook.go Outdated Show resolved Hide resolved
backend/app/notify/webhook.go Outdated Show resolved Hide resolved
backend/app/notify/webhook.go Outdated Show resolved Hide resolved
backend/app/notify/webhook.go Outdated Show resolved Hide resolved
backend/app/notify/webhook.go Outdated Show resolved Hide resolved
backend/app/notify/webhook.go Outdated Show resolved Hide resolved
backend/app/notify/webhook.go Show resolved Hide resolved
@bakurin bakurin requested a review from paskal August 10, 2021 19:58
Copy link
Collaborator

@paskal paskal left a 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.

backend/app/notify/webhook.go Outdated Show resolved Hide resolved
backend/app/notify/webhook_test.go Show resolved Hide resolved
backend/app/notify/webhook.go Show resolved Hide resolved
backend/app/notify/webhook.go Outdated Show resolved Hide resolved
backend/app/notify/webhook.go Show resolved Hide resolved
backend/app/notify/webhook.go Outdated Show resolved Hide resolved
backend/app/notify/webhook.go Show resolved Hide resolved
paskal
paskal previously approved these changes Aug 11, 2021
Copy link
Collaborator

@paskal paskal left a 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.

paskal
paskal previously approved these changes Aug 11, 2021
Copy link
Owner

@umputun umputun left a 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

@@ -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,..."`
Copy link
Owner

@umputun umputun Aug 11, 2021

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

Copy link
Contributor Author

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?

// 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 {
Copy link
Owner

@umputun umputun Aug 11, 2021

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

Copy link
Contributor Author

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

)

const (
webhookTimeOut = 5 * time.Second
Copy link
Owner

@umputun umputun Aug 11, 2021

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

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")
Copy link
Owner

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely

}
}

client := http.Client{Timeout: t.Timeout}
Copy link
Owner

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Makes sense.

defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
respBody, _ := ioutil.ReadAll(resp.Body)
Copy link
Owner

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

Copy link
Contributor Author

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?

Copy link
Owner

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)
     ....
  }
  ....
}

Copy link
Owner

@umputun umputun left a 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

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
Copy link
Owner

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.

Copy link
Contributor Author

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.

Copy link
Owner

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

Copy link
Contributor Author

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?

Copy link
Owner

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's ready

webhookDefaultTemplate = `{"text": "{{.Text}}"}`
)

type webhookClient interface {
Copy link
Owner

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

res.webhookClient = client
res.webhookTemplate = payloadTmpl

log.Printf("[DEBUG] create new Webhook notifier for %s", res.WebhookURL)
Copy link
Owner

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)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return nil
}

func (t *Webhook) doSend(ctx context.Context, payload io.Reader) error {
Copy link
Owner

@umputun umputun Aug 26, 2021

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did merge it.

@umputun
Copy link
Owner

umputun commented Aug 28, 2021

@bakurin pls rebase/squash all the commits from this PR into a single one as you are ready to merge it

@bakurin
Copy link
Contributor Author

bakurin commented Aug 28, 2021

The branch has been squashed/rebased and is ready to merge.

Copy link
Owner

@umputun umputun left a 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

@umputun umputun requested a review from paskal August 28, 2021 16:37
@umputun umputun merged commit be46e84 into umputun:master Aug 28, 2021
@paskal paskal mentioned this pull request Dec 19, 2021
@paskal paskal added this to the v1.9 milestone Jan 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants