From 97960a59308a8781e44b1cb86d52590ba771947f Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Thu, 14 May 2020 10:47:30 -0600 Subject: [PATCH] feat(http): Client disconnections should return a HTTP 499 error code. This commit checks http.Request.Context().Err() to see if the context has been canceled before writing an error code. It uses the non-standard Nginx 499 error code for client disconnection. --- authorization/http_server.go | 50 ++++++++++----------- http/bucket_service.go | 52 +++++++++++----------- http/org_service.go | 68 ++++++++++++++--------------- kit/transport/http/api.go | 21 ++++----- kit/transport/http/api_test.go | 4 +- kit/transport/http/error_handler.go | 15 +++++-- pkger/http_server.go | 58 ++++++++++++------------ session/http_server.go | 12 ++--- tenant/http_handler_urm.go | 20 ++++----- tenant/http_server.go | 4 +- tenant/http_server_bucket.go | 40 ++++++++--------- tenant/http_server_onboarding.go | 16 +++---- tenant/http_server_org.go | 30 ++++++------- tenant/http_server_user.go | 44 +++++++++---------- 14 files changed, 222 insertions(+), 212 deletions(-) diff --git a/authorization/http_server.go b/authorization/http_server.go index 8c3046e28b..aaac9fe95f 100644 --- a/authorization/http_server.go +++ b/authorization/http_server.go @@ -75,13 +75,13 @@ func (h *AuthHandler) handlePostAuthorization(w http.ResponseWriter, r *http.Req ctx := r.Context() a, err := decodePostAuthorizationRequest(ctx, r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } user, err := getAuthorizedUser(r, h.tenantService) if err != nil { - h.api.Err(w, influxdb.ErrUnableToCreateToken) + h.api.Err(w, r, influxdb.ErrUnableToCreateToken) return } @@ -93,13 +93,13 @@ func (h *AuthHandler) handlePostAuthorization(w http.ResponseWriter, r *http.Req auth := a.toInfluxdb(userID) if err := h.authSvc.CreateAuthorization(ctx, auth); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } perms, err := newPermissionsResponse(ctx, auth.Permissions, h.lookupService) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -107,11 +107,11 @@ func (h *AuthHandler) handlePostAuthorization(w http.ResponseWriter, r *http.Req resp, err := h.newAuthResponse(ctx, auth, perms) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } - h.api.Respond(w, http.StatusCreated, resp) + h.api.Respond(w, r, http.StatusCreated, resp) } func getAuthorizedUser(r *http.Request, ts TenantService) (*influxdb.User, error) { @@ -352,7 +352,7 @@ func (h *AuthHandler) handleGetAuthorizations(w http.ResponseWriter, r *http.Req req, err := decodeGetAuthorizationsRequest(ctx, r) if err != nil { h.log.Info("Failed to decode request", zap.String("handler", "getAuthorizations"), zap.Error(err)) - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -360,7 +360,7 @@ func (h *AuthHandler) handleGetAuthorizations(w http.ResponseWriter, r *http.Req as, _, err := h.authSvc.FindAuthorizations(ctx, req.filter, opts) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -369,7 +369,7 @@ func (h *AuthHandler) handleGetAuthorizations(w http.ResponseWriter, r *http.Req if f.User != nil { u, err := h.tenantService.FindUser(ctx, influxdb.UserFilter{Name: f.User}) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } f.UserID = &u.ID @@ -378,7 +378,7 @@ func (h *AuthHandler) handleGetAuthorizations(w http.ResponseWriter, r *http.Req if f.Org != nil { o, err := h.tenantService.FindOrganization(ctx, influxdb.OrganizationFilter{Name: f.Org}) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } f.OrgID = &o.ID @@ -388,7 +388,7 @@ func (h *AuthHandler) handleGetAuthorizations(w http.ResponseWriter, r *http.Req for _, a := range as { ps, err := newPermissionsResponse(ctx, a.Permissions, h.lookupService) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -402,7 +402,7 @@ func (h *AuthHandler) handleGetAuthorizations(w http.ResponseWriter, r *http.Req h.log.Debug("Auths retrieved ", zap.String("auths", fmt.Sprint(auths))) - h.api.Respond(w, http.StatusOK, newAuthsResponse(auths)) + h.api.Respond(w, r, http.StatusOK, newAuthsResponse(auths)) } type getAuthorizationsRequest struct { @@ -460,20 +460,20 @@ func (h *AuthHandler) handleGetAuthorization(w http.ResponseWriter, r *http.Requ id, err := influxdb.IDFromString(chi.URLParam(r, "id")) if err != nil { h.log.Info("Failed to decode request", zap.String("handler", "getAuthorization"), zap.Error(err)) - h.api.Err(w, err) + h.api.Err(w, r, err) return } a, err := h.authSvc.FindAuthorizationByID(ctx, *id) if err != nil { // Don't log here, it should already be handled by the service - h.api.Err(w, err) + h.api.Err(w, r, err) return } ps, err := newPermissionsResponse(ctx, a.Permissions, h.lookupService) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -481,11 +481,11 @@ func (h *AuthHandler) handleGetAuthorization(w http.ResponseWriter, r *http.Requ resp, err := h.newAuthResponse(ctx, a, ps) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } - h.api.Respond(w, http.StatusOK, resp) + h.api.Respond(w, r, http.StatusOK, resp) } // handleUpdateAuthorization is the HTTP handler for the PATCH /api/v2/authorizations/:id route that updates the authorization's status and desc. @@ -494,36 +494,36 @@ func (h *AuthHandler) handleUpdateAuthorization(w http.ResponseWriter, r *http.R req, err := decodeUpdateAuthorizationRequest(ctx, r) if err != nil { h.log.Info("Failed to decode request", zap.String("handler", "updateAuthorization"), zap.Error(err)) - h.api.Err(w, err) + h.api.Err(w, r, err) return } a, err := h.authSvc.FindAuthorizationByID(ctx, req.ID) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } a, err = h.authSvc.UpdateAuthorization(ctx, a.ID, req.AuthorizationUpdate) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } ps, err := newPermissionsResponse(ctx, a.Permissions, h.lookupService) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Auth updated", zap.String("auth", fmt.Sprint(a))) resp, err := h.newAuthResponse(ctx, a, ps) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } - h.api.Respond(w, http.StatusOK, resp) + h.api.Respond(w, r, http.StatusOK, resp) } type updateAuthorizationRequest struct { @@ -553,13 +553,13 @@ func (h *AuthHandler) handleDeleteAuthorization(w http.ResponseWriter, r *http.R id, err := influxdb.IDFromString(chi.URLParam(r, "id")) if err != nil { h.log.Info("Failed to decode request", zap.String("handler", "deleteAuthorization"), zap.Error(err)) - h.api.Err(w, err) + h.api.Err(w, r, err) return } if err := h.authSvc.DeleteAuthorization(r.Context(), *id); err != nil { // Don't log here, it should already be handled by the service - h.api.Err(w, err) + h.api.Err(w, r, err) return } diff --git a/http/bucket_service.go b/http/bucket_service.go index 44dabb4362..e47c10f7c7 100644 --- a/http/bucket_service.go +++ b/http/bucket_service.go @@ -321,18 +321,18 @@ func newBucketsResponse(ctx context.Context, opts influxdb.FindOptions, f influx func (h *BucketHandler) handlePostBucket(w http.ResponseWriter, r *http.Request) { var b postBucketRequest if err := h.api.DecodeJSON(r.Body, &b); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } bucket := b.toInfluxDB() if err := h.BucketService.CreateBucket(r.Context(), bucket); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Bucket created", zap.String("bucket", fmt.Sprint(bucket))) - h.api.Respond(w, http.StatusCreated, NewBucketResponse(bucket, []*influxdb.Label{})) + h.api.Respond(w, r, http.StatusCreated, NewBucketResponse(bucket, []*influxdb.Label{})) } type postBucketRequest struct { @@ -395,25 +395,25 @@ func (h *BucketHandler) handleGetBucket(w http.ResponseWriter, r *http.Request) id, err := decodeIDFromCtx(r.Context(), "id") if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } b, err := h.BucketService.FindBucketByID(ctx, id) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } labels, err := h.LabelService.FindResourceLabels(ctx, influxdb.LabelMappingFilter{ResourceID: b.ID, ResourceType: influxdb.BucketsResourceType}) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Bucket retrieved", zap.String("bucket", fmt.Sprint(b))) - h.api.Respond(w, http.StatusOK, NewBucketResponse(b, labels)) + h.api.Respond(w, r, http.StatusOK, NewBucketResponse(b, labels)) } func bucketIDPath(id influxdb.ID) string { @@ -424,25 +424,25 @@ func bucketIDPath(id influxdb.ID) string { func (h *BucketHandler) handleGetBucketLog(w http.ResponseWriter, r *http.Request) { id, err := decodeIDFromCtx(r.Context(), "id") if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } opts, err := influxdb.DecodeFindOptions(r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } log, _, err := h.BucketOperationLogService.GetBucketOperationLog(r.Context(), id, *opts) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Bucket log retrived", zap.String("bucket", fmt.Sprint(log))) - h.api.Respond(w, http.StatusOK, newBucketLogResponse(id, log)) + h.api.Respond(w, r, http.StatusOK, newBucketLogResponse(id, log)) } func newBucketLogResponse(id influxdb.ID, es []*influxdb.OperationLogEntry) *operationLogResponse { @@ -462,18 +462,18 @@ func newBucketLogResponse(id influxdb.ID, es []*influxdb.OperationLogEntry) *ope func (h *BucketHandler) handleDeleteBucket(w http.ResponseWriter, r *http.Request) { id, err := decodeIDFromCtx(r.Context(), "id") if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } if err := h.BucketService.DeleteBucket(r.Context(), id); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Bucket deleted", zap.String("bucketID", id.String())) - h.api.Respond(w, http.StatusNoContent, nil) + h.api.Respond(w, r, http.StatusNoContent, nil) } // handleGetBuckets is the HTTP handler for the GET /api/v2/buckets route. @@ -489,7 +489,7 @@ func (h *BucketHandler) handleGetBuckets(w http.ResponseWriter, r *http.Request) bucketID, err := decodeIDFromQuery(q, "id") if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } if bucketID > 0 { @@ -498,7 +498,7 @@ func (h *BucketHandler) handleGetBuckets(w http.ResponseWriter, r *http.Request) orgID, err := decodeIDFromQuery(q, "orgID") if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } if orgID > 0 { @@ -507,18 +507,18 @@ func (h *BucketHandler) handleGetBuckets(w http.ResponseWriter, r *http.Request) opts, err := influxdb.DecodeFindOptions(r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } bs, _, err := h.BucketService.FindBuckets(r.Context(), filter, *opts) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Buckets retrieved", zap.String("buckets", fmt.Sprint(bs))) - h.api.Respond(w, http.StatusOK, newBucketsResponse(r.Context(), *opts, filter, bs, h.LabelService)) + h.api.Respond(w, r, http.StatusOK, newBucketsResponse(r.Context(), *opts, filter, bs, h.LabelService)) } type getBucketsRequest struct { @@ -568,32 +568,32 @@ func decodeGetBucketsRequest(r *http.Request) (*getBucketsRequest, error) { func (h *BucketHandler) handlePatchBucket(w http.ResponseWriter, r *http.Request) { id, err := decodeIDFromCtx(r.Context(), "id") if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } var reqBody bucketUpdate if err := h.api.DecodeJSON(r.Body, &reqBody); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } if reqBody.Name != nil { b, err := h.BucketService.FindBucketByID(r.Context(), id) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } b.Name = *reqBody.Name if err := validBucketName(b); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } } b, err := h.BucketService.UpdateBucket(r.Context(), id, *reqBody.toInfluxDB()) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -604,12 +604,12 @@ func (h *BucketHandler) handlePatchBucket(w http.ResponseWriter, r *http.Request ResourceType: influxdb.BucketsResourceType, }) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Bucket updated", zap.String("bucket", fmt.Sprint(b))) - h.api.Respond(w, http.StatusOK, NewBucketResponse(b, labels)) + h.api.Respond(w, r, http.StatusOK, NewBucketResponse(b, labels)) } // BucketService connects to Influx via HTTP using tokens to manage buckets diff --git a/http/org_service.go b/http/org_service.go index b5596891d3..6d9df0a3c1 100644 --- a/http/org_service.go +++ b/http/org_service.go @@ -79,12 +79,12 @@ func checkOrganizationExists(orgHandler *OrgHandler) kithttp.Middleware { ctx := r.Context() orgID, err := decodeIDFromCtx(ctx, "id") if err != nil { - orgHandler.API.Err(w, err) + orgHandler.API.Err(w, r, err) return } if _, err := orgHandler.OrgSVC.FindOrganizationByID(ctx, orgID); err != nil { - orgHandler.API.Err(w, err) + orgHandler.API.Err(w, r, err) return } next.ServeHTTP(w, r) @@ -210,35 +210,35 @@ func newOrgResponse(o influxdb.Organization) orgResponse { func (h *OrgHandler) handlePostOrg(w http.ResponseWriter, r *http.Request) { var org influxdb.Organization if err := h.API.DecodeJSON(r.Body, &org); err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } if err := h.OrgSVC.CreateOrganization(r.Context(), &org); err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } h.log.Debug("Org created", zap.String("org", fmt.Sprint(org))) - h.API.Respond(w, http.StatusCreated, newOrgResponse(org)) + h.API.Respond(w, r, http.StatusCreated, newOrgResponse(org)) } // handleGetOrg is the HTTP handler for the GET /api/v2/orgs/:id route. func (h *OrgHandler) handleGetOrg(w http.ResponseWriter, r *http.Request) { id, err := decodeIDFromCtx(r.Context(), "id") if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } org, err := h.OrgSVC.FindOrganizationByID(r.Context(), id) if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } h.log.Debug("Org retrieved", zap.String("org", fmt.Sprint(org))) - h.API.Respond(w, http.StatusOK, newOrgResponse(*org)) + h.API.Respond(w, r, http.StatusOK, newOrgResponse(*org)) } // handleGetOrgs is the HTTP handler for the GET /api/v2/orgs route. @@ -251,7 +251,7 @@ func (h *OrgHandler) handleGetOrgs(w http.ResponseWriter, r *http.Request) { if orgID := qp.Get("orgID"); orgID != "" { id, err := influxdb.IDFromString(orgID) if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } filter.ID = id @@ -260,7 +260,7 @@ func (h *OrgHandler) handleGetOrgs(w http.ResponseWriter, r *http.Request) { if userID := qp.Get("userID"); userID != "" { id, err := influxdb.IDFromString(userID) if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } filter.UserID = id @@ -268,54 +268,54 @@ func (h *OrgHandler) handleGetOrgs(w http.ResponseWriter, r *http.Request) { orgs, _, err := h.OrgSVC.FindOrganizations(r.Context(), filter) if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } h.log.Debug("Orgs retrieved", zap.String("org", fmt.Sprint(orgs))) - h.API.Respond(w, http.StatusOK, newOrgsResponse(orgs)) + h.API.Respond(w, r, http.StatusOK, newOrgsResponse(orgs)) } // handleDeleteOrganization is the HTTP handler for the DELETE /api/v2/orgs/:id route. func (h *OrgHandler) handleDeleteOrg(w http.ResponseWriter, r *http.Request) { id, err := decodeIDFromCtx(r.Context(), "id") if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } ctx := r.Context() if err := h.OrgSVC.DeleteOrganization(ctx, id); err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } h.log.Debug("Org deleted", zap.String("orgID", fmt.Sprint(id))) - h.API.Respond(w, http.StatusNoContent, nil) + h.API.Respond(w, r, http.StatusNoContent, nil) } // handlePatchOrg is the HTTP handler for the PATH /api/v2/orgs route. func (h *OrgHandler) handlePatchOrg(w http.ResponseWriter, r *http.Request) { id, err := decodeIDFromCtx(r.Context(), "id") if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } var upd influxdb.OrganizationUpdate if err := h.API.DecodeJSON(r.Body, &upd); err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } org, err := h.OrgSVC.UpdateOrganization(r.Context(), id, upd) if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } h.log.Debug("Org updated", zap.String("org", fmt.Sprint(org))) - h.API.Respond(w, http.StatusOK, newOrgResponse(*org)) + h.API.Respond(w, r, http.StatusOK, newOrgResponse(*org)) } type secretsResponse struct { @@ -340,39 +340,39 @@ func newSecretsResponse(orgID influxdb.ID, ks []string) *secretsResponse { func (h *OrgHandler) handleGetSecrets(w http.ResponseWriter, r *http.Request) { orgID, err := decodeIDFromCtx(r.Context(), "id") if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } ks, err := h.SecretService.GetSecretKeys(r.Context(), orgID) if err != nil && influxdb.ErrorCode(err) != influxdb.ENotFound { - h.API.Err(w, err) + h.API.Err(w, r, err) return } - h.API.Respond(w, http.StatusOK, newSecretsResponse(orgID, ks)) + h.API.Respond(w, r, http.StatusOK, newSecretsResponse(orgID, ks)) } // handleGetPatchSecrets is the HTTP handler for the PATCH /api/v2/orgs/:id/secrets route. func (h *OrgHandler) handlePatchSecrets(w http.ResponseWriter, r *http.Request) { orgID, err := decodeIDFromCtx(r.Context(), "id") if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } var secrets map[string]string if err := h.API.DecodeJSON(r.Body, &secrets); err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } if err := h.SecretService.PatchSecrets(r.Context(), orgID, secrets); err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } - h.API.Respond(w, http.StatusNoContent, nil) + h.API.Respond(w, r, http.StatusNoContent, nil) } type secretsDeleteBody struct { @@ -383,47 +383,47 @@ type secretsDeleteBody struct { func (h *OrgHandler) handleDeleteSecrets(w http.ResponseWriter, r *http.Request) { orgID, err := decodeIDFromCtx(r.Context(), "id") if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } var reqBody secretsDeleteBody if err := h.API.DecodeJSON(r.Body, &reqBody); err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } if err := h.SecretService.DeleteSecret(r.Context(), orgID, reqBody.Secrets...); err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } - h.API.Respond(w, http.StatusNoContent, nil) + h.API.Respond(w, r, http.StatusNoContent, nil) } // hanldeGetOrganizationLog retrieves a organization log by the organizations ID. func (h *OrgHandler) handleGetOrgLog(w http.ResponseWriter, r *http.Request) { orgID, err := decodeIDFromCtx(r.Context(), "id") if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } opts, err := influxdb.DecodeFindOptions(r) if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } log, _, err := h.OrganizationOperationLogService.GetOrganizationOperationLog(r.Context(), orgID, *opts) if err != nil { - h.API.Err(w, err) + h.API.Err(w, r, err) return } h.log.Debug("Org logs retrieved", zap.String("log", fmt.Sprint(log))) - h.API.Respond(w, http.StatusOK, newOrganizationLogResponse(orgID, log)) + h.API.Respond(w, r, http.StatusOK, newOrganizationLogResponse(orgID, log)) } func newOrganizationLogResponse(id influxdb.ID, es []*influxdb.OperationLogEntry) *operationLogResponse { diff --git a/kit/transport/http/api.go b/kit/transport/http/api.go index eac985f499..f004560739 100644 --- a/kit/transport/http/api.go +++ b/kit/transport/http/api.go @@ -2,6 +2,7 @@ package http import ( "compress/gzip" + "context" "encoding/gob" "encoding/json" "fmt" @@ -26,7 +27,7 @@ type API struct { unmarshalErrFn func(encoding string, err error) error okErrFn func(err error) error - errFn func(err error) (interface{}, int, error) + errFn func(ctx context.Context, err error) (interface{}, int, error) } // APIOptFn is a functional option for setting fields on the API type. @@ -40,7 +41,7 @@ func WithLog(logger *zap.Logger) APIOptFn { } // WithErrFn sets the err handling func for issues when writing to the response body. -func WithErrFn(fn func(err error) (interface{}, int, error)) APIOptFn { +func WithErrFn(fn func(ctx context.Context, err error) (interface{}, int, error)) APIOptFn { return func(api *API) { api.errFn = fn } @@ -86,7 +87,7 @@ func NewAPI(opts ...APIOptFn) *API { Msg: fmt.Sprintf("failed to unmarshal %s: %s", encoding, err), } }, - errFn: func(err error) (interface{}, int, error) { + errFn: func(ctx context.Context, err error) (interface{}, int, error) { msg := err.Error() if msg == "" { msg = "an internal error has occurred" @@ -95,7 +96,7 @@ func NewAPI(opts ...APIOptFn) *API { return ErrBody{ Code: code, Msg: msg, - }, ErrorCodeToStatusCode(code), nil + }, ErrorCodeToStatusCode(ctx, code), nil }, } for _, o := range opts { @@ -144,7 +145,7 @@ func (a *API) decode(encoding string, dec decoder, v interface{}) error { } // Respond writes to the response writer, handling all errors in writing. -func (a *API) Respond(w http.ResponseWriter, status int, v interface{}) { +func (a *API) Respond(w http.ResponseWriter, r *http.Request, status int, v interface{}) { if status == http.StatusNoContent { w.WriteHeader(status) return @@ -179,7 +180,7 @@ func (a *API) Respond(w http.ResponseWriter, status int, v interface{}) { b, err = json.Marshal(v) } if err != nil { - a.Err(w, err) + a.Err(w, r, err) return } @@ -194,17 +195,17 @@ func (a *API) Respond(w http.ResponseWriter, status int, v interface{}) { } // Err is used for writing an error to the response. -func (a *API) Err(w http.ResponseWriter, err error) { +func (a *API) Err(w http.ResponseWriter, r *http.Request, err error) { if err == nil { return } a.logErr("api error encountered", zap.Error(err)) - v, status, err := a.errFn(err) + v, status, err := a.errFn(r.Context(), err) if err != nil { a.logErr("failed to write err to response writer", zap.Error(err)) - a.Respond(w, http.StatusInternalServerError, ErrBody{ + a.Respond(w, r, http.StatusInternalServerError, ErrBody{ Code: "internal error", Msg: "an unexpected error occured", }) @@ -215,7 +216,7 @@ func (a *API) Err(w http.ResponseWriter, err error) { w.Header().Set(PlatformErrorCodeHeader, eb.Code) } - a.Respond(w, status, v) + a.Respond(w, r, status, v) } func (a *API) logErr(msg string, fields ...zap.Field) { diff --git a/kit/transport/http/api_test.go b/kit/transport/http/api_test.go index 9e6fd7aca2..6f844d4308 100644 --- a/kit/transport/http/api_test.go +++ b/kit/transport/http/api_test.go @@ -142,7 +142,7 @@ func Test_API(t *testing.T) { responder := kithttp.NewAPI() svr := func(w http.ResponseWriter, r *http.Request) { - responder.Respond(w, statusCode, map[string]string{ + responder.Respond(w, r, statusCode, map[string]string{ "foo": "bar", }) } @@ -208,7 +208,7 @@ func Test_API(t *testing.T) { responder := kithttp.NewAPI() svr := func(w http.ResponseWriter, r *http.Request) { - responder.Err(w, tt.expectedErr) + responder.Err(w, r, tt.expectedErr) } testttp. diff --git a/kit/transport/http/error_handler.go b/kit/transport/http/error_handler.go index 61dca7d206..5f3b6920db 100644 --- a/kit/transport/http/error_handler.go +++ b/kit/transport/http/error_handler.go @@ -23,7 +23,7 @@ func (h ErrorHandler) HandleHTTPError(ctx context.Context, err error, w http.Res code := influxdb.ErrorCode(err) w.Header().Set(PlatformErrorCodeHeader, code) w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(ErrorCodeToStatusCode(code)) + w.WriteHeader(ErrorCodeToStatusCode(ctx, code)) var e struct { Code string `json:"code"` Message string `json:"message"` @@ -51,12 +51,21 @@ func StatusCodeToErrorCode(statusCode int) string { // ErrorCodeToStatusCode maps an influxdb error code string to a // http status code integer. -func ErrorCodeToStatusCode(code string) int { +func ErrorCodeToStatusCode(ctx context.Context, code string) int { + // If the client disconnects early or times out then return a different + // error than the passed in error code. Client timeouts return a 408 + // while disconnections return a non-standard Nginx HTTP 499 code. + if err := ctx.Err(); err == context.DeadlineExceeded { + return http.StatusRequestTimeout + } else if err == context.Canceled { + return 499 // https://httpstatuses.com/499 + } + + // Otherwise map internal error codes to HTTP status codes. statusCode, ok := influxDBErrorToStatusCode[code] if ok { return statusCode } - return http.StatusInternalServerError } diff --git a/pkger/http_server.go b/pkger/http_server.go index 785ac86513..8f52fa0f90 100644 --- a/pkger/http_server.go +++ b/pkger/http_server.go @@ -79,7 +79,7 @@ func (s *HTTPServer) listStacks(w http.ResponseWriter, r *http.Request) { rawOrgID := q.Get("orgID") orgID, err := influxdb.IDFromString(rawOrgID) if err != nil { - s.api.Err(w, &influxdb.Error{ + s.api.Err(w, r, &influxdb.Error{ Code: influxdb.EInvalid, Msg: fmt.Sprintf("organization id[%q] is invalid", rawOrgID), Err: err, @@ -88,7 +88,7 @@ func (s *HTTPServer) listStacks(w http.ResponseWriter, r *http.Request) { } if err := r.ParseForm(); err != nil { - s.api.Err(w, &influxdb.Error{ + s.api.Err(w, r, &influxdb.Error{ Code: influxdb.EInvalid, Msg: "failed to parse form from encoded url", Err: err, @@ -103,7 +103,7 @@ func (s *HTTPServer) listStacks(w http.ResponseWriter, r *http.Request) { for _, idRaw := range r.Form["stackID"] { id, err := influxdb.IDFromString(idRaw) if err != nil { - s.api.Err(w, &influxdb.Error{ + s.api.Err(w, r, &influxdb.Error{ Code: influxdb.EInvalid, Msg: fmt.Sprintf("stack ID[%q] provided is invalid", idRaw), Err: err, @@ -115,14 +115,14 @@ func (s *HTTPServer) listStacks(w http.ResponseWriter, r *http.Request) { stacks, err := s.svc.ListStacks(r.Context(), *orgID, filter) if err != nil { - s.api.Err(w, err) + s.api.Err(w, r, err) return } if stacks == nil { stacks = []Stack{} } - s.api.Respond(w, http.StatusOK, RespListStacks{ + s.api.Respond(w, r, http.StatusOK, RespListStacks{ Stacks: stacks, }) } @@ -174,14 +174,14 @@ type RespCreateStack struct { func (s *HTTPServer) createStack(w http.ResponseWriter, r *http.Request) { var reqBody ReqCreateStack if err := s.api.DecodeJSON(r.Body, &reqBody); err != nil { - s.api.Err(w, err) + s.api.Err(w, r, err) return } defer r.Body.Close() auth, err := pctx.GetAuthorizer(r.Context()) if err != nil { - s.api.Err(w, err) + s.api.Err(w, r, err) return } @@ -192,11 +192,11 @@ func (s *HTTPServer) createStack(w http.ResponseWriter, r *http.Request) { URLs: reqBody.URLs, }) if err != nil { - s.api.Err(w, err) + s.api.Err(w, r, err) return } - s.api.Respond(w, http.StatusCreated, RespCreateStack{ + s.api.Respond(w, r, http.StatusCreated, RespCreateStack{ ID: stack.ID.String(), OrgID: stack.OrgID.String(), Name: stack.Name, @@ -209,13 +209,13 @@ func (s *HTTPServer) createStack(w http.ResponseWriter, r *http.Request) { func (s *HTTPServer) deleteStack(w http.ResponseWriter, r *http.Request) { orgID, err := getRequiredOrgIDFromQuery(r.URL.Query()) if err != nil { - s.api.Err(w, err) + s.api.Err(w, r, err) return } stackID, err := influxdb.IDFromString(chi.URLParam(r, "stack_id")) if err != nil { - s.api.Err(w, &influxdb.Error{ + s.api.Err(w, r, &influxdb.Error{ Code: influxdb.EInvalid, Msg: "the stack id provided in the path was invalid", Err: err, @@ -225,7 +225,7 @@ func (s *HTTPServer) deleteStack(w http.ResponseWriter, r *http.Request) { auth, err := pctx.GetAuthorizer(r.Context()) if err != nil { - s.api.Err(w, err) + s.api.Err(w, r, err) return } userID := auth.GetUserID() @@ -236,11 +236,11 @@ func (s *HTTPServer) deleteStack(w http.ResponseWriter, r *http.Request) { StackID: *stackID, }) if err != nil { - s.api.Err(w, err) + s.api.Err(w, r, err) return } - s.api.Respond(w, http.StatusNoContent, nil) + s.api.Respond(w, r, http.StatusNoContent, nil) } func getRequiredOrgIDFromQuery(q url.Values) (influxdb.ID, error) { @@ -306,7 +306,7 @@ func (s *HTTPServer) createPkg(w http.ResponseWriter, r *http.Request) { var reqBody ReqCreatePkg if err := s.api.DecodeJSON(r.Body, &reqBody); err != nil { - s.api.Err(w, err) + s.api.Err(w, r, err) return } defer r.Body.Close() @@ -328,7 +328,7 @@ func (s *HTTPServer) createPkg(w http.ResponseWriter, r *http.Request) { newPkg, err := s.svc.CreatePkg(r.Context(), opts...) if err != nil { - s.api.Err(w, err) + s.api.Err(w, r, err) return } @@ -347,7 +347,7 @@ func (s *HTTPServer) createPkg(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") } - s.encResp(w, enc, http.StatusOK, resp) + s.encResp(w, r, enc, http.StatusOK, resp) } // PkgRemote provides a package via a remote (i.e. a gist). If content type is not @@ -432,13 +432,13 @@ func (s *HTTPServer) applyPkg(w http.ResponseWriter, r *http.Request) { var reqBody ReqApplyPkg encoding, err := decodeWithEncoding(r, &reqBody) if err != nil { - s.api.Err(w, newDecodeErr(encoding.String(), err)) + s.api.Err(w, r, newDecodeErr(encoding.String(), err)) return } orgID, err := influxdb.IDFromString(reqBody.OrgID) if err != nil { - s.api.Err(w, &influxdb.Error{ + s.api.Err(w, r, &influxdb.Error{ Code: influxdb.EInvalid, Msg: fmt.Sprintf("invalid organization ID provided: %q", reqBody.OrgID), }) @@ -448,7 +448,7 @@ func (s *HTTPServer) applyPkg(w http.ResponseWriter, r *http.Request) { var stackID influxdb.ID if reqBody.StackID != nil { if err := stackID.DecodeFromString(*reqBody.StackID); err != nil { - s.api.Err(w, &influxdb.Error{ + s.api.Err(w, r, &influxdb.Error{ Code: influxdb.EInvalid, Msg: fmt.Sprintf("invalid stack ID provided: %q", *reqBody.StackID), }) @@ -458,14 +458,14 @@ func (s *HTTPServer) applyPkg(w http.ResponseWriter, r *http.Request) { auth, err := pctx.GetAuthorizer(r.Context()) if err != nil { - s.api.Err(w, err) + s.api.Err(w, r, err) return } userID := auth.GetUserID() parsedPkg, err := reqBody.Pkgs(encoding) if err != nil { - s.api.Err(w, &influxdb.Error{ + s.api.Err(w, r, &influxdb.Error{ Code: influxdb.EUnprocessableEntity, Err: err, }) @@ -480,7 +480,7 @@ func (s *HTTPServer) applyPkg(w http.ResponseWriter, r *http.Request) { if reqBody.DryRun { sum, diff, err := s.svc.DryRun(r.Context(), *orgID, userID, parsedPkg, applyOpts...) if IsParseErr(err) { - s.api.Respond(w, http.StatusUnprocessableEntity, RespApplyPkg{ + s.api.Respond(w, r, http.StatusUnprocessableEntity, RespApplyPkg{ Diff: diff, Summary: sum, Errors: convertParseErr(err), @@ -488,11 +488,11 @@ func (s *HTTPServer) applyPkg(w http.ResponseWriter, r *http.Request) { return } if err != nil { - s.api.Err(w, err) + s.api.Err(w, r, err) return } - s.api.Respond(w, http.StatusOK, RespApplyPkg{ + s.api.Respond(w, r, http.StatusOK, RespApplyPkg{ Diff: diff, Summary: sum, }) @@ -503,11 +503,11 @@ func (s *HTTPServer) applyPkg(w http.ResponseWriter, r *http.Request) { sum, diff, err := s.svc.Apply(r.Context(), *orgID, userID, parsedPkg, applyOpts...) if err != nil && !IsParseErr(err) { - s.api.Err(w, err) + s.api.Err(w, r, err) return } - s.api.Respond(w, http.StatusCreated, RespApplyPkg{ + s.api.Respond(w, r, http.StatusCreated, RespApplyPkg{ Diff: diff, Summary: sum, Errors: convertParseErr(err), @@ -551,10 +551,10 @@ func newJSONEnc(w io.Writer) encoder { return enc } -func (s *HTTPServer) encResp(w http.ResponseWriter, enc encoder, code int, res interface{}) { +func (s *HTTPServer) encResp(w http.ResponseWriter, r *http.Request, enc encoder, code int, res interface{}) { w.WriteHeader(code) if err := enc.Encode(res); err != nil { - s.api.Err(w, &influxdb.Error{ + s.api.Err(w, r, &influxdb.Error{ Msg: fmt.Sprintf("unable to marshal; Err: %v", err), Code: influxdb.EInternal, Err: err, diff --git a/session/http_server.go b/session/http_server.go index 387c5f4e59..4aec33106f 100644 --- a/session/http_server.go +++ b/session/http_server.go @@ -81,7 +81,7 @@ func (h *SessionHandler) handleSignin(w http.ResponseWriter, r *http.Request) { req, decErr := decodeSigninRequest(ctx, r) if decErr != nil { - h.api.Err(w, ErrUnauthorized) + h.api.Err(w, r, ErrUnauthorized) return } @@ -89,18 +89,18 @@ func (h *SessionHandler) handleSignin(w http.ResponseWriter, r *http.Request) { Name: &req.Username, }) if err != nil { - h.api.Err(w, ErrUnauthorized) + h.api.Err(w, r, ErrUnauthorized) return } if err := h.passSvc.ComparePassword(ctx, u.ID, req.Password); err != nil { - h.api.Err(w, ErrUnauthorized) + h.api.Err(w, r, ErrUnauthorized) return } s, e := h.sessionSvc.CreateSession(ctx, req.Username) if e != nil { - h.api.Err(w, ErrUnauthorized) + h.api.Err(w, r, ErrUnauthorized) return } @@ -134,12 +134,12 @@ func (h *SessionHandler) handleSignout(w http.ResponseWriter, r *http.Request) { req, err := decodeSignoutRequest(ctx, r) if err != nil { - h.api.Err(w, ErrUnauthorized) + h.api.Err(w, r, ErrUnauthorized) return } if err := h.sessionSvc.ExpireSession(ctx, req.Key); err != nil { - h.api.Err(w, ErrUnauthorized) + h.api.Err(w, r, ErrUnauthorized) return } diff --git a/tenant/http_handler_urm.go b/tenant/http_handler_urm.go index 700fafc3f1..15c0cb8b3f 100644 --- a/tenant/http_handler_urm.go +++ b/tenant/http_handler_urm.go @@ -48,7 +48,7 @@ func (h *urmHandler) getURMsByType(w http.ResponseWriter, r *http.Request) { ctx := r.Context() req, err := h.decodeGetRequest(ctx, r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -59,7 +59,7 @@ func (h *urmHandler) getURMsByType(w http.ResponseWriter, r *http.Request) { } mappings, _, err := h.svc.FindUserResourceMappings(ctx, filter) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -70,7 +70,7 @@ func (h *urmHandler) getURMsByType(w http.ResponseWriter, r *http.Request) { } user, err := h.userSvc.FindUserByID(ctx, m.UserID) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -78,7 +78,7 @@ func (h *urmHandler) getURMsByType(w http.ResponseWriter, r *http.Request) { } h.log.Debug("Members/owners retrieved", zap.String("users", fmt.Sprint(users))) - h.api.Respond(w, http.StatusOK, newResourceUsersResponse(filter, users)) + h.api.Respond(w, r, http.StatusOK, newResourceUsersResponse(filter, users)) } @@ -112,13 +112,13 @@ func (h *urmHandler) postURMByType(w http.ResponseWriter, r *http.Request) { ctx := r.Context() req, err := h.decodePostRequest(ctx, r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } user, err := h.userSvc.FindUserByID(ctx, req.UserID) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -129,12 +129,12 @@ func (h *urmHandler) postURMByType(w http.ResponseWriter, r *http.Request) { UserType: userType, } if err := h.svc.CreateUserResourceMapping(ctx, mapping); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Member/owner created", zap.String("mapping", fmt.Sprint(mapping))) - h.api.Respond(w, http.StatusCreated, newResourceUserResponse(user, userType)) + h.api.Respond(w, r, http.StatusCreated, newResourceUserResponse(user, userType)) } type postRequest struct { @@ -178,12 +178,12 @@ func (h *urmHandler) deleteURM(w http.ResponseWriter, r *http.Request) { ctx := r.Context() req, err := h.decodeDeleteRequest(ctx, r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } if err := h.svc.DeleteUserResourceMapping(ctx, req.resourceID, req.userID); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Member deleted", zap.String("resourceID", req.resourceID.String()), zap.String("memberID", req.userID.String())) diff --git a/tenant/http_server.go b/tenant/http_server.go index b7db548c06..7a11b18244 100644 --- a/tenant/http_server.go +++ b/tenant/http_server.go @@ -20,7 +20,7 @@ func ValidResource(api *kit.API, lookupOrgByResourceID func(context.Context, inf statusW := kit.NewStatusResponseWriter(w) id, err := influxdb.IDFromString(chi.URLParam(r, "id")) if err != nil { - api.Err(w, ErrCorruptID(err)) + api.Err(w, r, ErrCorruptID(err)) return } @@ -28,7 +28,7 @@ func ValidResource(api *kit.API, lookupOrgByResourceID func(context.Context, inf orgID, err := lookupOrgByResourceID(ctx, *id) if err != nil { - api.Err(w, err) + api.Err(w, r, err) return } diff --git a/tenant/http_server_bucket.go b/tenant/http_server_bucket.go index 5fbc356d98..ce75123eac 100644 --- a/tenant/http_server_bucket.go +++ b/tenant/http_server_bucket.go @@ -250,28 +250,28 @@ func newBucketsResponse(ctx context.Context, opts influxdb.FindOptions, f influx func (h *BucketHandler) handlePostBucket(w http.ResponseWriter, r *http.Request) { var b postBucketRequest if err := h.api.DecodeJSON(r.Body, &b); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } if err := b.OK(); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } bucket := b.toInfluxDB() if err := validBucketName(bucket); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } if err := h.bucketSvc.CreateBucket(r.Context(), bucket); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Bucket created", zap.String("bucket", fmt.Sprint(bucket))) - h.api.Respond(w, http.StatusCreated, NewBucketResponse(bucket)) + h.api.Respond(w, r, http.StatusCreated, NewBucketResponse(bucket)) } type postBucketRequest struct { @@ -334,55 +334,55 @@ func (h *BucketHandler) handleGetBucket(w http.ResponseWriter, r *http.Request) id, err := influxdb.IDFromString(chi.URLParam(r, "id")) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } b, err := h.bucketSvc.FindBucketByID(ctx, *id) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Bucket retrieved", zap.String("bucket", fmt.Sprint(b))) - h.api.Respond(w, http.StatusOK, NewBucketResponse(b)) + h.api.Respond(w, r, http.StatusOK, NewBucketResponse(b)) } // handleDeleteBucket is the HTTP handler for the DELETE /api/v2/buckets/:id route. func (h *BucketHandler) handleDeleteBucket(w http.ResponseWriter, r *http.Request) { id, err := influxdb.IDFromString(chi.URLParam(r, "id")) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } if err := h.bucketSvc.DeleteBucket(r.Context(), *id); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Bucket deleted", zap.String("bucketID", id.String())) - h.api.Respond(w, http.StatusNoContent, nil) + h.api.Respond(w, r, http.StatusNoContent, nil) } // handleGetBuckets is the HTTP handler for the GET /api/v2/buckets route. func (h *BucketHandler) handleGetBuckets(w http.ResponseWriter, r *http.Request) { bucketsRequest, err := decodeGetBucketsRequest(r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } bs, _, err := h.bucketSvc.FindBuckets(r.Context(), bucketsRequest.filter, bucketsRequest.opts) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Buckets retrieved", zap.String("buckets", fmt.Sprint(bs))) - h.api.Respond(w, http.StatusOK, newBucketsResponse(r.Context(), bucketsRequest.opts, bucketsRequest.filter, bs)) + h.api.Respond(w, r, http.StatusOK, newBucketsResponse(r.Context(), bucketsRequest.opts, bucketsRequest.filter, bs)) } type getBucketsRequest struct { @@ -432,38 +432,38 @@ func decodeGetBucketsRequest(r *http.Request) (*getBucketsRequest, error) { func (h *BucketHandler) handlePatchBucket(w http.ResponseWriter, r *http.Request) { id, err := influxdb.IDFromString(chi.URLParam(r, "id")) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } var reqBody bucketUpdate if err := h.api.DecodeJSON(r.Body, &reqBody); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } if reqBody.Name != nil { b, err := h.bucketSvc.FindBucketByID(r.Context(), *id) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } b.Name = *reqBody.Name if err := validBucketName(b); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } } b, err := h.bucketSvc.UpdateBucket(r.Context(), *id, *reqBody.toInfluxDB()) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Bucket updated", zap.String("bucket", fmt.Sprint(b))) - h.api.Respond(w, http.StatusOK, NewBucketResponse(b)) + h.api.Respond(w, r, http.StatusOK, NewBucketResponse(b)) } func (h *BucketHandler) lookupOrgByBucketID(ctx context.Context, id influxdb.ID) (influxdb.ID, error) { diff --git a/tenant/http_server_onboarding.go b/tenant/http_server_onboarding.go index c7c67e3188..029ce9432d 100644 --- a/tenant/http_server_onboarding.go +++ b/tenant/http_server_onboarding.go @@ -65,12 +65,12 @@ func (h *OnboardHandler) handleIsOnboarding(w http.ResponseWriter, r *http.Reque ctx := r.Context() result, err := h.onboardingSvc.IsOnboarding(ctx) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Onboarding eligibility check finished", zap.String("result", fmt.Sprint(result))) - h.api.Respond(w, http.StatusOK, isOnboardingResponse{result}) + h.api.Respond(w, r, http.StatusOK, isOnboardingResponse{result}) } // handleInitialOnboardRequest is the HTTP handler for the GET /api/v2/setup route. @@ -78,17 +78,17 @@ func (h *OnboardHandler) handleInitialOnboardRequest(w http.ResponseWriter, r *h ctx := r.Context() req, err := decodeOnboardRequest(ctx, r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } results, err := h.onboardingSvc.OnboardInitialUser(ctx, req) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Onboarding setup completed", zap.String("results", fmt.Sprint(results))) - h.api.Respond(w, http.StatusCreated, NewOnboardingResponse(results)) + h.api.Respond(w, r, http.StatusCreated, NewOnboardingResponse(results)) } // isOnboarding is the HTTP handler for the POST /api/v2/setup route. @@ -96,17 +96,17 @@ func (h *OnboardHandler) handleOnboardRequest(w http.ResponseWriter, r *http.Req ctx := r.Context() req, err := decodeOnboardRequest(ctx, r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } results, err := h.onboardingSvc.OnboardUser(ctx, req) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Onboarding setup completed", zap.String("results", fmt.Sprint(results))) - h.api.Respond(w, http.StatusCreated, NewOnboardingResponse(results)) + h.api.Respond(w, r, http.StatusCreated, NewOnboardingResponse(results)) } type onboardingResponse struct { diff --git a/tenant/http_server_org.go b/tenant/http_server_org.go index 83394ca074..16b8fa5785 100644 --- a/tenant/http_server_org.go +++ b/tenant/http_server_org.go @@ -95,36 +95,36 @@ func newOrgsResponse(orgs []*influxdb.Organization) *orgsResponse { func (h *OrgHandler) handlePostOrg(w http.ResponseWriter, r *http.Request) { var org influxdb.Organization if err := h.api.DecodeJSON(r.Body, &org); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } if err := h.orgSvc.CreateOrganization(r.Context(), &org); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Org created", zap.String("org", fmt.Sprint(org))) - h.api.Respond(w, http.StatusCreated, newOrgResponse(org)) + h.api.Respond(w, r, http.StatusCreated, newOrgResponse(org)) } // handleGetOrg is the HTTP handler for the GET /api/v2/orgs/:id route. func (h *OrgHandler) handleGetOrg(w http.ResponseWriter, r *http.Request) { id, err := influxdb.IDFromString(chi.URLParam(r, "id")) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } org, err := h.orgSvc.FindOrganizationByID(r.Context(), *id) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Org retrieved", zap.String("org", fmt.Sprint(org))) - h.api.Respond(w, http.StatusOK, newOrgResponse(*org)) + h.api.Respond(w, r, http.StatusOK, newOrgResponse(*org)) } // handleGetOrgs is the HTTP handler for the GET /api/v2/orgs route. @@ -151,54 +151,54 @@ func (h *OrgHandler) handleGetOrgs(w http.ResponseWriter, r *http.Request) { orgs, _, err := h.orgSvc.FindOrganizations(r.Context(), filter) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Orgs retrieved", zap.String("org", fmt.Sprint(orgs))) - h.api.Respond(w, http.StatusOK, newOrgsResponse(orgs)) + h.api.Respond(w, r, http.StatusOK, newOrgsResponse(orgs)) } // handlePatchOrg is the HTTP handler for the PATH /api/v2/orgs route. func (h *OrgHandler) handlePatchOrg(w http.ResponseWriter, r *http.Request) { id, err := influxdb.IDFromString(chi.URLParam(r, "id")) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } var upd influxdb.OrganizationUpdate if err := h.api.DecodeJSON(r.Body, &upd); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } org, err := h.orgSvc.UpdateOrganization(r.Context(), *id, upd) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Org updated", zap.String("org", fmt.Sprint(org))) - h.api.Respond(w, http.StatusOK, newOrgResponse(*org)) + h.api.Respond(w, r, http.StatusOK, newOrgResponse(*org)) } // handleDeleteOrganization is the HTTP handler for the DELETE /api/v2/orgs/:id route. func (h *OrgHandler) handleDeleteOrg(w http.ResponseWriter, r *http.Request) { id, err := influxdb.IDFromString(chi.URLParam(r, "id")) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } ctx := r.Context() if err := h.orgSvc.DeleteOrganization(ctx, *id); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Org deleted", zap.String("orgID", fmt.Sprint(id))) - h.api.Respond(w, http.StatusNoContent, nil) + h.api.Respond(w, r, http.StatusNoContent, nil) } func (h *OrgHandler) lookupOrgByID(ctx context.Context, id influxdb.ID) (influxdb.ID, error) { diff --git a/tenant/http_server_user.go b/tenant/http_server_user.go index 7b209226cd..9d7c9f82d5 100644 --- a/tenant/http_server_user.go +++ b/tenant/http_server_user.go @@ -86,7 +86,7 @@ func (h *UserHandler) handlePostUserPassword(w http.ResponseWriter, r *http.Requ var body passwordSetRequest err := json.NewDecoder(r.Body).Decode(&body) if err != nil { - h.api.Err(w, &influxdb.Error{ + h.api.Err(w, r, &influxdb.Error{ Code: influxdb.EInvalid, Err: err, }) @@ -96,7 +96,7 @@ func (h *UserHandler) handlePostUserPassword(w http.ResponseWriter, r *http.Requ param := chi.URLParam(r, "id") userID, err := influxdb.IDFromString(param) if err != nil { - h.api.Err(w, &influxdb.Error{ + h.api.Err(w, r, &influxdb.Error{ Msg: "invalid user ID provided in route", }) return @@ -104,7 +104,7 @@ func (h *UserHandler) handlePostUserPassword(w http.ResponseWriter, r *http.Requ err = h.passwordSvc.SetPassword(r.Context(), *userID, body.Password) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -120,7 +120,7 @@ func (h *UserHandler) putPassword(ctx context.Context, w http.ResponseWriter, r param := chi.URLParam(r, "id") userID, err := influxdb.IDFromString(param) if err != nil { - h.api.Err(w, &influxdb.Error{ + h.api.Err(w, r, &influxdb.Error{ Msg: "invalid user ID provided in route", }) return @@ -138,7 +138,7 @@ func (h *UserHandler) handlePutUserPassword(w http.ResponseWriter, r *http.Reque ctx := r.Context() _, err := h.putPassword(ctx, w, r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("User password updated") @@ -182,7 +182,7 @@ func (h *UserHandler) handlePostUser(w http.ResponseWriter, r *http.Request) { ctx := r.Context() req, err := decodePostUserRequest(ctx, r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -191,12 +191,12 @@ func (h *UserHandler) handlePostUser(w http.ResponseWriter, r *http.Request) { } if err := h.userSvc.CreateUser(ctx, req.User); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("User created", zap.String("user", fmt.Sprint(req.User))) - h.api.Respond(w, http.StatusCreated, newUserResponse(req.User)) + h.api.Respond(w, r, http.StatusCreated, newUserResponse(req.User)) } type postUserRequest struct { @@ -220,7 +220,7 @@ func (h *UserHandler) handleGetMe(w http.ResponseWriter, r *http.Request) { a, err := icontext.GetAuthorizer(ctx) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } @@ -228,11 +228,11 @@ func (h *UserHandler) handleGetMe(w http.ResponseWriter, r *http.Request) { user, err := h.userSvc.FindUserByID(ctx, id) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } - h.api.Respond(w, http.StatusOK, newUserResponse(user)) + h.api.Respond(w, r, http.StatusOK, newUserResponse(user)) } // handleGetUser is the HTTP handler for the GET /api/v2/users/:id route. @@ -240,18 +240,18 @@ func (h *UserHandler) handleGetUser(w http.ResponseWriter, r *http.Request) { ctx := r.Context() req, err := decodeGetUserRequest(ctx, r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } b, err := h.userSvc.FindUserByID(ctx, req.UserID) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("User retrieved", zap.String("user", fmt.Sprint(b))) - h.api.Respond(w, http.StatusOK, newUserResponse(b)) + h.api.Respond(w, r, http.StatusOK, newUserResponse(b)) } type getUserRequest struct { @@ -284,12 +284,12 @@ func (h *UserHandler) handleDeleteUser(w http.ResponseWriter, r *http.Request) { ctx := r.Context() req, err := decodeDeleteUserRequest(ctx, r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } if err := h.userSvc.DeleteUser(ctx, req.UserID); err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("User deleted", zap.String("userID", fmt.Sprint(req.UserID))) @@ -373,18 +373,18 @@ func (h *UserHandler) handleGetUsers(w http.ResponseWriter, r *http.Request) { ctx := r.Context() req, err := decodeGetUsersRequest(ctx, r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } users, _, err := h.userSvc.FindUsers(ctx, req.filter) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Users retrieved", zap.String("users", fmt.Sprint(users))) - h.api.Respond(w, http.StatusOK, newUsersResponse(users)) + h.api.Respond(w, r, http.StatusOK, newUsersResponse(users)) } type getUsersRequest struct { @@ -415,18 +415,18 @@ func (h *UserHandler) handlePatchUser(w http.ResponseWriter, r *http.Request) { ctx := r.Context() req, err := decodePatchUserRequest(ctx, r) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } b, err := h.userSvc.UpdateUser(ctx, req.UserID, req.Update) if err != nil { - h.api.Err(w, err) + h.api.Err(w, r, err) return } h.log.Debug("Users updated", zap.String("user", fmt.Sprint(b))) - h.api.Respond(w, http.StatusOK, newUserResponse(b)) + h.api.Respond(w, r, http.StatusOK, newUserResponse(b)) } type patchUserRequest struct {