-
Notifications
You must be signed in to change notification settings - Fork 1
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
PLATFORM-9039: webhooks reliability #98
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -509,8 +509,7 @@ func (e *WebHook) execute(ctx context.Context, data *templateContext) error { | |
if resp.StatusCode >= http.StatusBadRequest { | ||
span.SetStatus(codes.Error, "HTTP status code >= 400") | ||
if canInterrupt || parseResponse { | ||
// TODO: double-check if we could use upstream `parseWebhookResponse` | ||
if err := e.parseResponse(resp); err != nil { | ||
if err := e.parseWebhookResponse(resp, data.Identity); err != nil { | ||
return err | ||
} | ||
} | ||
|
@@ -524,8 +523,7 @@ func (e *WebHook) execute(ctx context.Context, data *templateContext) error { | |
} | ||
|
||
if parseResponse { | ||
// TODO: double-check if we could use upstream `parseWebhookResponse` | ||
return e.parseResponse(resp) | ||
return e.parseWebhookResponse(resp, data.Identity) | ||
} | ||
return nil | ||
} | ||
|
@@ -540,17 +538,28 @@ func (e *WebHook) execute(ctx context.Context, data *templateContext) error { | |
return nil | ||
} | ||
|
||
func parseWebhookResponse(resp *http.Response, id *identity.Identity) (err error) { | ||
func (e *WebHook) parseWebhookResponse(resp *http.Response, id *identity.Identity) (err error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's try to push it to the upstream There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. otherwise, we won't have an access to the logger There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the logger is only available through |
||
if resp == nil { | ||
return errors.Errorf("empty response provided from the webhook") | ||
} | ||
|
||
// fandom-start | ||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return errors.Wrap(err, "could not read response body") | ||
} | ||
|
||
e.deps.Logger().WithField("response", string(body)).WithField("status_code", resp.StatusCode).Debug("webhook: received response") | ||
// fandom-end | ||
|
||
if resp.StatusCode == http.StatusOK { | ||
var hookResponse struct { | ||
Identity *identity.Identity `json:"identity"` | ||
} | ||
|
||
if err := json.NewDecoder(resp.Body).Decode(&hookResponse); err != nil { | ||
// todo: replace it with json.NewDecoder(resp.Body).Decode(&hookResponse) to be consistent with upstream | ||
// used json.Unmarshal for now, because json.NewDecoder(resp.Body).Decode(&hookResponse) panics | ||
if err := json.Unmarshal(body, &hookResponse); err != nil { | ||
return errors.Wrap(err, "webhook response could not be unmarshalled properly from JSON") | ||
} | ||
|
||
|
@@ -595,7 +604,9 @@ func parseWebhookResponse(resp *http.Response, id *identity.Identity) (err error | |
return nil | ||
} else if resp.StatusCode >= http.StatusBadRequest { | ||
var hookResponse rawHookResponse | ||
if err := json.NewDecoder(resp.Body).Decode(&hookResponse); err != nil { | ||
// todo: replace it with json.NewDecoder(resp.Body).Decode(&hookResponse) to be consistent with upstream | ||
// used json.Unmarshal for now, because json.NewDecoder(resp.Body).Decode(&hookResponse) panics | ||
if err = json.Unmarshal(body, &hookResponse); err != nil { | ||
return errors.Wrap(err, "webhook response could not be unmarshalled properly from JSON") | ||
} | ||
|
||
|
@@ -618,12 +629,16 @@ func parseWebhookResponse(resp *http.Response, id *identity.Identity) (err error | |
} | ||
validationErrs = append(validationErrs, schema.NewHookValidationError(msg.InstancePtr, "a webhook target returned an error", messages)) | ||
} | ||
// fandom-start | ||
validationErr := schema.NewValidationListError(validationErrs) | ||
|
||
if len(validationErrs) == 0 { | ||
e.deps.Logger().WithField("validations", validationErr).Debug("webhook: parsed validations") | ||
return errors.New("error while parsing webhook response: got no validation errors") | ||
} | ||
|
||
return schema.NewValidationListError(validationErrs) | ||
return errors.WithStack(validationErr) | ||
// fandom-end | ||
} | ||
|
||
return nil | ||
|
@@ -633,52 +648,3 @@ func isTimeoutError(err error) bool { | |
var te interface{ Timeout() bool } | ||
return errors.As(err, &te) && te.Timeout() || errors.Is(err, context.DeadlineExceeded) | ||
} | ||
|
||
//nolint:deadcode,unused | ||
func (e *WebHook) parseResponse(resp *http.Response) (err error) { | ||
if resp == nil { | ||
return fmt.Errorf("empty response provided from the webhook") | ||
} | ||
|
||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return errors.Wrap(err, "could not read response body") | ||
} | ||
|
||
hookResponse := &rawHookResponse{ | ||
Messages: []errorMessage{}, | ||
} | ||
|
||
// fandom-start | ||
e.deps.Logger().WithField("response", string(body)).WithField("status_code", resp.StatusCode).Debug("webhook: received response") | ||
// fandom-end | ||
|
||
if err = json.Unmarshal(body, &hookResponse); err != nil { | ||
return errors.Wrap(err, "hook response could not be unmarshalled properly") | ||
} | ||
|
||
var validationErrs []*schema.ValidationError | ||
|
||
for _, msg := range hookResponse.Messages { | ||
messages := text.Messages{} | ||
for _, detail := range msg.DetailedMessages { | ||
messages.Add(&text.Message{ | ||
ID: text.ID(detail.ID), | ||
Text: detail.Text, | ||
Type: text.UITextType(detail.Type), | ||
Context: detail.Context, | ||
}) | ||
} | ||
validationErrs = append(validationErrs, schema.NewHookValidationError(msg.InstancePtr, msg.DetailedMessages[0].Text, messages)) | ||
} | ||
validationErr := schema.NewValidationListError(validationErrs) | ||
|
||
if len(validationErrs) == 0 { | ||
// fandom-start | ||
e.deps.Logger().WithField("validations", validationErr).Debug("webhook: parsed validations") | ||
// fandom-end | ||
return errors.New("error while parsing hook response: got no validation errors") | ||
} | ||
|
||
return errors.WithStack(validationErr) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,9 +84,11 @@ func TestWebHooks(t *testing.T) { | |
whDeps := struct { | ||
x.SimpleLoggerWithClient | ||
*jsonnetsecure.TestProvider | ||
x.ResilientClientProvider | ||
}{ | ||
x.SimpleLoggerWithClient{L: logger, C: reg.HTTPClient(context.Background()), T: otelx.NewNoop(logger, &otelx.Config{ServiceName: "kratos"})}, | ||
jsonnetsecure.NewTestProvider(t), | ||
reg, | ||
} | ||
type WebHookRequest struct { | ||
Body string | ||
|
@@ -934,8 +936,8 @@ func TestWebHooks(t *testing.T) { | |
// error. | ||
|
||
var wg sync.WaitGroup | ||
wg.Add(3) // HTTP client does 3 attempts | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's no longer true assuming this https://github.com/Wikia/kratos/pull/97/files#r1479563845 |
||
ts := newServer(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { | ||
wg.Add(1) | ||
defer wg.Done() | ||
w.WriteHeader(500) | ||
_, _ = w.Write([]byte(`{"error":"some error"}`)) | ||
|
@@ -1011,9 +1013,11 @@ func TestDisallowPrivateIPRanges(t *testing.T) { | |
whDeps := struct { | ||
x.SimpleLoggerWithClient | ||
*jsonnetsecure.TestProvider | ||
x.ResilientClientProvider | ||
}{ | ||
x.SimpleLoggerWithClient{L: logger, C: reg.HTTPClient(context.Background()), T: otelx.NewNoop(logger, &otelx.Config{ServiceName: "kratos"})}, | ||
jsonnetsecure.NewTestProvider(t), | ||
reg, | ||
} | ||
|
||
req := &http.Request{ | ||
|
@@ -1081,9 +1085,11 @@ func TestAsyncWebhook(t *testing.T) { | |
whDeps := struct { | ||
x.SimpleLoggerWithClient | ||
*jsonnetsecure.TestProvider | ||
x.ResilientClientProvider | ||
}{ | ||
x.SimpleLoggerWithClient{L: logger, C: reg.HTTPClient(context.Background()), T: otelx.NewNoop(logger, &otelx.Config{ServiceName: "kratos"})}, | ||
jsonnetsecure.NewTestProvider(t), | ||
reg, | ||
} | ||
|
||
req := &http.Request{ | ||
|
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.
context: https://github.com/Wikia/kratos/pull/97/files#r1478195394