diff --git a/openapi-internal.yaml b/openapi-internal.yaml index 155efe4..1fb1fd7 100644 --- a/openapi-internal.yaml +++ b/openapi-internal.yaml @@ -65,6 +65,23 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /mapfixes/{MapfixID}/status/validator-submitted: + post: + summary: (Internal endpoint) Role Validator changes status from Submitting -> Submitted + operationId: actionMapfixSubmitted + tags: + - Mapfixes + parameters: + - $ref: '#/components/parameters/MapfixID' + responses: + "204": + description: Successful response + default: + description: General Error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" /mapfixes/{MapfixID}/status/validator-validated: post: summary: (Internal endpoint) Role Validator changes status from Validating -> Validated @@ -203,6 +220,23 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /submissions/{SubmissionID}/status/validator-submitted: + post: + summary: (Internal endpoint) Role Validator changes status from Submitting -> Submitted + operationId: actionSubmissionSubmitted + tags: + - Submissions + parameters: + - $ref: '#/components/parameters/SubmissionID' + responses: + "204": + description: Successful response + default: + description: General Error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" /submissions/{SubmissionID}/status/validator-validated: post: summary: (Internal endpoint) Role Validator changes status from Validating -> Validated diff --git a/openapi.yaml b/openapi.yaml index 00b1140..16869ba 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -367,10 +367,27 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" - /mapfixes/{MapfixID}/status/submit: + /mapfixes/{MapfixID}/status/trigger-submit: post: - summary: Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted - operationId: actionMapfixSubmit + summary: Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting + operationId: actionMapfixTriggerSubmit + tags: + - Mapfixes + parameters: + - $ref: '#/components/parameters/MapfixID' + responses: + "204": + description: Successful response + default: + description: General Error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /mapfixes/{MapfixID}/status/reset-submitting: + post: + summary: Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction + operationId: actionMapfixResetSubmitting tags: - Mapfixes parameters: @@ -757,10 +774,27 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" - /submissions/{SubmissionID}/status/submit: + /submissions/{SubmissionID}/status/trigger-submit: post: - summary: Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted - operationId: actionSubmissionSubmit + summary: Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting + operationId: actionSubmissionTriggerSubmit + tags: + - Submissions + parameters: + - $ref: '#/components/parameters/SubmissionID' + responses: + "204": + description: Successful response + default: + description: General Error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /submissions/{SubmissionID}/status/reset-submitting: + post: + summary: Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction + operationId: actionSubmissionResetSubmitting tags: - Submissions parameters: diff --git a/pkg/api/oas_client_gen.go b/pkg/api/oas_client_gen.go index f865e3a..89085f9 100644 --- a/pkg/api/oas_client_gen.go +++ b/pkg/api/oas_client_gen.go @@ -47,6 +47,13 @@ type Invoker interface { // // POST /mapfixes/{MapfixID}/status/request-changes ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error + // ActionMapfixResetSubmitting invokes actionMapfixResetSubmitting operation. + // + // Role Submitter manually resets submitting softlock and changes status from Submitting -> + // UnderConstruction. + // + // POST /mapfixes/{MapfixID}/status/reset-submitting + ActionMapfixResetSubmitting(ctx context.Context, params ActionMapfixResetSubmittingParams) error // ActionMapfixRetryValidate invokes actionMapfixRetryValidate operation. // // Role Reviewer re-runs validation and changes status from Accepted -> Validating. @@ -59,12 +66,12 @@ type Invoker interface { // // POST /mapfixes/{MapfixID}/status/revoke ActionMapfixRevoke(ctx context.Context, params ActionMapfixRevokeParams) error - // ActionMapfixSubmit invokes actionMapfixSubmit operation. + // ActionMapfixTriggerSubmit invokes actionMapfixTriggerSubmit operation. // - // Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. + // Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting. // - // POST /mapfixes/{MapfixID}/status/submit - ActionMapfixSubmit(ctx context.Context, params ActionMapfixSubmitParams) error + // POST /mapfixes/{MapfixID}/status/trigger-submit + ActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) error // ActionMapfixTriggerUpload invokes actionMapfixTriggerUpload operation. // // Role Admin changes status from Validated -> Uploading. @@ -101,6 +108,13 @@ type Invoker interface { // // POST /submissions/{SubmissionID}/status/request-changes ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error + // ActionSubmissionResetSubmitting invokes actionSubmissionResetSubmitting operation. + // + // Role Submitter manually resets submitting softlock and changes status from Submitting -> + // UnderConstruction. + // + // POST /submissions/{SubmissionID}/status/reset-submitting + ActionSubmissionResetSubmitting(ctx context.Context, params ActionSubmissionResetSubmittingParams) error // ActionSubmissionRetryValidate invokes actionSubmissionRetryValidate operation. // // Role Reviewer re-runs validation and changes status from Accepted -> Validating. @@ -113,12 +127,12 @@ type Invoker interface { // // POST /submissions/{SubmissionID}/status/revoke ActionSubmissionRevoke(ctx context.Context, params ActionSubmissionRevokeParams) error - // ActionSubmissionSubmit invokes actionSubmissionSubmit operation. + // ActionSubmissionTriggerSubmit invokes actionSubmissionTriggerSubmit operation. // - // Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. + // Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting. // - // POST /submissions/{SubmissionID}/status/submit - ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error + // POST /submissions/{SubmissionID}/status/trigger-submit + ActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) error // ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation. // // Role Admin changes status from Validated -> Uploading. @@ -746,6 +760,131 @@ func (c *Client) sendActionMapfixRequestChanges(ctx context.Context, params Acti return result, nil } +// ActionMapfixResetSubmitting invokes actionMapfixResetSubmitting operation. +// +// Role Submitter manually resets submitting softlock and changes status from Submitting -> +// UnderConstruction. +// +// POST /mapfixes/{MapfixID}/status/reset-submitting +func (c *Client) ActionMapfixResetSubmitting(ctx context.Context, params ActionMapfixResetSubmittingParams) error { + _, err := c.sendActionMapfixResetSubmitting(ctx, params) + return err +} + +func (c *Client) sendActionMapfixResetSubmitting(ctx context.Context, params ActionMapfixResetSubmittingParams) (res *ActionMapfixResetSubmittingNoContent, err error) { + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("actionMapfixResetSubmitting"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/reset-submitting"), + } + + // Run stopwatch. + startTime := time.Now() + defer func() { + // Use floating point division here for higher precision (instead of Millisecond method). + elapsedDuration := time.Since(startTime) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) + }() + + // Increment request counter. + c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + + // Start a span for this request. + ctx, span := c.cfg.Tracer.Start(ctx, ActionMapfixResetSubmittingOperation, + trace.WithAttributes(otelAttrs...), + clientSpanKind, + ) + // Track stage for error reporting. + var stage string + defer func() { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, stage) + c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + } + span.End() + }() + + stage = "BuildURL" + u := uri.Clone(c.requestURL(ctx)) + var pathParts [3]string + pathParts[0] = "/mapfixes/" + { + // Encode "MapfixID" parameter. + e := uri.NewPathEncoder(uri.PathEncoderConfig{ + Param: "MapfixID", + Style: uri.PathStyleSimple, + Explode: false, + }) + if err := func() error { + return e.EncodeValue(conv.Int64ToString(params.MapfixID)) + }(); err != nil { + return res, errors.Wrap(err, "encode path") + } + encoded, err := e.Result() + if err != nil { + return res, errors.Wrap(err, "encode path") + } + pathParts[1] = encoded + } + pathParts[2] = "/status/reset-submitting" + uri.AddPathParts(u, pathParts[:]...) + + stage = "EncodeRequest" + r, err := ht.NewRequest(ctx, "POST", u) + if err != nil { + return res, errors.Wrap(err, "create request") + } + + { + type bitset = [1]uint8 + var satisfied bitset + { + stage = "Security:CookieAuth" + switch err := c.securityCookieAuth(ctx, ActionMapfixResetSubmittingOperation, r); { + case err == nil: // if NO error + satisfied[0] |= 1 << 0 + case errors.Is(err, ogenerrors.ErrSkipClientSecurity): + // Skip this security. + default: + return res, errors.Wrap(err, "security \"CookieAuth\"") + } + } + + if ok := func() bool { + nextRequirement: + for _, requirement := range []bitset{ + {0b00000001}, + } { + for i, mask := range requirement { + if satisfied[i]&mask != mask { + continue nextRequirement + } + } + return true + } + return false + }(); !ok { + return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied + } + } + + stage = "SendRequest" + resp, err := c.cfg.Client.Do(r) + if err != nil { + return res, errors.Wrap(err, "do request") + } + defer resp.Body.Close() + + stage = "DecodeResponse" + result, err := decodeActionMapfixResetSubmittingResponse(resp) + if err != nil { + return res, errors.Wrap(err, "decode response") + } + + return result, nil +} + // ActionMapfixRetryValidate invokes actionMapfixRetryValidate operation. // // Role Reviewer re-runs validation and changes status from Accepted -> Validating. @@ -994,21 +1133,21 @@ func (c *Client) sendActionMapfixRevoke(ctx context.Context, params ActionMapfix return result, nil } -// ActionMapfixSubmit invokes actionMapfixSubmit operation. +// ActionMapfixTriggerSubmit invokes actionMapfixTriggerSubmit operation. // -// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. +// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting. // -// POST /mapfixes/{MapfixID}/status/submit -func (c *Client) ActionMapfixSubmit(ctx context.Context, params ActionMapfixSubmitParams) error { - _, err := c.sendActionMapfixSubmit(ctx, params) +// POST /mapfixes/{MapfixID}/status/trigger-submit +func (c *Client) ActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) error { + _, err := c.sendActionMapfixTriggerSubmit(ctx, params) return err } -func (c *Client) sendActionMapfixSubmit(ctx context.Context, params ActionMapfixSubmitParams) (res *ActionMapfixSubmitNoContent, err error) { +func (c *Client) sendActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) (res *ActionMapfixTriggerSubmitNoContent, err error) { otelAttrs := []attribute.KeyValue{ - otelogen.OperationID("actionMapfixSubmit"), + otelogen.OperationID("actionMapfixTriggerSubmit"), semconv.HTTPRequestMethodKey.String("POST"), - semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/submit"), + semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/trigger-submit"), } // Run stopwatch. @@ -1023,7 +1162,7 @@ func (c *Client) sendActionMapfixSubmit(ctx context.Context, params ActionMapfix c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) // Start a span for this request. - ctx, span := c.cfg.Tracer.Start(ctx, ActionMapfixSubmitOperation, + ctx, span := c.cfg.Tracer.Start(ctx, ActionMapfixTriggerSubmitOperation, trace.WithAttributes(otelAttrs...), clientSpanKind, ) @@ -1060,7 +1199,7 @@ func (c *Client) sendActionMapfixSubmit(ctx context.Context, params ActionMapfix } pathParts[1] = encoded } - pathParts[2] = "/status/submit" + pathParts[2] = "/status/trigger-submit" uri.AddPathParts(u, pathParts[:]...) stage = "EncodeRequest" @@ -1074,7 +1213,7 @@ func (c *Client) sendActionMapfixSubmit(ctx context.Context, params ActionMapfix var satisfied bitset { stage = "Security:CookieAuth" - switch err := c.securityCookieAuth(ctx, ActionMapfixSubmitOperation, r); { + switch err := c.securityCookieAuth(ctx, ActionMapfixTriggerSubmitOperation, r); { case err == nil: // if NO error satisfied[0] |= 1 << 0 case errors.Is(err, ogenerrors.ErrSkipClientSecurity): @@ -1110,7 +1249,7 @@ func (c *Client) sendActionMapfixSubmit(ctx context.Context, params ActionMapfix defer resp.Body.Close() stage = "DecodeResponse" - result, err := decodeActionMapfixSubmitResponse(resp) + result, err := decodeActionMapfixTriggerSubmitResponse(resp) if err != nil { return res, errors.Wrap(err, "decode response") } @@ -1862,6 +2001,131 @@ func (c *Client) sendActionSubmissionRequestChanges(ctx context.Context, params return result, nil } +// ActionSubmissionResetSubmitting invokes actionSubmissionResetSubmitting operation. +// +// Role Submitter manually resets submitting softlock and changes status from Submitting -> +// UnderConstruction. +// +// POST /submissions/{SubmissionID}/status/reset-submitting +func (c *Client) ActionSubmissionResetSubmitting(ctx context.Context, params ActionSubmissionResetSubmittingParams) error { + _, err := c.sendActionSubmissionResetSubmitting(ctx, params) + return err +} + +func (c *Client) sendActionSubmissionResetSubmitting(ctx context.Context, params ActionSubmissionResetSubmittingParams) (res *ActionSubmissionResetSubmittingNoContent, err error) { + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("actionSubmissionResetSubmitting"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/reset-submitting"), + } + + // Run stopwatch. + startTime := time.Now() + defer func() { + // Use floating point division here for higher precision (instead of Millisecond method). + elapsedDuration := time.Since(startTime) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) + }() + + // Increment request counter. + c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + + // Start a span for this request. + ctx, span := c.cfg.Tracer.Start(ctx, ActionSubmissionResetSubmittingOperation, + trace.WithAttributes(otelAttrs...), + clientSpanKind, + ) + // Track stage for error reporting. + var stage string + defer func() { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, stage) + c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + } + span.End() + }() + + stage = "BuildURL" + u := uri.Clone(c.requestURL(ctx)) + var pathParts [3]string + pathParts[0] = "/submissions/" + { + // Encode "SubmissionID" parameter. + e := uri.NewPathEncoder(uri.PathEncoderConfig{ + Param: "SubmissionID", + Style: uri.PathStyleSimple, + Explode: false, + }) + if err := func() error { + return e.EncodeValue(conv.Int64ToString(params.SubmissionID)) + }(); err != nil { + return res, errors.Wrap(err, "encode path") + } + encoded, err := e.Result() + if err != nil { + return res, errors.Wrap(err, "encode path") + } + pathParts[1] = encoded + } + pathParts[2] = "/status/reset-submitting" + uri.AddPathParts(u, pathParts[:]...) + + stage = "EncodeRequest" + r, err := ht.NewRequest(ctx, "POST", u) + if err != nil { + return res, errors.Wrap(err, "create request") + } + + { + type bitset = [1]uint8 + var satisfied bitset + { + stage = "Security:CookieAuth" + switch err := c.securityCookieAuth(ctx, ActionSubmissionResetSubmittingOperation, r); { + case err == nil: // if NO error + satisfied[0] |= 1 << 0 + case errors.Is(err, ogenerrors.ErrSkipClientSecurity): + // Skip this security. + default: + return res, errors.Wrap(err, "security \"CookieAuth\"") + } + } + + if ok := func() bool { + nextRequirement: + for _, requirement := range []bitset{ + {0b00000001}, + } { + for i, mask := range requirement { + if satisfied[i]&mask != mask { + continue nextRequirement + } + } + return true + } + return false + }(); !ok { + return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied + } + } + + stage = "SendRequest" + resp, err := c.cfg.Client.Do(r) + if err != nil { + return res, errors.Wrap(err, "do request") + } + defer resp.Body.Close() + + stage = "DecodeResponse" + result, err := decodeActionSubmissionResetSubmittingResponse(resp) + if err != nil { + return res, errors.Wrap(err, "decode response") + } + + return result, nil +} + // ActionSubmissionRetryValidate invokes actionSubmissionRetryValidate operation. // // Role Reviewer re-runs validation and changes status from Accepted -> Validating. @@ -2110,21 +2374,21 @@ func (c *Client) sendActionSubmissionRevoke(ctx context.Context, params ActionSu return result, nil } -// ActionSubmissionSubmit invokes actionSubmissionSubmit operation. +// ActionSubmissionTriggerSubmit invokes actionSubmissionTriggerSubmit operation. // -// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. +// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting. // -// POST /submissions/{SubmissionID}/status/submit -func (c *Client) ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error { - _, err := c.sendActionSubmissionSubmit(ctx, params) +// POST /submissions/{SubmissionID}/status/trigger-submit +func (c *Client) ActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) error { + _, err := c.sendActionSubmissionTriggerSubmit(ctx, params) return err } -func (c *Client) sendActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) (res *ActionSubmissionSubmitNoContent, err error) { +func (c *Client) sendActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) (res *ActionSubmissionTriggerSubmitNoContent, err error) { otelAttrs := []attribute.KeyValue{ - otelogen.OperationID("actionSubmissionSubmit"), + otelogen.OperationID("actionSubmissionTriggerSubmit"), semconv.HTTPRequestMethodKey.String("POST"), - semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/submit"), + semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/trigger-submit"), } // Run stopwatch. @@ -2139,7 +2403,7 @@ func (c *Client) sendActionSubmissionSubmit(ctx context.Context, params ActionSu c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) // Start a span for this request. - ctx, span := c.cfg.Tracer.Start(ctx, ActionSubmissionSubmitOperation, + ctx, span := c.cfg.Tracer.Start(ctx, ActionSubmissionTriggerSubmitOperation, trace.WithAttributes(otelAttrs...), clientSpanKind, ) @@ -2176,7 +2440,7 @@ func (c *Client) sendActionSubmissionSubmit(ctx context.Context, params ActionSu } pathParts[1] = encoded } - pathParts[2] = "/status/submit" + pathParts[2] = "/status/trigger-submit" uri.AddPathParts(u, pathParts[:]...) stage = "EncodeRequest" @@ -2190,7 +2454,7 @@ func (c *Client) sendActionSubmissionSubmit(ctx context.Context, params ActionSu var satisfied bitset { stage = "Security:CookieAuth" - switch err := c.securityCookieAuth(ctx, ActionSubmissionSubmitOperation, r); { + switch err := c.securityCookieAuth(ctx, ActionSubmissionTriggerSubmitOperation, r); { case err == nil: // if NO error satisfied[0] |= 1 << 0 case errors.Is(err, ogenerrors.ErrSkipClientSecurity): @@ -2226,7 +2490,7 @@ func (c *Client) sendActionSubmissionSubmit(ctx context.Context, params ActionSu defer resp.Body.Close() stage = "DecodeResponse" - result, err := decodeActionSubmissionSubmitResponse(resp) + result, err := decodeActionSubmissionTriggerSubmitResponse(resp) if err != nil { return res, errors.Wrap(err, "decode response") } diff --git a/pkg/api/oas_handlers_gen.go b/pkg/api/oas_handlers_gen.go index 995a780..e6bcc7b 100644 --- a/pkg/api/oas_handlers_gen.go +++ b/pkg/api/oas_handlers_gen.go @@ -615,6 +615,202 @@ func (s *Server) handleActionMapfixRequestChangesRequest(args [1]string, argsEsc } } +// handleActionMapfixResetSubmittingRequest handles actionMapfixResetSubmitting operation. +// +// Role Submitter manually resets submitting softlock and changes status from Submitting -> +// UnderConstruction. +// +// POST /mapfixes/{MapfixID}/status/reset-submitting +func (s *Server) handleActionMapfixResetSubmittingRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("actionMapfixResetSubmitting"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/reset-submitting"), + } + + // Start a span for this request. + ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixResetSubmittingOperation, + trace.WithAttributes(otelAttrs...), + serverSpanKind, + ) + defer span.End() + + // Add Labeler to context. + labeler := &Labeler{attrs: otelAttrs} + ctx = contextWithLabeler(ctx, labeler) + + // Run stopwatch. + startTime := time.Now() + defer func() { + elapsedDuration := time.Since(startTime) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) + + // Increment request counter. + s.requests.Add(ctx, 1, attrOpt) + + // Use floating point division here for higher precision (instead of Millisecond method). + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) + }() + + var ( + recordError = func(stage string, err error) { + span.RecordError(err) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + code := statusWriter.status + if code >= 100 && code < 500 { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) + } + err error + opErrContext = ogenerrors.OperationContext{ + Name: ActionMapfixResetSubmittingOperation, + ID: "actionMapfixResetSubmitting", + } + ) + { + type bitset = [1]uint8 + var satisfied bitset + { + sctx, ok, err := s.securityCookieAuth(ctx, ActionMapfixResetSubmittingOperation, r) + if err != nil { + err = &ogenerrors.SecurityError{ + OperationContext: opErrContext, + Security: "CookieAuth", + Err: err, + } + if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil { + defer recordError("Security:CookieAuth", err) + } + return + } + if ok { + satisfied[0] |= 1 << 0 + ctx = sctx + } + } + + if ok := func() bool { + nextRequirement: + for _, requirement := range []bitset{ + {0b00000001}, + } { + for i, mask := range requirement { + if satisfied[i]&mask != mask { + continue nextRequirement + } + } + return true + } + return false + }(); !ok { + err = &ogenerrors.SecurityError{ + OperationContext: opErrContext, + Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, + } + if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil { + defer recordError("Security", err) + } + return + } + } + params, err := decodeActionMapfixResetSubmittingParams(args, argsEscaped, r) + if err != nil { + err = &ogenerrors.DecodeParamsError{ + OperationContext: opErrContext, + Err: err, + } + defer recordError("DecodeParams", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + + var response *ActionMapfixResetSubmittingNoContent + if m := s.cfg.Middleware; m != nil { + mreq := middleware.Request{ + Context: ctx, + OperationName: ActionMapfixResetSubmittingOperation, + OperationSummary: "Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction", + OperationID: "actionMapfixResetSubmitting", + Body: nil, + Params: middleware.Parameters{ + { + Name: "MapfixID", + In: "path", + }: params.MapfixID, + }, + Raw: r, + } + + type ( + Request = struct{} + Params = ActionMapfixResetSubmittingParams + Response = *ActionMapfixResetSubmittingNoContent + ) + response, err = middleware.HookMiddleware[ + Request, + Params, + Response, + ]( + m, + mreq, + unpackActionMapfixResetSubmittingParams, + func(ctx context.Context, request Request, params Params) (response Response, err error) { + err = s.h.ActionMapfixResetSubmitting(ctx, params) + return response, err + }, + ) + } else { + err = s.h.ActionMapfixResetSubmitting(ctx, params) + } + if err != nil { + if errRes, ok := errors.Into[*ErrorStatusCode](err); ok { + if err := encodeErrorResponse(errRes, w, span); err != nil { + defer recordError("Internal", err) + } + return + } + if errors.Is(err, ht.ErrNotImplemented) { + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { + defer recordError("Internal", err) + } + return + } + + if err := encodeActionMapfixResetSubmittingResponse(response, w, span); err != nil { + defer recordError("EncodeResponse", err) + if !errors.Is(err, ht.ErrInternalServerErrorResponse) { + s.cfg.ErrorHandler(ctx, w, r, err) + } + return + } +} + // handleActionMapfixRetryValidateRequest handles actionMapfixRetryValidate operation. // // Role Reviewer re-runs validation and changes status from Accepted -> Validating. @@ -1005,22 +1201,22 @@ func (s *Server) handleActionMapfixRevokeRequest(args [1]string, argsEscaped boo } } -// handleActionMapfixSubmitRequest handles actionMapfixSubmit operation. +// handleActionMapfixTriggerSubmitRequest handles actionMapfixTriggerSubmit operation. // -// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. +// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting. // -// POST /mapfixes/{MapfixID}/status/submit -func (s *Server) handleActionMapfixSubmitRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { +// POST /mapfixes/{MapfixID}/status/trigger-submit +func (s *Server) handleActionMapfixTriggerSubmitRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ - otelogen.OperationID("actionMapfixSubmit"), + otelogen.OperationID("actionMapfixTriggerSubmit"), semconv.HTTPRequestMethodKey.String("POST"), - semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/submit"), + semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/trigger-submit"), } // Start a span for this request. - ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixSubmitOperation, + ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixTriggerSubmitOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) @@ -1075,15 +1271,15 @@ func (s *Server) handleActionMapfixSubmitRequest(args [1]string, argsEscaped boo } err error opErrContext = ogenerrors.OperationContext{ - Name: ActionMapfixSubmitOperation, - ID: "actionMapfixSubmit", + Name: ActionMapfixTriggerSubmitOperation, + ID: "actionMapfixTriggerSubmit", } ) { type bitset = [1]uint8 var satisfied bitset { - sctx, ok, err := s.securityCookieAuth(ctx, ActionMapfixSubmitOperation, r) + sctx, ok, err := s.securityCookieAuth(ctx, ActionMapfixTriggerSubmitOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, @@ -1125,7 +1321,7 @@ func (s *Server) handleActionMapfixSubmitRequest(args [1]string, argsEscaped boo return } } - params, err := decodeActionMapfixSubmitParams(args, argsEscaped, r) + params, err := decodeActionMapfixTriggerSubmitParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, @@ -1136,13 +1332,13 @@ func (s *Server) handleActionMapfixSubmitRequest(args [1]string, argsEscaped boo return } - var response *ActionMapfixSubmitNoContent + var response *ActionMapfixTriggerSubmitNoContent if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, - OperationName: ActionMapfixSubmitOperation, - OperationSummary: "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted", - OperationID: "actionMapfixSubmit", + OperationName: ActionMapfixTriggerSubmitOperation, + OperationSummary: "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting", + OperationID: "actionMapfixTriggerSubmit", Body: nil, Params: middleware.Parameters{ { @@ -1155,8 +1351,8 @@ func (s *Server) handleActionMapfixSubmitRequest(args [1]string, argsEscaped boo type ( Request = struct{} - Params = ActionMapfixSubmitParams - Response = *ActionMapfixSubmitNoContent + Params = ActionMapfixTriggerSubmitParams + Response = *ActionMapfixTriggerSubmitNoContent ) response, err = middleware.HookMiddleware[ Request, @@ -1165,14 +1361,14 @@ func (s *Server) handleActionMapfixSubmitRequest(args [1]string, argsEscaped boo ]( m, mreq, - unpackActionMapfixSubmitParams, + unpackActionMapfixTriggerSubmitParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { - err = s.h.ActionMapfixSubmit(ctx, params) + err = s.h.ActionMapfixTriggerSubmit(ctx, params) return response, err }, ) } else { - err = s.h.ActionMapfixSubmit(ctx, params) + err = s.h.ActionMapfixTriggerSubmit(ctx, params) } if err != nil { if errRes, ok := errors.Into[*ErrorStatusCode](err); ok { @@ -1191,7 +1387,7 @@ func (s *Server) handleActionMapfixSubmitRequest(args [1]string, argsEscaped boo return } - if err := encodeActionMapfixSubmitResponse(response, w, span); err != nil { + if err := encodeActionMapfixTriggerSubmitResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) @@ -2370,6 +2566,202 @@ func (s *Server) handleActionSubmissionRequestChangesRequest(args [1]string, arg } } +// handleActionSubmissionResetSubmittingRequest handles actionSubmissionResetSubmitting operation. +// +// Role Submitter manually resets submitting softlock and changes status from Submitting -> +// UnderConstruction. +// +// POST /submissions/{SubmissionID}/status/reset-submitting +func (s *Server) handleActionSubmissionResetSubmittingRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("actionSubmissionResetSubmitting"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/reset-submitting"), + } + + // Start a span for this request. + ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionResetSubmittingOperation, + trace.WithAttributes(otelAttrs...), + serverSpanKind, + ) + defer span.End() + + // Add Labeler to context. + labeler := &Labeler{attrs: otelAttrs} + ctx = contextWithLabeler(ctx, labeler) + + // Run stopwatch. + startTime := time.Now() + defer func() { + elapsedDuration := time.Since(startTime) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) + + // Increment request counter. + s.requests.Add(ctx, 1, attrOpt) + + // Use floating point division here for higher precision (instead of Millisecond method). + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) + }() + + var ( + recordError = func(stage string, err error) { + span.RecordError(err) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + code := statusWriter.status + if code >= 100 && code < 500 { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) + } + err error + opErrContext = ogenerrors.OperationContext{ + Name: ActionSubmissionResetSubmittingOperation, + ID: "actionSubmissionResetSubmitting", + } + ) + { + type bitset = [1]uint8 + var satisfied bitset + { + sctx, ok, err := s.securityCookieAuth(ctx, ActionSubmissionResetSubmittingOperation, r) + if err != nil { + err = &ogenerrors.SecurityError{ + OperationContext: opErrContext, + Security: "CookieAuth", + Err: err, + } + if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil { + defer recordError("Security:CookieAuth", err) + } + return + } + if ok { + satisfied[0] |= 1 << 0 + ctx = sctx + } + } + + if ok := func() bool { + nextRequirement: + for _, requirement := range []bitset{ + {0b00000001}, + } { + for i, mask := range requirement { + if satisfied[i]&mask != mask { + continue nextRequirement + } + } + return true + } + return false + }(); !ok { + err = &ogenerrors.SecurityError{ + OperationContext: opErrContext, + Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, + } + if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil { + defer recordError("Security", err) + } + return + } + } + params, err := decodeActionSubmissionResetSubmittingParams(args, argsEscaped, r) + if err != nil { + err = &ogenerrors.DecodeParamsError{ + OperationContext: opErrContext, + Err: err, + } + defer recordError("DecodeParams", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + + var response *ActionSubmissionResetSubmittingNoContent + if m := s.cfg.Middleware; m != nil { + mreq := middleware.Request{ + Context: ctx, + OperationName: ActionSubmissionResetSubmittingOperation, + OperationSummary: "Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction", + OperationID: "actionSubmissionResetSubmitting", + Body: nil, + Params: middleware.Parameters{ + { + Name: "SubmissionID", + In: "path", + }: params.SubmissionID, + }, + Raw: r, + } + + type ( + Request = struct{} + Params = ActionSubmissionResetSubmittingParams + Response = *ActionSubmissionResetSubmittingNoContent + ) + response, err = middleware.HookMiddleware[ + Request, + Params, + Response, + ]( + m, + mreq, + unpackActionSubmissionResetSubmittingParams, + func(ctx context.Context, request Request, params Params) (response Response, err error) { + err = s.h.ActionSubmissionResetSubmitting(ctx, params) + return response, err + }, + ) + } else { + err = s.h.ActionSubmissionResetSubmitting(ctx, params) + } + if err != nil { + if errRes, ok := errors.Into[*ErrorStatusCode](err); ok { + if err := encodeErrorResponse(errRes, w, span); err != nil { + defer recordError("Internal", err) + } + return + } + if errors.Is(err, ht.ErrNotImplemented) { + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { + defer recordError("Internal", err) + } + return + } + + if err := encodeActionSubmissionResetSubmittingResponse(response, w, span); err != nil { + defer recordError("EncodeResponse", err) + if !errors.Is(err, ht.ErrInternalServerErrorResponse) { + s.cfg.ErrorHandler(ctx, w, r, err) + } + return + } +} + // handleActionSubmissionRetryValidateRequest handles actionSubmissionRetryValidate operation. // // Role Reviewer re-runs validation and changes status from Accepted -> Validating. @@ -2760,22 +3152,22 @@ func (s *Server) handleActionSubmissionRevokeRequest(args [1]string, argsEscaped } } -// handleActionSubmissionSubmitRequest handles actionSubmissionSubmit operation. +// handleActionSubmissionTriggerSubmitRequest handles actionSubmissionTriggerSubmit operation. // -// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. +// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting. // -// POST /submissions/{SubmissionID}/status/submit -func (s *Server) handleActionSubmissionSubmitRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { +// POST /submissions/{SubmissionID}/status/trigger-submit +func (s *Server) handleActionSubmissionTriggerSubmitRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ - otelogen.OperationID("actionSubmissionSubmit"), + otelogen.OperationID("actionSubmissionTriggerSubmit"), semconv.HTTPRequestMethodKey.String("POST"), - semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/submit"), + semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/trigger-submit"), } // Start a span for this request. - ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionSubmitOperation, + ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionTriggerSubmitOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) @@ -2830,15 +3222,15 @@ func (s *Server) handleActionSubmissionSubmitRequest(args [1]string, argsEscaped } err error opErrContext = ogenerrors.OperationContext{ - Name: ActionSubmissionSubmitOperation, - ID: "actionSubmissionSubmit", + Name: ActionSubmissionTriggerSubmitOperation, + ID: "actionSubmissionTriggerSubmit", } ) { type bitset = [1]uint8 var satisfied bitset { - sctx, ok, err := s.securityCookieAuth(ctx, ActionSubmissionSubmitOperation, r) + sctx, ok, err := s.securityCookieAuth(ctx, ActionSubmissionTriggerSubmitOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, @@ -2880,7 +3272,7 @@ func (s *Server) handleActionSubmissionSubmitRequest(args [1]string, argsEscaped return } } - params, err := decodeActionSubmissionSubmitParams(args, argsEscaped, r) + params, err := decodeActionSubmissionTriggerSubmitParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, @@ -2891,13 +3283,13 @@ func (s *Server) handleActionSubmissionSubmitRequest(args [1]string, argsEscaped return } - var response *ActionSubmissionSubmitNoContent + var response *ActionSubmissionTriggerSubmitNoContent if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, - OperationName: ActionSubmissionSubmitOperation, - OperationSummary: "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted", - OperationID: "actionSubmissionSubmit", + OperationName: ActionSubmissionTriggerSubmitOperation, + OperationSummary: "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting", + OperationID: "actionSubmissionTriggerSubmit", Body: nil, Params: middleware.Parameters{ { @@ -2910,8 +3302,8 @@ func (s *Server) handleActionSubmissionSubmitRequest(args [1]string, argsEscaped type ( Request = struct{} - Params = ActionSubmissionSubmitParams - Response = *ActionSubmissionSubmitNoContent + Params = ActionSubmissionTriggerSubmitParams + Response = *ActionSubmissionTriggerSubmitNoContent ) response, err = middleware.HookMiddleware[ Request, @@ -2920,14 +3312,14 @@ func (s *Server) handleActionSubmissionSubmitRequest(args [1]string, argsEscaped ]( m, mreq, - unpackActionSubmissionSubmitParams, + unpackActionSubmissionTriggerSubmitParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { - err = s.h.ActionSubmissionSubmit(ctx, params) + err = s.h.ActionSubmissionTriggerSubmit(ctx, params) return response, err }, ) } else { - err = s.h.ActionSubmissionSubmit(ctx, params) + err = s.h.ActionSubmissionTriggerSubmit(ctx, params) } if err != nil { if errRes, ok := errors.Into[*ErrorStatusCode](err); ok { @@ -2946,7 +3338,7 @@ func (s *Server) handleActionSubmissionSubmitRequest(args [1]string, argsEscaped return } - if err := encodeActionSubmissionSubmitResponse(response, w, span); err != nil { + if err := encodeActionSubmissionTriggerSubmitResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) diff --git a/pkg/api/oas_operations_gen.go b/pkg/api/oas_operations_gen.go index 09d06f4..bbc161d 100644 --- a/pkg/api/oas_operations_gen.go +++ b/pkg/api/oas_operations_gen.go @@ -9,18 +9,20 @@ const ( ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted" ActionMapfixRejectOperation OperationName = "ActionMapfixReject" ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges" + ActionMapfixResetSubmittingOperation OperationName = "ActionMapfixResetSubmitting" ActionMapfixRetryValidateOperation OperationName = "ActionMapfixRetryValidate" ActionMapfixRevokeOperation OperationName = "ActionMapfixRevoke" - ActionMapfixSubmitOperation OperationName = "ActionMapfixSubmit" + ActionMapfixTriggerSubmitOperation OperationName = "ActionMapfixTriggerSubmit" ActionMapfixTriggerUploadOperation OperationName = "ActionMapfixTriggerUpload" ActionMapfixTriggerValidateOperation OperationName = "ActionMapfixTriggerValidate" ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated" ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted" ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject" ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges" + ActionSubmissionResetSubmittingOperation OperationName = "ActionSubmissionResetSubmitting" ActionSubmissionRetryValidateOperation OperationName = "ActionSubmissionRetryValidate" ActionSubmissionRevokeOperation OperationName = "ActionSubmissionRevoke" - ActionSubmissionSubmitOperation OperationName = "ActionSubmissionSubmit" + ActionSubmissionTriggerSubmitOperation OperationName = "ActionSubmissionTriggerSubmit" ActionSubmissionTriggerUploadOperation OperationName = "ActionSubmissionTriggerUpload" ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate" ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated" diff --git a/pkg/api/oas_parameters_gen.go b/pkg/api/oas_parameters_gen.go index b604dc9..f434a5f 100644 --- a/pkg/api/oas_parameters_gen.go +++ b/pkg/api/oas_parameters_gen.go @@ -264,6 +264,89 @@ func decodeActionMapfixRequestChangesParams(args [1]string, argsEscaped bool, r return params, nil } +// ActionMapfixResetSubmittingParams is parameters of actionMapfixResetSubmitting operation. +type ActionMapfixResetSubmittingParams struct { + // The unique identifier for a mapfix. + MapfixID int64 +} + +func unpackActionMapfixResetSubmittingParams(packed middleware.Parameters) (params ActionMapfixResetSubmittingParams) { + { + key := middleware.ParameterKey{ + Name: "MapfixID", + In: "path", + } + params.MapfixID = packed[key].(int64) + } + return params +} + +func decodeActionMapfixResetSubmittingParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixResetSubmittingParams, _ error) { + // Decode path: MapfixID. + if err := func() error { + param := args[0] + if argsEscaped { + unescaped, err := url.PathUnescape(args[0]) + if err != nil { + return errors.Wrap(err, "unescape path") + } + param = unescaped + } + if len(param) > 0 { + d := uri.NewPathDecoder(uri.PathDecoderConfig{ + Param: "MapfixID", + Value: param, + Style: uri.PathStyleSimple, + Explode: false, + }) + + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToInt64(val) + if err != nil { + return err + } + + params.MapfixID = c + return nil + }(); err != nil { + return err + } + if err := func() error { + if err := (validate.Int{ + MinSet: true, + Min: 0, + MaxSet: false, + Max: 0, + MinExclusive: false, + MaxExclusive: false, + MultipleOfSet: false, + MultipleOf: 0, + }).Validate(int64(params.MapfixID)); err != nil { + return errors.Wrap(err, "int") + } + return nil + }(); err != nil { + return err + } + } else { + return validate.ErrFieldRequired + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "MapfixID", + In: "path", + Err: err, + } + } + return params, nil +} + // ActionMapfixRetryValidateParams is parameters of actionMapfixRetryValidate operation. type ActionMapfixRetryValidateParams struct { // The unique identifier for a mapfix. @@ -430,13 +513,13 @@ func decodeActionMapfixRevokeParams(args [1]string, argsEscaped bool, r *http.Re return params, nil } -// ActionMapfixSubmitParams is parameters of actionMapfixSubmit operation. -type ActionMapfixSubmitParams struct { +// ActionMapfixTriggerSubmitParams is parameters of actionMapfixTriggerSubmit operation. +type ActionMapfixTriggerSubmitParams struct { // The unique identifier for a mapfix. MapfixID int64 } -func unpackActionMapfixSubmitParams(packed middleware.Parameters) (params ActionMapfixSubmitParams) { +func unpackActionMapfixTriggerSubmitParams(packed middleware.Parameters) (params ActionMapfixTriggerSubmitParams) { { key := middleware.ParameterKey{ Name: "MapfixID", @@ -447,7 +530,7 @@ func unpackActionMapfixSubmitParams(packed middleware.Parameters) (params Action return params } -func decodeActionMapfixSubmitParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixSubmitParams, _ error) { +func decodeActionMapfixTriggerSubmitParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixTriggerSubmitParams, _ error) { // Decode path: MapfixID. if err := func() error { param := args[0] @@ -1011,6 +1094,89 @@ func decodeActionSubmissionRequestChangesParams(args [1]string, argsEscaped bool return params, nil } +// ActionSubmissionResetSubmittingParams is parameters of actionSubmissionResetSubmitting operation. +type ActionSubmissionResetSubmittingParams struct { + // The unique identifier for a submission. + SubmissionID int64 +} + +func unpackActionSubmissionResetSubmittingParams(packed middleware.Parameters) (params ActionSubmissionResetSubmittingParams) { + { + key := middleware.ParameterKey{ + Name: "SubmissionID", + In: "path", + } + params.SubmissionID = packed[key].(int64) + } + return params +} + +func decodeActionSubmissionResetSubmittingParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionResetSubmittingParams, _ error) { + // Decode path: SubmissionID. + if err := func() error { + param := args[0] + if argsEscaped { + unescaped, err := url.PathUnescape(args[0]) + if err != nil { + return errors.Wrap(err, "unescape path") + } + param = unescaped + } + if len(param) > 0 { + d := uri.NewPathDecoder(uri.PathDecoderConfig{ + Param: "SubmissionID", + Value: param, + Style: uri.PathStyleSimple, + Explode: false, + }) + + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToInt64(val) + if err != nil { + return err + } + + params.SubmissionID = c + return nil + }(); err != nil { + return err + } + if err := func() error { + if err := (validate.Int{ + MinSet: true, + Min: 0, + MaxSet: false, + Max: 0, + MinExclusive: false, + MaxExclusive: false, + MultipleOfSet: false, + MultipleOf: 0, + }).Validate(int64(params.SubmissionID)); err != nil { + return errors.Wrap(err, "int") + } + return nil + }(); err != nil { + return err + } + } else { + return validate.ErrFieldRequired + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "SubmissionID", + In: "path", + Err: err, + } + } + return params, nil +} + // ActionSubmissionRetryValidateParams is parameters of actionSubmissionRetryValidate operation. type ActionSubmissionRetryValidateParams struct { // The unique identifier for a submission. @@ -1177,13 +1343,13 @@ func decodeActionSubmissionRevokeParams(args [1]string, argsEscaped bool, r *htt return params, nil } -// ActionSubmissionSubmitParams is parameters of actionSubmissionSubmit operation. -type ActionSubmissionSubmitParams struct { +// ActionSubmissionTriggerSubmitParams is parameters of actionSubmissionTriggerSubmit operation. +type ActionSubmissionTriggerSubmitParams struct { // The unique identifier for a submission. SubmissionID int64 } -func unpackActionSubmissionSubmitParams(packed middleware.Parameters) (params ActionSubmissionSubmitParams) { +func unpackActionSubmissionTriggerSubmitParams(packed middleware.Parameters) (params ActionSubmissionTriggerSubmitParams) { { key := middleware.ParameterKey{ Name: "SubmissionID", @@ -1194,7 +1360,7 @@ func unpackActionSubmissionSubmitParams(packed middleware.Parameters) (params Ac return params } -func decodeActionSubmissionSubmitParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionSubmitParams, _ error) { +func decodeActionSubmissionTriggerSubmitParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionTriggerSubmitParams, _ error) { // Decode path: SubmissionID. if err := func() error { param := args[0] diff --git a/pkg/api/oas_response_decoders_gen.go b/pkg/api/oas_response_decoders_gen.go index 242b3b7..e6336d5 100644 --- a/pkg/api/oas_response_decoders_gen.go +++ b/pkg/api/oas_response_decoders_gen.go @@ -195,6 +195,66 @@ func decodeActionMapfixRequestChangesResponse(resp *http.Response) (res *ActionM return res, errors.Wrap(defRes, "error") } +func decodeActionMapfixResetSubmittingResponse(resp *http.Response) (res *ActionMapfixResetSubmittingNoContent, _ error) { + switch resp.StatusCode { + case 204: + // Code 204. + return &ActionMapfixResetSubmittingNoContent{}, nil + } + // Convenient error response. + defRes, err := func() (res *ErrorStatusCode, err error) { + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response Error + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + // Validate response. + if err := func() error { + if err := response.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "validate") + } + return &ErrorStatusCode{ + StatusCode: resp.StatusCode, + Response: response, + }, nil + default: + return res, validate.InvalidContentType(ct) + } + }() + if err != nil { + return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode) + } + return res, errors.Wrap(defRes, "error") +} + func decodeActionMapfixRetryValidateResponse(resp *http.Response) (res *ActionMapfixRetryValidateNoContent, _ error) { switch resp.StatusCode { case 204: @@ -315,11 +375,11 @@ func decodeActionMapfixRevokeResponse(resp *http.Response) (res *ActionMapfixRev return res, errors.Wrap(defRes, "error") } -func decodeActionMapfixSubmitResponse(resp *http.Response) (res *ActionMapfixSubmitNoContent, _ error) { +func decodeActionMapfixTriggerSubmitResponse(resp *http.Response) (res *ActionMapfixTriggerSubmitNoContent, _ error) { switch resp.StatusCode { case 204: // Code 204. - return &ActionMapfixSubmitNoContent{}, nil + return &ActionMapfixTriggerSubmitNoContent{}, nil } // Convenient error response. defRes, err := func() (res *ErrorStatusCode, err error) { @@ -735,6 +795,66 @@ func decodeActionSubmissionRequestChangesResponse(resp *http.Response) (res *Act return res, errors.Wrap(defRes, "error") } +func decodeActionSubmissionResetSubmittingResponse(resp *http.Response) (res *ActionSubmissionResetSubmittingNoContent, _ error) { + switch resp.StatusCode { + case 204: + // Code 204. + return &ActionSubmissionResetSubmittingNoContent{}, nil + } + // Convenient error response. + defRes, err := func() (res *ErrorStatusCode, err error) { + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response Error + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + // Validate response. + if err := func() error { + if err := response.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "validate") + } + return &ErrorStatusCode{ + StatusCode: resp.StatusCode, + Response: response, + }, nil + default: + return res, validate.InvalidContentType(ct) + } + }() + if err != nil { + return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode) + } + return res, errors.Wrap(defRes, "error") +} + func decodeActionSubmissionRetryValidateResponse(resp *http.Response) (res *ActionSubmissionRetryValidateNoContent, _ error) { switch resp.StatusCode { case 204: @@ -855,11 +975,11 @@ func decodeActionSubmissionRevokeResponse(resp *http.Response) (res *ActionSubmi return res, errors.Wrap(defRes, "error") } -func decodeActionSubmissionSubmitResponse(resp *http.Response) (res *ActionSubmissionSubmitNoContent, _ error) { +func decodeActionSubmissionTriggerSubmitResponse(resp *http.Response) (res *ActionSubmissionTriggerSubmitNoContent, _ error) { switch resp.StatusCode { case 204: // Code 204. - return &ActionSubmissionSubmitNoContent{}, nil + return &ActionSubmissionTriggerSubmitNoContent{}, nil } // Convenient error response. defRes, err := func() (res *ErrorStatusCode, err error) { diff --git a/pkg/api/oas_response_encoders_gen.go b/pkg/api/oas_response_encoders_gen.go index cb0a98a..33e5527 100644 --- a/pkg/api/oas_response_encoders_gen.go +++ b/pkg/api/oas_response_encoders_gen.go @@ -34,6 +34,13 @@ func encodeActionMapfixRequestChangesResponse(response *ActionMapfixRequestChang return nil } +func encodeActionMapfixResetSubmittingResponse(response *ActionMapfixResetSubmittingNoContent, w http.ResponseWriter, span trace.Span) error { + w.WriteHeader(204) + span.SetStatus(codes.Ok, http.StatusText(204)) + + return nil +} + func encodeActionMapfixRetryValidateResponse(response *ActionMapfixRetryValidateNoContent, w http.ResponseWriter, span trace.Span) error { w.WriteHeader(204) span.SetStatus(codes.Ok, http.StatusText(204)) @@ -48,7 +55,7 @@ func encodeActionMapfixRevokeResponse(response *ActionMapfixRevokeNoContent, w h return nil } -func encodeActionMapfixSubmitResponse(response *ActionMapfixSubmitNoContent, w http.ResponseWriter, span trace.Span) error { +func encodeActionMapfixTriggerSubmitResponse(response *ActionMapfixTriggerSubmitNoContent, w http.ResponseWriter, span trace.Span) error { w.WriteHeader(204) span.SetStatus(codes.Ok, http.StatusText(204)) @@ -97,6 +104,13 @@ func encodeActionSubmissionRequestChangesResponse(response *ActionSubmissionRequ return nil } +func encodeActionSubmissionResetSubmittingResponse(response *ActionSubmissionResetSubmittingNoContent, w http.ResponseWriter, span trace.Span) error { + w.WriteHeader(204) + span.SetStatus(codes.Ok, http.StatusText(204)) + + return nil +} + func encodeActionSubmissionRetryValidateResponse(response *ActionSubmissionRetryValidateNoContent, w http.ResponseWriter, span trace.Span) error { w.WriteHeader(204) span.SetStatus(codes.Ok, http.StatusText(204)) @@ -111,7 +125,7 @@ func encodeActionSubmissionRevokeResponse(response *ActionSubmissionRevokeNoCont return nil } -func encodeActionSubmissionSubmitResponse(response *ActionSubmissionSubmitNoContent, w http.ResponseWriter, span trace.Span) error { +func encodeActionSubmissionTriggerSubmitResponse(response *ActionSubmissionTriggerSubmitNoContent, w http.ResponseWriter, span trace.Span) error { w.WriteHeader(204) span.SetStatus(codes.Ok, http.StatusText(204)) diff --git a/pkg/api/oas_router_gen.go b/pkg/api/oas_router_gen.go index 6989865..4244ce2 100644 --- a/pkg/api/oas_router_gen.go +++ b/pkg/api/oas_router_gen.go @@ -318,6 +318,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { break } switch elem[0] { + case 's': // Prefix: "submitting" + + if l := len("submitting"); len(elem) >= l && elem[0:l] == "submitting" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixResetSubmittingRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + case 'u': // Prefix: "uploading" if l := len("uploading"); len(elem) >= l && elem[0:l] == "uploading" { @@ -410,28 +432,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { } - case 's': // Prefix: "submit" - - if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleActionMapfixSubmitRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - case 't': // Prefix: "trigger-" if l := len("trigger-"); len(elem) >= l && elem[0:l] == "trigger-" { @@ -444,6 +444,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { break } switch elem[0] { + case 's': // Prefix: "submit" + + if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixTriggerSubmitRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + case 'u': // Prefix: "upload" if l := len("upload"); len(elem) >= l && elem[0:l] == "upload" { @@ -1072,6 +1094,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { break } switch elem[0] { + case 's': // Prefix: "submitting" + + if l := len("submitting"); len(elem) >= l && elem[0:l] == "submitting" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionSubmissionResetSubmittingRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + case 'u': // Prefix: "uploading" if l := len("uploading"); len(elem) >= l && elem[0:l] == "uploading" { @@ -1164,28 +1208,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { } - case 's': // Prefix: "submit" - - if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleActionSubmissionSubmitRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - case 't': // Prefix: "trigger-" if l := len("trigger-"); len(elem) >= l && elem[0:l] == "trigger-" { @@ -1198,6 +1220,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { break } switch elem[0] { + case 's': // Prefix: "submit" + + if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionSubmissionTriggerSubmitRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + case 'u': // Prefix: "upload" if l := len("upload"); len(elem) >= l && elem[0:l] == "upload" { @@ -1629,6 +1673,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { break } switch elem[0] { + case 's': // Prefix: "submitting" + + if l := len("submitting"); len(elem) >= l && elem[0:l] == "submitting" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixResetSubmittingOperation + r.summary = "Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction" + r.operationID = "actionMapfixResetSubmitting" + r.pathPattern = "/mapfixes/{MapfixID}/status/reset-submitting" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + case 'u': // Prefix: "uploading" if l := len("uploading"); len(elem) >= l && elem[0:l] == "uploading" { @@ -1729,30 +1797,6 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { } - case 's': // Prefix: "submit" - - if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = ActionMapfixSubmitOperation - r.summary = "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted" - r.operationID = "actionMapfixSubmit" - r.pathPattern = "/mapfixes/{MapfixID}/status/submit" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - case 't': // Prefix: "trigger-" if l := len("trigger-"); len(elem) >= l && elem[0:l] == "trigger-" { @@ -1765,6 +1809,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { break } switch elem[0] { + case 's': // Prefix: "submit" + + if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixTriggerSubmitOperation + r.summary = "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting" + r.operationID = "actionMapfixTriggerSubmit" + r.pathPattern = "/mapfixes/{MapfixID}/status/trigger-submit" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + case 'u': // Prefix: "upload" if l := len("upload"); len(elem) >= l && elem[0:l] == "upload" { @@ -2485,6 +2553,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { break } switch elem[0] { + case 's': // Prefix: "submitting" + + if l := len("submitting"); len(elem) >= l && elem[0:l] == "submitting" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionSubmissionResetSubmittingOperation + r.summary = "Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction" + r.operationID = "actionSubmissionResetSubmitting" + r.pathPattern = "/submissions/{SubmissionID}/status/reset-submitting" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + case 'u': // Prefix: "uploading" if l := len("uploading"); len(elem) >= l && elem[0:l] == "uploading" { @@ -2585,30 +2677,6 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { } - case 's': // Prefix: "submit" - - if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = ActionSubmissionSubmitOperation - r.summary = "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted" - r.operationID = "actionSubmissionSubmit" - r.pathPattern = "/submissions/{SubmissionID}/status/submit" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - case 't': // Prefix: "trigger-" if l := len("trigger-"); len(elem) >= l && elem[0:l] == "trigger-" { @@ -2621,6 +2689,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { break } switch elem[0] { + case 's': // Prefix: "submit" + + if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionSubmissionTriggerSubmitOperation + r.summary = "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting" + r.operationID = "actionSubmissionTriggerSubmit" + r.pathPattern = "/submissions/{SubmissionID}/status/trigger-submit" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + case 'u': // Prefix: "upload" if l := len("upload"); len(elem) >= l && elem[0:l] == "upload" { diff --git a/pkg/api/oas_schemas_gen.go b/pkg/api/oas_schemas_gen.go index 5590292..b54f84e 100644 --- a/pkg/api/oas_schemas_gen.go +++ b/pkg/api/oas_schemas_gen.go @@ -23,14 +23,17 @@ type ActionMapfixRejectNoContent struct{} // ActionMapfixRequestChangesNoContent is response for ActionMapfixRequestChanges operation. type ActionMapfixRequestChangesNoContent struct{} +// ActionMapfixResetSubmittingNoContent is response for ActionMapfixResetSubmitting operation. +type ActionMapfixResetSubmittingNoContent struct{} + // ActionMapfixRetryValidateNoContent is response for ActionMapfixRetryValidate operation. type ActionMapfixRetryValidateNoContent struct{} // ActionMapfixRevokeNoContent is response for ActionMapfixRevoke operation. type ActionMapfixRevokeNoContent struct{} -// ActionMapfixSubmitNoContent is response for ActionMapfixSubmit operation. -type ActionMapfixSubmitNoContent struct{} +// ActionMapfixTriggerSubmitNoContent is response for ActionMapfixTriggerSubmit operation. +type ActionMapfixTriggerSubmitNoContent struct{} // ActionMapfixTriggerUploadNoContent is response for ActionMapfixTriggerUpload operation. type ActionMapfixTriggerUploadNoContent struct{} @@ -50,14 +53,17 @@ type ActionSubmissionRejectNoContent struct{} // ActionSubmissionRequestChangesNoContent is response for ActionSubmissionRequestChanges operation. type ActionSubmissionRequestChangesNoContent struct{} +// ActionSubmissionResetSubmittingNoContent is response for ActionSubmissionResetSubmitting operation. +type ActionSubmissionResetSubmittingNoContent struct{} + // ActionSubmissionRetryValidateNoContent is response for ActionSubmissionRetryValidate operation. type ActionSubmissionRetryValidateNoContent struct{} // ActionSubmissionRevokeNoContent is response for ActionSubmissionRevoke operation. type ActionSubmissionRevokeNoContent struct{} -// ActionSubmissionSubmitNoContent is response for ActionSubmissionSubmit operation. -type ActionSubmissionSubmitNoContent struct{} +// ActionSubmissionTriggerSubmitNoContent is response for ActionSubmissionTriggerSubmit operation. +type ActionSubmissionTriggerSubmitNoContent struct{} // ActionSubmissionTriggerUploadNoContent is response for ActionSubmissionTriggerUpload operation. type ActionSubmissionTriggerUploadNoContent struct{} diff --git a/pkg/api/oas_server_gen.go b/pkg/api/oas_server_gen.go index 0fddb0c..aa46501 100644 --- a/pkg/api/oas_server_gen.go +++ b/pkg/api/oas_server_gen.go @@ -26,6 +26,13 @@ type Handler interface { // // POST /mapfixes/{MapfixID}/status/request-changes ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error + // ActionMapfixResetSubmitting implements actionMapfixResetSubmitting operation. + // + // Role Submitter manually resets submitting softlock and changes status from Submitting -> + // UnderConstruction. + // + // POST /mapfixes/{MapfixID}/status/reset-submitting + ActionMapfixResetSubmitting(ctx context.Context, params ActionMapfixResetSubmittingParams) error // ActionMapfixRetryValidate implements actionMapfixRetryValidate operation. // // Role Reviewer re-runs validation and changes status from Accepted -> Validating. @@ -38,12 +45,12 @@ type Handler interface { // // POST /mapfixes/{MapfixID}/status/revoke ActionMapfixRevoke(ctx context.Context, params ActionMapfixRevokeParams) error - // ActionMapfixSubmit implements actionMapfixSubmit operation. + // ActionMapfixTriggerSubmit implements actionMapfixTriggerSubmit operation. // - // Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. + // Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting. // - // POST /mapfixes/{MapfixID}/status/submit - ActionMapfixSubmit(ctx context.Context, params ActionMapfixSubmitParams) error + // POST /mapfixes/{MapfixID}/status/trigger-submit + ActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) error // ActionMapfixTriggerUpload implements actionMapfixTriggerUpload operation. // // Role Admin changes status from Validated -> Uploading. @@ -80,6 +87,13 @@ type Handler interface { // // POST /submissions/{SubmissionID}/status/request-changes ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error + // ActionSubmissionResetSubmitting implements actionSubmissionResetSubmitting operation. + // + // Role Submitter manually resets submitting softlock and changes status from Submitting -> + // UnderConstruction. + // + // POST /submissions/{SubmissionID}/status/reset-submitting + ActionSubmissionResetSubmitting(ctx context.Context, params ActionSubmissionResetSubmittingParams) error // ActionSubmissionRetryValidate implements actionSubmissionRetryValidate operation. // // Role Reviewer re-runs validation and changes status from Accepted -> Validating. @@ -92,12 +106,12 @@ type Handler interface { // // POST /submissions/{SubmissionID}/status/revoke ActionSubmissionRevoke(ctx context.Context, params ActionSubmissionRevokeParams) error - // ActionSubmissionSubmit implements actionSubmissionSubmit operation. + // ActionSubmissionTriggerSubmit implements actionSubmissionTriggerSubmit operation. // - // Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. + // Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting. // - // POST /submissions/{SubmissionID}/status/submit - ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error + // POST /submissions/{SubmissionID}/status/trigger-submit + ActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) error // ActionSubmissionTriggerUpload implements actionSubmissionTriggerUpload operation. // // Role Admin changes status from Validated -> Uploading. diff --git a/pkg/api/oas_unimplemented_gen.go b/pkg/api/oas_unimplemented_gen.go index cacd62c..239065f 100644 --- a/pkg/api/oas_unimplemented_gen.go +++ b/pkg/api/oas_unimplemented_gen.go @@ -40,6 +40,16 @@ func (UnimplementedHandler) ActionMapfixRequestChanges(ctx context.Context, para return ht.ErrNotImplemented } +// ActionMapfixResetSubmitting implements actionMapfixResetSubmitting operation. +// +// Role Submitter manually resets submitting softlock and changes status from Submitting -> +// UnderConstruction. +// +// POST /mapfixes/{MapfixID}/status/reset-submitting +func (UnimplementedHandler) ActionMapfixResetSubmitting(ctx context.Context, params ActionMapfixResetSubmittingParams) error { + return ht.ErrNotImplemented +} + // ActionMapfixRetryValidate implements actionMapfixRetryValidate operation. // // Role Reviewer re-runs validation and changes status from Accepted -> Validating. @@ -58,12 +68,12 @@ func (UnimplementedHandler) ActionMapfixRevoke(ctx context.Context, params Actio return ht.ErrNotImplemented } -// ActionMapfixSubmit implements actionMapfixSubmit operation. +// ActionMapfixTriggerSubmit implements actionMapfixTriggerSubmit operation. // -// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. +// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting. // -// POST /mapfixes/{MapfixID}/status/submit -func (UnimplementedHandler) ActionMapfixSubmit(ctx context.Context, params ActionMapfixSubmitParams) error { +// POST /mapfixes/{MapfixID}/status/trigger-submit +func (UnimplementedHandler) ActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) error { return ht.ErrNotImplemented } @@ -121,6 +131,16 @@ func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context, return ht.ErrNotImplemented } +// ActionSubmissionResetSubmitting implements actionSubmissionResetSubmitting operation. +// +// Role Submitter manually resets submitting softlock and changes status from Submitting -> +// UnderConstruction. +// +// POST /submissions/{SubmissionID}/status/reset-submitting +func (UnimplementedHandler) ActionSubmissionResetSubmitting(ctx context.Context, params ActionSubmissionResetSubmittingParams) error { + return ht.ErrNotImplemented +} + // ActionSubmissionRetryValidate implements actionSubmissionRetryValidate operation. // // Role Reviewer re-runs validation and changes status from Accepted -> Validating. @@ -139,12 +159,12 @@ func (UnimplementedHandler) ActionSubmissionRevoke(ctx context.Context, params A return ht.ErrNotImplemented } -// ActionSubmissionSubmit implements actionSubmissionSubmit operation. +// ActionSubmissionTriggerSubmit implements actionSubmissionTriggerSubmit operation. // -// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. +// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting. // -// POST /submissions/{SubmissionID}/status/submit -func (UnimplementedHandler) ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error { +// POST /submissions/{SubmissionID}/status/trigger-submit +func (UnimplementedHandler) ActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) error { return ht.ErrNotImplemented } diff --git a/pkg/internal/oas_client_gen.go b/pkg/internal/oas_client_gen.go index 0248a3f..3a33e68 100644 --- a/pkg/internal/oas_client_gen.go +++ b/pkg/internal/oas_client_gen.go @@ -34,6 +34,12 @@ type Invoker interface { // // POST /mapfixes/{MapfixID}/status/validator-failed ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error + // ActionMapfixSubmitted invokes actionMapfixSubmitted operation. + // + // (Internal endpoint) Role Validator changes status from Submitting -> Submitted. + // + // POST /mapfixes/{MapfixID}/status/validator-submitted + ActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) error // ActionMapfixUploaded invokes actionMapfixUploaded operation. // // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. @@ -58,6 +64,12 @@ type Invoker interface { // // POST /submissions/{SubmissionID}/status/validator-failed ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error + // ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation. + // + // (Internal endpoint) Role Validator changes status from Submitting -> Submitted. + // + // POST /submissions/{SubmissionID}/status/validator-submitted + ActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) error // ActionSubmissionUploaded invokes actionSubmissionUploaded operation. // // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. @@ -282,6 +294,97 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf return result, nil } +// ActionMapfixSubmitted invokes actionMapfixSubmitted operation. +// +// (Internal endpoint) Role Validator changes status from Submitting -> Submitted. +// +// POST /mapfixes/{MapfixID}/status/validator-submitted +func (c *Client) ActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) error { + _, err := c.sendActionMapfixSubmitted(ctx, params) + return err +} + +func (c *Client) sendActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) (res *ActionMapfixSubmittedNoContent, err error) { + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("actionMapfixSubmitted"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-submitted"), + } + + // Run stopwatch. + startTime := time.Now() + defer func() { + // Use floating point division here for higher precision (instead of Millisecond method). + elapsedDuration := time.Since(startTime) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) + }() + + // Increment request counter. + c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + + // Start a span for this request. + ctx, span := c.cfg.Tracer.Start(ctx, ActionMapfixSubmittedOperation, + trace.WithAttributes(otelAttrs...), + clientSpanKind, + ) + // Track stage for error reporting. + var stage string + defer func() { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, stage) + c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + } + span.End() + }() + + stage = "BuildURL" + u := uri.Clone(c.requestURL(ctx)) + var pathParts [3]string + pathParts[0] = "/mapfixes/" + { + // Encode "MapfixID" parameter. + e := uri.NewPathEncoder(uri.PathEncoderConfig{ + Param: "MapfixID", + Style: uri.PathStyleSimple, + Explode: false, + }) + if err := func() error { + return e.EncodeValue(conv.Int64ToString(params.MapfixID)) + }(); err != nil { + return res, errors.Wrap(err, "encode path") + } + encoded, err := e.Result() + if err != nil { + return res, errors.Wrap(err, "encode path") + } + pathParts[1] = encoded + } + pathParts[2] = "/status/validator-submitted" + uri.AddPathParts(u, pathParts[:]...) + + stage = "EncodeRequest" + r, err := ht.NewRequest(ctx, "POST", u) + if err != nil { + return res, errors.Wrap(err, "create request") + } + + stage = "SendRequest" + resp, err := c.cfg.Client.Do(r) + if err != nil { + return res, errors.Wrap(err, "do request") + } + defer resp.Body.Close() + + stage = "DecodeResponse" + result, err := decodeActionMapfixSubmittedResponse(resp) + if err != nil { + return res, errors.Wrap(err, "decode response") + } + + return result, nil +} + // ActionMapfixUploaded invokes actionMapfixUploaded operation. // // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. @@ -682,6 +785,97 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action return result, nil } +// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation. +// +// (Internal endpoint) Role Validator changes status from Submitting -> Submitted. +// +// POST /submissions/{SubmissionID}/status/validator-submitted +func (c *Client) ActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) error { + _, err := c.sendActionSubmissionSubmitted(ctx, params) + return err +} + +func (c *Client) sendActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) (res *ActionSubmissionSubmittedNoContent, err error) { + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("actionSubmissionSubmitted"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validator-submitted"), + } + + // Run stopwatch. + startTime := time.Now() + defer func() { + // Use floating point division here for higher precision (instead of Millisecond method). + elapsedDuration := time.Since(startTime) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) + }() + + // Increment request counter. + c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + + // Start a span for this request. + ctx, span := c.cfg.Tracer.Start(ctx, ActionSubmissionSubmittedOperation, + trace.WithAttributes(otelAttrs...), + clientSpanKind, + ) + // Track stage for error reporting. + var stage string + defer func() { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, stage) + c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + } + span.End() + }() + + stage = "BuildURL" + u := uri.Clone(c.requestURL(ctx)) + var pathParts [3]string + pathParts[0] = "/submissions/" + { + // Encode "SubmissionID" parameter. + e := uri.NewPathEncoder(uri.PathEncoderConfig{ + Param: "SubmissionID", + Style: uri.PathStyleSimple, + Explode: false, + }) + if err := func() error { + return e.EncodeValue(conv.Int64ToString(params.SubmissionID)) + }(); err != nil { + return res, errors.Wrap(err, "encode path") + } + encoded, err := e.Result() + if err != nil { + return res, errors.Wrap(err, "encode path") + } + pathParts[1] = encoded + } + pathParts[2] = "/status/validator-submitted" + uri.AddPathParts(u, pathParts[:]...) + + stage = "EncodeRequest" + r, err := ht.NewRequest(ctx, "POST", u) + if err != nil { + return res, errors.Wrap(err, "create request") + } + + stage = "SendRequest" + resp, err := c.cfg.Client.Do(r) + if err != nil { + return res, errors.Wrap(err, "do request") + } + defer resp.Body.Close() + + stage = "DecodeResponse" + result, err := decodeActionSubmissionSubmittedResponse(resp) + if err != nil { + return res, errors.Wrap(err, "decode response") + } + + return result, nil +} + // ActionSubmissionUploaded invokes actionSubmissionUploaded operation. // // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. diff --git a/pkg/internal/oas_handlers_gen.go b/pkg/internal/oas_handlers_gen.go index b382980..a205b0e 100644 --- a/pkg/internal/oas_handlers_gen.go +++ b/pkg/internal/oas_handlers_gen.go @@ -183,6 +183,155 @@ func (s *Server) handleActionMapfixAcceptedRequest(args [1]string, argsEscaped b } } +// handleActionMapfixSubmittedRequest handles actionMapfixSubmitted operation. +// +// (Internal endpoint) Role Validator changes status from Submitting -> Submitted. +// +// POST /mapfixes/{MapfixID}/status/validator-submitted +func (s *Server) handleActionMapfixSubmittedRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("actionMapfixSubmitted"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-submitted"), + } + + // Start a span for this request. + ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixSubmittedOperation, + trace.WithAttributes(otelAttrs...), + serverSpanKind, + ) + defer span.End() + + // Add Labeler to context. + labeler := &Labeler{attrs: otelAttrs} + ctx = contextWithLabeler(ctx, labeler) + + // Run stopwatch. + startTime := time.Now() + defer func() { + elapsedDuration := time.Since(startTime) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) + + // Increment request counter. + s.requests.Add(ctx, 1, attrOpt) + + // Use floating point division here for higher precision (instead of Millisecond method). + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) + }() + + var ( + recordError = func(stage string, err error) { + span.RecordError(err) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + code := statusWriter.status + if code >= 100 && code < 500 { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) + } + err error + opErrContext = ogenerrors.OperationContext{ + Name: ActionMapfixSubmittedOperation, + ID: "actionMapfixSubmitted", + } + ) + params, err := decodeActionMapfixSubmittedParams(args, argsEscaped, r) + if err != nil { + err = &ogenerrors.DecodeParamsError{ + OperationContext: opErrContext, + Err: err, + } + defer recordError("DecodeParams", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + + var response *ActionMapfixSubmittedNoContent + if m := s.cfg.Middleware; m != nil { + mreq := middleware.Request{ + Context: ctx, + OperationName: ActionMapfixSubmittedOperation, + OperationSummary: "(Internal endpoint) Role Validator changes status from Submitting -> Submitted", + OperationID: "actionMapfixSubmitted", + Body: nil, + Params: middleware.Parameters{ + { + Name: "MapfixID", + In: "path", + }: params.MapfixID, + }, + Raw: r, + } + + type ( + Request = struct{} + Params = ActionMapfixSubmittedParams + Response = *ActionMapfixSubmittedNoContent + ) + response, err = middleware.HookMiddleware[ + Request, + Params, + Response, + ]( + m, + mreq, + unpackActionMapfixSubmittedParams, + func(ctx context.Context, request Request, params Params) (response Response, err error) { + err = s.h.ActionMapfixSubmitted(ctx, params) + return response, err + }, + ) + } else { + err = s.h.ActionMapfixSubmitted(ctx, params) + } + if err != nil { + if errRes, ok := errors.Into[*ErrorStatusCode](err); ok { + if err := encodeErrorResponse(errRes, w, span); err != nil { + defer recordError("Internal", err) + } + return + } + if errors.Is(err, ht.ErrNotImplemented) { + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { + defer recordError("Internal", err) + } + return + } + + if err := encodeActionMapfixSubmittedResponse(response, w, span); err != nil { + defer recordError("EncodeResponse", err) + if !errors.Is(err, ht.ErrInternalServerErrorResponse) { + s.cfg.ErrorHandler(ctx, w, r, err) + } + return + } +} + // handleActionMapfixUploadedRequest handles actionMapfixUploaded operation. // // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. @@ -787,6 +936,155 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap } } +// handleActionSubmissionSubmittedRequest handles actionSubmissionSubmitted operation. +// +// (Internal endpoint) Role Validator changes status from Submitting -> Submitted. +// +// POST /submissions/{SubmissionID}/status/validator-submitted +func (s *Server) handleActionSubmissionSubmittedRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("actionSubmissionSubmitted"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validator-submitted"), + } + + // Start a span for this request. + ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionSubmittedOperation, + trace.WithAttributes(otelAttrs...), + serverSpanKind, + ) + defer span.End() + + // Add Labeler to context. + labeler := &Labeler{attrs: otelAttrs} + ctx = contextWithLabeler(ctx, labeler) + + // Run stopwatch. + startTime := time.Now() + defer func() { + elapsedDuration := time.Since(startTime) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) + + // Increment request counter. + s.requests.Add(ctx, 1, attrOpt) + + // Use floating point division here for higher precision (instead of Millisecond method). + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) + }() + + var ( + recordError = func(stage string, err error) { + span.RecordError(err) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + code := statusWriter.status + if code >= 100 && code < 500 { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) + } + err error + opErrContext = ogenerrors.OperationContext{ + Name: ActionSubmissionSubmittedOperation, + ID: "actionSubmissionSubmitted", + } + ) + params, err := decodeActionSubmissionSubmittedParams(args, argsEscaped, r) + if err != nil { + err = &ogenerrors.DecodeParamsError{ + OperationContext: opErrContext, + Err: err, + } + defer recordError("DecodeParams", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + + var response *ActionSubmissionSubmittedNoContent + if m := s.cfg.Middleware; m != nil { + mreq := middleware.Request{ + Context: ctx, + OperationName: ActionSubmissionSubmittedOperation, + OperationSummary: "(Internal endpoint) Role Validator changes status from Submitting -> Submitted", + OperationID: "actionSubmissionSubmitted", + Body: nil, + Params: middleware.Parameters{ + { + Name: "SubmissionID", + In: "path", + }: params.SubmissionID, + }, + Raw: r, + } + + type ( + Request = struct{} + Params = ActionSubmissionSubmittedParams + Response = *ActionSubmissionSubmittedNoContent + ) + response, err = middleware.HookMiddleware[ + Request, + Params, + Response, + ]( + m, + mreq, + unpackActionSubmissionSubmittedParams, + func(ctx context.Context, request Request, params Params) (response Response, err error) { + err = s.h.ActionSubmissionSubmitted(ctx, params) + return response, err + }, + ) + } else { + err = s.h.ActionSubmissionSubmitted(ctx, params) + } + if err != nil { + if errRes, ok := errors.Into[*ErrorStatusCode](err); ok { + if err := encodeErrorResponse(errRes, w, span); err != nil { + defer recordError("Internal", err) + } + return + } + if errors.Is(err, ht.ErrNotImplemented) { + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { + defer recordError("Internal", err) + } + return + } + + if err := encodeActionSubmissionSubmittedResponse(response, w, span); err != nil { + defer recordError("EncodeResponse", err) + if !errors.Is(err, ht.ErrInternalServerErrorResponse) { + s.cfg.ErrorHandler(ctx, w, r, err) + } + return + } +} + // handleActionSubmissionUploadedRequest handles actionSubmissionUploaded operation. // // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. diff --git a/pkg/internal/oas_operations_gen.go b/pkg/internal/oas_operations_gen.go index 9e4ecd3..cc5e6af 100644 --- a/pkg/internal/oas_operations_gen.go +++ b/pkg/internal/oas_operations_gen.go @@ -7,10 +7,12 @@ type OperationName = string const ( ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted" + ActionMapfixSubmittedOperation OperationName = "ActionMapfixSubmitted" ActionMapfixUploadedOperation OperationName = "ActionMapfixUploaded" ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated" ActionOperationFailedOperation OperationName = "ActionOperationFailed" ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted" + ActionSubmissionSubmittedOperation OperationName = "ActionSubmissionSubmitted" ActionSubmissionUploadedOperation OperationName = "ActionSubmissionUploaded" ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated" CreateMapfixOperation OperationName = "CreateMapfix" diff --git a/pkg/internal/oas_parameters_gen.go b/pkg/internal/oas_parameters_gen.go index 0832e8f..f564af1 100644 --- a/pkg/internal/oas_parameters_gen.go +++ b/pkg/internal/oas_parameters_gen.go @@ -159,6 +159,89 @@ func decodeActionMapfixAcceptedParams(args [1]string, argsEscaped bool, r *http. return params, nil } +// ActionMapfixSubmittedParams is parameters of actionMapfixSubmitted operation. +type ActionMapfixSubmittedParams struct { + // The unique identifier for a submission. + MapfixID int64 +} + +func unpackActionMapfixSubmittedParams(packed middleware.Parameters) (params ActionMapfixSubmittedParams) { + { + key := middleware.ParameterKey{ + Name: "MapfixID", + In: "path", + } + params.MapfixID = packed[key].(int64) + } + return params +} + +func decodeActionMapfixSubmittedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixSubmittedParams, _ error) { + // Decode path: MapfixID. + if err := func() error { + param := args[0] + if argsEscaped { + unescaped, err := url.PathUnescape(args[0]) + if err != nil { + return errors.Wrap(err, "unescape path") + } + param = unescaped + } + if len(param) > 0 { + d := uri.NewPathDecoder(uri.PathDecoderConfig{ + Param: "MapfixID", + Value: param, + Style: uri.PathStyleSimple, + Explode: false, + }) + + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToInt64(val) + if err != nil { + return err + } + + params.MapfixID = c + return nil + }(); err != nil { + return err + } + if err := func() error { + if err := (validate.Int{ + MinSet: true, + Min: 0, + MaxSet: false, + Max: 0, + MinExclusive: false, + MaxExclusive: false, + MultipleOfSet: false, + MultipleOf: 0, + }).Validate(int64(params.MapfixID)); err != nil { + return errors.Wrap(err, "int") + } + return nil + }(); err != nil { + return err + } + } else { + return validate.ErrFieldRequired + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "MapfixID", + In: "path", + Err: err, + } + } + return params, nil +} + // ActionMapfixUploadedParams is parameters of actionMapfixUploaded operation. type ActionMapfixUploadedParams struct { // The unique identifier for a submission. @@ -613,6 +696,89 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h return params, nil } +// ActionSubmissionSubmittedParams is parameters of actionSubmissionSubmitted operation. +type ActionSubmissionSubmittedParams struct { + // The unique identifier for a submission. + SubmissionID int64 +} + +func unpackActionSubmissionSubmittedParams(packed middleware.Parameters) (params ActionSubmissionSubmittedParams) { + { + key := middleware.ParameterKey{ + Name: "SubmissionID", + In: "path", + } + params.SubmissionID = packed[key].(int64) + } + return params +} + +func decodeActionSubmissionSubmittedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionSubmittedParams, _ error) { + // Decode path: SubmissionID. + if err := func() error { + param := args[0] + if argsEscaped { + unescaped, err := url.PathUnescape(args[0]) + if err != nil { + return errors.Wrap(err, "unescape path") + } + param = unescaped + } + if len(param) > 0 { + d := uri.NewPathDecoder(uri.PathDecoderConfig{ + Param: "SubmissionID", + Value: param, + Style: uri.PathStyleSimple, + Explode: false, + }) + + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToInt64(val) + if err != nil { + return err + } + + params.SubmissionID = c + return nil + }(); err != nil { + return err + } + if err := func() error { + if err := (validate.Int{ + MinSet: true, + Min: 0, + MaxSet: false, + Max: 0, + MinExclusive: false, + MaxExclusive: false, + MultipleOfSet: false, + MultipleOf: 0, + }).Validate(int64(params.SubmissionID)); err != nil { + return errors.Wrap(err, "int") + } + return nil + }(); err != nil { + return err + } + } else { + return validate.ErrFieldRequired + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "SubmissionID", + In: "path", + Err: err, + } + } + return params, nil +} + // ActionSubmissionUploadedParams is parameters of actionSubmissionUploaded operation. type ActionSubmissionUploadedParams struct { // The unique identifier for a submission. diff --git a/pkg/internal/oas_response_decoders_gen.go b/pkg/internal/oas_response_decoders_gen.go index d86dfef..8b7f1da 100644 --- a/pkg/internal/oas_response_decoders_gen.go +++ b/pkg/internal/oas_response_decoders_gen.go @@ -75,6 +75,66 @@ func decodeActionMapfixAcceptedResponse(resp *http.Response) (res *ActionMapfixA return res, errors.Wrap(defRes, "error") } +func decodeActionMapfixSubmittedResponse(resp *http.Response) (res *ActionMapfixSubmittedNoContent, _ error) { + switch resp.StatusCode { + case 204: + // Code 204. + return &ActionMapfixSubmittedNoContent{}, nil + } + // Convenient error response. + defRes, err := func() (res *ErrorStatusCode, err error) { + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response Error + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + // Validate response. + if err := func() error { + if err := response.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "validate") + } + return &ErrorStatusCode{ + StatusCode: resp.StatusCode, + Response: response, + }, nil + default: + return res, validate.InvalidContentType(ct) + } + }() + if err != nil { + return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode) + } + return res, errors.Wrap(defRes, "error") +} + func decodeActionMapfixUploadedResponse(resp *http.Response) (res *ActionMapfixUploadedNoContent, _ error) { switch resp.StatusCode { case 204: @@ -315,6 +375,66 @@ func decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSub return res, errors.Wrap(defRes, "error") } +func decodeActionSubmissionSubmittedResponse(resp *http.Response) (res *ActionSubmissionSubmittedNoContent, _ error) { + switch resp.StatusCode { + case 204: + // Code 204. + return &ActionSubmissionSubmittedNoContent{}, nil + } + // Convenient error response. + defRes, err := func() (res *ErrorStatusCode, err error) { + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response Error + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + // Validate response. + if err := func() error { + if err := response.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "validate") + } + return &ErrorStatusCode{ + StatusCode: resp.StatusCode, + Response: response, + }, nil + default: + return res, validate.InvalidContentType(ct) + } + }() + if err != nil { + return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode) + } + return res, errors.Wrap(defRes, "error") +} + func decodeActionSubmissionUploadedResponse(resp *http.Response) (res *ActionSubmissionUploadedNoContent, _ error) { switch resp.StatusCode { case 204: diff --git a/pkg/internal/oas_response_encoders_gen.go b/pkg/internal/oas_response_encoders_gen.go index 03bd236..38bfb96 100644 --- a/pkg/internal/oas_response_encoders_gen.go +++ b/pkg/internal/oas_response_encoders_gen.go @@ -20,6 +20,13 @@ func encodeActionMapfixAcceptedResponse(response *ActionMapfixAcceptedNoContent, return nil } +func encodeActionMapfixSubmittedResponse(response *ActionMapfixSubmittedNoContent, w http.ResponseWriter, span trace.Span) error { + w.WriteHeader(204) + span.SetStatus(codes.Ok, http.StatusText(204)) + + return nil +} + func encodeActionMapfixUploadedResponse(response *ActionMapfixUploadedNoContent, w http.ResponseWriter, span trace.Span) error { w.WriteHeader(204) span.SetStatus(codes.Ok, http.StatusText(204)) @@ -48,6 +55,13 @@ func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNo return nil } +func encodeActionSubmissionSubmittedResponse(response *ActionSubmissionSubmittedNoContent, w http.ResponseWriter, span trace.Span) error { + w.WriteHeader(204) + span.SetStatus(codes.Ok, http.StatusText(204)) + + return nil +} + func encodeActionSubmissionUploadedResponse(response *ActionSubmissionUploadedNoContent, w http.ResponseWriter, span trace.Span) error { w.WriteHeader(204) span.SetStatus(codes.Ok, http.StatusText(204)) diff --git a/pkg/internal/oas_router_gen.go b/pkg/internal/oas_router_gen.go index ecc609b..3e9fba8 100644 --- a/pkg/internal/oas_router_gen.go +++ b/pkg/internal/oas_router_gen.go @@ -147,6 +147,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + case 's': // Prefix: "submitted" + + if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixSubmittedRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + case 'u': // Prefix: "uploaded" if l := len("uploaded"); len(elem) >= l && elem[0:l] == "uploaded" { @@ -454,6 +476,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + case 's': // Prefix: "submitted" + + if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionSubmissionSubmittedRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + case 'u': // Prefix: "uploaded" if l := len("uploaded"); len(elem) >= l && elem[0:l] == "uploaded" { @@ -716,6 +760,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { } } + case 's': // Prefix: "submitted" + + if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixSubmittedOperation + r.summary = "(Internal endpoint) Role Validator changes status from Submitting -> Submitted" + r.operationID = "actionMapfixSubmitted" + r.pathPattern = "/mapfixes/{MapfixID}/status/validator-submitted" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + case 'u': // Prefix: "uploaded" if l := len("uploaded"); len(elem) >= l && elem[0:l] == "uploaded" { @@ -1059,6 +1127,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { } } + case 's': // Prefix: "submitted" + + if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionSubmissionSubmittedOperation + r.summary = "(Internal endpoint) Role Validator changes status from Submitting -> Submitted" + r.operationID = "actionSubmissionSubmitted" + r.pathPattern = "/submissions/{SubmissionID}/status/validator-submitted" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + case 'u': // Prefix: "uploaded" if l := len("uploaded"); len(elem) >= l && elem[0:l] == "uploaded" { diff --git a/pkg/internal/oas_schemas_gen.go b/pkg/internal/oas_schemas_gen.go index 1fecc20..e68e85a 100644 --- a/pkg/internal/oas_schemas_gen.go +++ b/pkg/internal/oas_schemas_gen.go @@ -13,6 +13,9 @@ func (s *ErrorStatusCode) Error() string { // ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation. type ActionMapfixAcceptedNoContent struct{} +// ActionMapfixSubmittedNoContent is response for ActionMapfixSubmitted operation. +type ActionMapfixSubmittedNoContent struct{} + // ActionMapfixUploadedNoContent is response for ActionMapfixUploaded operation. type ActionMapfixUploadedNoContent struct{} @@ -25,6 +28,9 @@ type ActionOperationFailedNoContent struct{} // ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation. type ActionSubmissionAcceptedNoContent struct{} +// ActionSubmissionSubmittedNoContent is response for ActionSubmissionSubmitted operation. +type ActionSubmissionSubmittedNoContent struct{} + // ActionSubmissionUploadedNoContent is response for ActionSubmissionUploaded operation. type ActionSubmissionUploadedNoContent struct{} diff --git a/pkg/internal/oas_server_gen.go b/pkg/internal/oas_server_gen.go index 4fd886c..94c8192 100644 --- a/pkg/internal/oas_server_gen.go +++ b/pkg/internal/oas_server_gen.go @@ -14,6 +14,12 @@ type Handler interface { // // POST /mapfixes/{MapfixID}/status/validator-failed ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error + // ActionMapfixSubmitted implements actionMapfixSubmitted operation. + // + // (Internal endpoint) Role Validator changes status from Submitting -> Submitted. + // + // POST /mapfixes/{MapfixID}/status/validator-submitted + ActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) error // ActionMapfixUploaded implements actionMapfixUploaded operation. // // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. @@ -38,6 +44,12 @@ type Handler interface { // // POST /submissions/{SubmissionID}/status/validator-failed ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error + // ActionSubmissionSubmitted implements actionSubmissionSubmitted operation. + // + // (Internal endpoint) Role Validator changes status from Submitting -> Submitted. + // + // POST /submissions/{SubmissionID}/status/validator-submitted + ActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) error // ActionSubmissionUploaded implements actionSubmissionUploaded operation. // // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. diff --git a/pkg/internal/oas_unimplemented_gen.go b/pkg/internal/oas_unimplemented_gen.go index e231989..7baa6e9 100644 --- a/pkg/internal/oas_unimplemented_gen.go +++ b/pkg/internal/oas_unimplemented_gen.go @@ -22,6 +22,15 @@ func (UnimplementedHandler) ActionMapfixAccepted(ctx context.Context, params Act return ht.ErrNotImplemented } +// ActionMapfixSubmitted implements actionMapfixSubmitted operation. +// +// (Internal endpoint) Role Validator changes status from Submitting -> Submitted. +// +// POST /mapfixes/{MapfixID}/status/validator-submitted +func (UnimplementedHandler) ActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) error { + return ht.ErrNotImplemented +} + // ActionMapfixUploaded implements actionMapfixUploaded operation. // // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. @@ -58,6 +67,15 @@ func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params return ht.ErrNotImplemented } +// ActionSubmissionSubmitted implements actionSubmissionSubmitted operation. +// +// (Internal endpoint) Role Validator changes status from Submitting -> Submitted. +// +// POST /submissions/{SubmissionID}/status/validator-submitted +func (UnimplementedHandler) ActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) error { + return ht.ErrNotImplemented +} + // ActionSubmissionUploaded implements actionSubmissionUploaded operation. // // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. diff --git a/pkg/service/mapfixes.go b/pkg/service/mapfixes.go index 2b17ea0..b9ff7d7 100644 --- a/pkg/service/mapfixes.go +++ b/pkg/service/mapfixes.go @@ -461,12 +461,12 @@ func (svc *Service) ActionMapfixRevoke(ctx context.Context, params api.ActionMap return nil } -// ActionMapfixSubmit invokes actionMapfixSubmit operation. +// ActionMapfixTriggerSubmit invokes actionMapfixTriggerSubmit operation. // -// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. +// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting. // -// POST /mapfixes/{MapfixID}/status/submit -func (svc *Service) ActionMapfixSubmit(ctx context.Context, params api.ActionMapfixSubmitParams) error { +// POST /mapfixes/{MapfixID}/status/trigger-submit +func (svc *Service) ActionMapfixTriggerSubmit(ctx context.Context, params api.ActionMapfixTriggerSubmitParams) error { userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) if !ok { return ErrUserInfo @@ -522,6 +522,72 @@ func (svc *Service) ActionMapfixSubmit(ctx context.Context, params api.ActionMap return nil } +// ActionMapfixResetSubmitting implements actionMapfixResetSubmitting operation. +// +// Role MapfixReview changes status from Submitting -> UnderConstruction. +// +// POST /mapfixes/{MapfixID}/status/reset-submitting +func (svc *Service) ActionMapfixResetSubmitting(ctx context.Context, params api.ActionMapfixResetSubmittingParams) error { + userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) + if !ok { + return ErrUserInfo + } + + userId, err := userInfo.GetUserID() + if err != nil { + return err + } + + // check when mapfix was updated + mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID) + if err != nil { + return err + } + if time.Now().Before(mapfix.UpdatedAt.Add(time.Second*10)) { + // the last time the mapfix was updated must be longer than 10 seconds ago + return ErrDelayReset + } + + // check if caller has required role + has_role := userId == mapfix.Submitter + if !has_role { + return ErrPermissionDeniedNotSubmitter + } + + // transaction + target_status := model.MapfixStatusUnderConstruction + smap := datastore.Optional() + smap.Add("status_id", target_status) + smap.Add("status_message", "Manually forced reset") + err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap) + if err != nil { + return err + } + + event_data := model.AuditEventDataAction{ + TargetStatus: uint32(target_status), + } + + EventData, err := json.Marshal(event_data) + if err != nil { + return err + } + + _, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{ + ID: 0, + User: userId, + ResourceType: model.ResourceMapfix, + ResourceID: params.MapfixID, + EventType: model.AuditEventTypeAction, + EventData: EventData, + }) + if err != nil { + return err + } + + return nil +} + // ActionMapfixTriggerUpload invokes actionMapfixTriggerUpload operation. // // Role Admin changes status from Validated -> Uploading. diff --git a/pkg/service/submissions.go b/pkg/service/submissions.go index e15003d..c81c24a 100644 --- a/pkg/service/submissions.go +++ b/pkg/service/submissions.go @@ -482,12 +482,12 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio return nil } -// ActionSubmissionSubmit invokes actionSubmissionSubmit operation. +// ActionSubmissionTriggerSubmit invokes actionSubmissionTriggerSubmit operation. // -// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. +// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting. // -// POST /submissions/{SubmissionID}/status/submit -func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.ActionSubmissionSubmitParams) error { +// POST /submissions/{SubmissionID}/status/trigger-submit +func (svc *Service) ActionSubmissionTriggerSubmit(ctx context.Context, params api.ActionSubmissionTriggerSubmitParams) error { userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) if !ok { return ErrUserInfo @@ -543,6 +543,72 @@ func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.Actio return nil } +// ActionSubmissionResetSubmitting implements actionSubmissionResetSubmitting operation. +// +// Role SubmissionReview changes status from Submitting -> UnderConstruction. +// +// POST /submissions/{SubmissionID}/status/reset-submitting +func (svc *Service) ActionSubmissionResetSubmitting(ctx context.Context, params api.ActionSubmissionResetSubmittingParams) error { + userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) + if !ok { + return ErrUserInfo + } + + userId, err := userInfo.GetUserID() + if err != nil { + return err + } + + // check when submission was updated + submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID) + if err != nil { + return err + } + if time.Now().Before(submission.UpdatedAt.Add(time.Second*10)) { + // the last time the submission was updated must be longer than 10 seconds ago + return ErrDelayReset + } + + // check if caller has required role + has_role := userId == submission.Submitter + if !has_role { + return ErrPermissionDeniedNotSubmitter + } + + // transaction + target_status := model.SubmissionStatusUnderConstruction + smap := datastore.Optional() + smap.Add("status_id", target_status) + smap.Add("status_message", "Manually forced reset") + err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap) + if err != nil { + return err + } + + event_data := model.AuditEventDataAction{ + TargetStatus: uint32(target_status), + } + + EventData, err := json.Marshal(event_data) + if err != nil { + return err + } + + _, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{ + ID: 0, + User: userId, + ResourceType: model.ResourceSubmission, + ResourceID: params.SubmissionID, + EventType: model.AuditEventTypeAction, + EventData: EventData, + }) + if err != nil { + return err + } + + return nil +} + // ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation. // // Role Admin changes status from Validated -> Uploading. diff --git a/pkg/service_internal/mapfixes.go b/pkg/service_internal/mapfixes.go index 5526b20..0827424 100644 --- a/pkg/service_internal/mapfixes.go +++ b/pkg/service_internal/mapfixes.go @@ -74,6 +74,45 @@ func (svc *Service) UpdateMapfixValidatedModel(ctx context.Context, params inter return nil } +// ActionMapfixValidate invokes actionMapfixValidate operation. +// +// Role Validator changes status from Submitting -> Submitted. +// +// POST /mapfixes/{MapfixID}/status/validator-submitted +func (svc *Service) ActionMapfixSubmitted(ctx context.Context, params internal.ActionMapfixSubmittedParams) error { + // transaction + target_status := model.MapfixStatusSubmitted + smap := datastore.Optional() + smap.Add("status_id", target_status) + err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap) + if err != nil { + return err + } + + event_data := model.AuditEventDataAction{ + TargetStatus: uint32(target_status), + } + + EventData, err := json.Marshal(event_data) + if err != nil { + return err + } + + _, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{ + ID: 0, + User: ValidtorUserID, + ResourceType: model.ResourceMapfix, + ResourceID: params.MapfixID, + EventType: model.AuditEventTypeAction, + EventData: EventData, + }) + if err != nil { + return err + } + + return nil +} + // ActionMapfixValidate invokes actionMapfixValidate operation. // // Role Validator changes status from Validating -> Validated. diff --git a/pkg/service_internal/submissions.go b/pkg/service_internal/submissions.go index 164eb60..3250ca7 100644 --- a/pkg/service_internal/submissions.go +++ b/pkg/service_internal/submissions.go @@ -73,6 +73,45 @@ func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params i return nil } +// ActionSubmissionValidate invokes actionSubmissionValidate operation. +// +// Role Validator changes status from Submitting -> Submitted. +// +// POST /submissions/{SubmissionID}/status/validator-submitted +func (svc *Service) ActionSubmissionSubmitted(ctx context.Context, params internal.ActionSubmissionSubmittedParams) error { + // transaction + target_status := model.SubmissionStatusSubmitted + smap := datastore.Optional() + smap.Add("status_id", target_status) + err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap) + if err != nil { + return err + } + + event_data := model.AuditEventDataAction{ + TargetStatus: uint32(target_status), + } + + EventData, err := json.Marshal(event_data) + if err != nil { + return err + } + + _, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{ + ID: 0, + User: ValidtorUserID, + ResourceType: model.ResourceSubmission, + ResourceID: params.SubmissionID, + EventType: model.AuditEventTypeAction, + EventData: EventData, + }) + if err != nil { + return err + } + + return nil +} + // ActionSubmissionValidate invokes actionSubmissionValidate operation. // // Role Validator changes status from Validating -> Validated. diff --git a/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx b/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx index 008fc11..c9138c0 100644 --- a/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx +++ b/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx @@ -3,13 +3,25 @@ import { MapfixStatus } from "@/app/ts/Mapfix"; import { Button, ButtonOwnProps } from "@mui/material"; import { useState, useEffect } from "react"; -type Actions = "Completed" | "Submit" | "Reject" | "Revoke" -type ApiActions = Lowercase | "request-changes" | "trigger-validate" | "retry-validate" | "trigger-upload" | "reset-uploading" | "reset-validating" -type Review = Actions | "Request Changes" | "Accept" | "Validate" | "Upload" | "Reset Uploading (fix softlocked status)" | "Reset Validating (fix softlocked status)" | "Request Changes" +interface ReviewAction { + name: string, + action: string, +} + +const ReviewActions = { + Submit: {name:"Submit",action:"trigger-submit"} as ReviewAction, + Revoke: {name:"Revoke",action:"revoke"} as ReviewAction, + Accept: {name:"Accept",action:"trigger-validate"} as ReviewAction, + Reject: {name:"Reject",action:"reject"} as ReviewAction, + Validate: {name:"Validate",action:"retry-validate"} as ReviewAction, + ResetValidating: {name:"Reset Validating (fix softlocked status)",action:"reset-validating"} as ReviewAction, + RequestChanges: {name:"Request Changes",action:"request-changes"} as ReviewAction, + Upload: {name:"Upload",action:"trigger-upload"} as ReviewAction, + ResetUploading: {name:"Reset Uploading (fix softlocked status)",action:"reset-uploading"} as ReviewAction, +} interface ReviewButton { - name: Review, - action: ApiActions, + action: ReviewAction, mapfixId: string, color: ButtonOwnProps["color"] } @@ -20,7 +32,7 @@ interface ReviewId { mapfixSubmitter: number, } -async function ReviewButtonClicked(action: ApiActions, mapfixId: string) { +async function ReviewButtonClicked(action: string, mapfixId: string) { try { const response = await fetch(`/api/mapfixes/${mapfixId}/status/${action}`, { method: "POST", @@ -46,7 +58,7 @@ function ReviewButton(props: ReviewButton) { return + onClick={() => { ReviewButtonClicked(props.action.action, props.mapfixId) }}>{props.action.name} } export default function ReviewButtons(props: ReviewId) { @@ -95,10 +107,10 @@ export default function ReviewButtons(props: ReviewId) { const is_submitter = user === props.mapfixSubmitter; if (is_submitter) { if ([MapfixStatus.UnderConstruction, MapfixStatus.ChangesRequested].includes(mapfixStatus!)) { - visibleButtons.push({ name: "Submit", action: "submit", color: "info", mapfixId }); + visibleButtons.push({ action: ReviewActions.Submit, color: "info", mapfixId }); } if ([MapfixStatus.Submitted, MapfixStatus.ChangesRequested].includes(mapfixStatus!)) { - visibleButtons.push({ name: "Revoke", action: "revoke", color: "info", mapfixId }); + visibleButtons.push({ action: ReviewActions.Revoke, color: "info", mapfixId }); } } @@ -106,14 +118,14 @@ export default function ReviewButtons(props: ReviewId) { // you can't review your own mapfix! // note that this means there needs to be more than one person with MapfixReview if (!is_submitter && mapfixStatus === MapfixStatus.Submitted) { - visibleButtons.push({ name: "Accept", action: "trigger-validate", color: "info", mapfixId }); - visibleButtons.push({ name: "Reject", action: "reject", color: "error", mapfixId }); + visibleButtons.push({ action: ReviewActions.Accept, color: "info", mapfixId }); + visibleButtons.push({ action: ReviewActions.Reject, color: "error", mapfixId }); } if (mapfixStatus === MapfixStatus.AcceptedUnvalidated) { - visibleButtons.push({ name: "Validate", action: "retry-validate", color: "info", mapfixId }); + visibleButtons.push({ action: ReviewActions.Validate, color: "info", mapfixId }); } if (mapfixStatus === MapfixStatus.Validating) { - visibleButtons.push({ name: "Reset Validating (fix softlocked status)", action: "reset-validating", color: "error", mapfixId }); + visibleButtons.push({ action: ReviewActions.ResetValidating, color: "error", mapfixId }); } // this button serves the same purpose as Revoke if you are both // the map submitter and have MapfixReview when status is Submitted @@ -121,16 +133,16 @@ export default function ReviewButtons(props: ReviewId) { [MapfixStatus.Validated, MapfixStatus.AcceptedUnvalidated].includes(mapfixStatus!) || !is_submitter && mapfixStatus == MapfixStatus.Submitted ) { - visibleButtons.push({ name: "Request Changes", action: "request-changes", color: "error", mapfixId }); + visibleButtons.push({ action: ReviewActions.RequestChanges, color: "error", mapfixId }); } } if (roles&RolesConstants.MapfixUpload) { if (mapfixStatus === MapfixStatus.Validated) { - visibleButtons.push({ name: "Upload", action: "trigger-upload", color: "info", mapfixId }); + visibleButtons.push({ action: ReviewActions.Upload, color: "info", mapfixId }); } if (mapfixStatus === MapfixStatus.Uploading) { - visibleButtons.push({ name: "Reset Uploading (fix softlocked status)", action: "reset-uploading", color: "error", mapfixId }); + visibleButtons.push({ action: ReviewActions.ResetUploading, color: "error", mapfixId }); } } @@ -140,7 +152,7 @@ export default function ReviewButtons(props: ReviewId) {

No available actions

) : ( visibleButtons.map((btn) => ( - + )) )} diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index 345d435..aa68897 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -3,13 +3,25 @@ import { SubmissionStatus } from "@/app/ts/Submission"; import { Button, ButtonOwnProps } from "@mui/material"; import { useState, useEffect } from "react"; -type Actions = "Completed" | "Submit" | "Reject" | "Revoke" -type ApiActions = Lowercase | "request-changes" | "trigger-validate" | "retry-validate" | "trigger-upload" | "reset-uploading" | "reset-validating" -type Review = Actions | "Request Changes" | "Accept" | "Validate" | "Upload" | "Reset Uploading (fix softlocked status)" | "Reset Validating (fix softlocked status)" | "Request Changes" +interface ReviewAction { + name: string, + action: string, +} + +const ReviewActions = { + Submit: {name:"Submit",action:"trigger-submit"} as ReviewAction, + Revoke: {name:"Revoke",action:"revoke"} as ReviewAction, + Accept: {name:"Accept",action:"trigger-validate"} as ReviewAction, + Reject: {name:"Reject",action:"reject"} as ReviewAction, + Validate: {name:"Validate",action:"retry-validate"} as ReviewAction, + ResetValidating: {name:"Reset Validating (fix softlocked status)",action:"reset-validating"} as ReviewAction, + RequestChanges: {name:"Request Changes",action:"request-changes"} as ReviewAction, + Upload: {name:"Upload",action:"trigger-upload"} as ReviewAction, + ResetUploading: {name:"Reset Uploading (fix softlocked status)",action:"reset-uploading"} as ReviewAction, +} interface ReviewButton { - name: Review, - action: ApiActions, + action: ReviewAction, submissionId: string, color: ButtonOwnProps["color"] } @@ -20,7 +32,7 @@ interface ReviewId { submissionSubmitter: number, } -async function ReviewButtonClicked(action: ApiActions, submissionId: string) { +async function ReviewButtonClicked(action: string, submissionId: string) { try { const response = await fetch(`/api/submissions/${submissionId}/status/${action}`, { method: "POST", @@ -46,7 +58,7 @@ function ReviewButton(props: ReviewButton) { return + onClick={() => { ReviewButtonClicked(props.action.action, props.submissionId) }}>{props.action.name} } export default function ReviewButtons(props: ReviewId) { @@ -95,10 +107,10 @@ export default function ReviewButtons(props: ReviewId) { const is_submitter = user === props.submissionSubmitter; if (is_submitter) { if ([SubmissionStatus.UnderConstruction, SubmissionStatus.ChangesRequested].includes(submissionStatus!)) { - visibleButtons.push({ name: "Submit", action: "submit", color: "info", submissionId }); + visibleButtons.push({ action: ReviewActions.Submit, color: "info", submissionId }); } if ([SubmissionStatus.Submitted, SubmissionStatus.ChangesRequested].includes(submissionStatus!)) { - visibleButtons.push({ name: "Revoke", action: "revoke", color: "info", submissionId }); + visibleButtons.push({ action: ReviewActions.Revoke, color: "info", submissionId }); } } @@ -106,14 +118,14 @@ export default function ReviewButtons(props: ReviewId) { // you can't review your own submission! // note that this means there needs to be more than one person with SubmissionReview if (!is_submitter && submissionStatus === SubmissionStatus.Submitted) { - visibleButtons.push({ name: "Accept", action: "trigger-validate", color: "info", submissionId }); - visibleButtons.push({ name: "Reject", action: "reject", color: "error", submissionId }); + visibleButtons.push({ action: ReviewActions.Accept, color: "info", submissionId }); + visibleButtons.push({ action: ReviewActions.Reject, color: "error", submissionId }); } if (submissionStatus === SubmissionStatus.AcceptedUnvalidated) { - visibleButtons.push({ name: "Validate", action: "retry-validate", color: "info", submissionId }); + visibleButtons.push({ action: ReviewActions.Validate, color: "info", submissionId }); } if (submissionStatus === SubmissionStatus.Validating) { - visibleButtons.push({ name: "Reset Validating (fix softlocked status)", action: "reset-validating", color: "error", submissionId }); + visibleButtons.push({ action: ReviewActions.ResetValidating, color: "error", submissionId }); } // this button serves the same purpose as Revoke if you are both // the map submitter and have SubmissionReview when status is Submitted @@ -121,17 +133,17 @@ export default function ReviewButtons(props: ReviewId) { [SubmissionStatus.Validated, SubmissionStatus.AcceptedUnvalidated].includes(submissionStatus!) || !is_submitter && submissionStatus == SubmissionStatus.Submitted ) { - visibleButtons.push({ name: "Request Changes", action: "request-changes", color: "error", submissionId }); + visibleButtons.push({ action: ReviewActions.RequestChanges, color: "error", submissionId }); } } if (roles&RolesConstants.SubmissionUpload) { if (submissionStatus === SubmissionStatus.Validated) { - visibleButtons.push({ name: "Upload", action: "trigger-upload", color: "info", submissionId }); + visibleButtons.push({ action: ReviewActions.Upload, color: "info", submissionId }); } // TODO: hide Reset buttons for 10 seconds if (submissionStatus === SubmissionStatus.Uploading) { - visibleButtons.push({ name: "Reset Uploading (fix softlocked status)", action: "reset-uploading", color: "error", submissionId }); + visibleButtons.push({ action: ReviewActions.ResetUploading, color: "error", submissionId }); } } @@ -141,7 +153,7 @@ export default function ReviewButtons(props: ReviewId) {

No available actions

) : ( visibleButtons.map((btn) => ( - + )) )}