15 Commits

Author SHA1 Message Date
c77355cea3 web: implement roles fetch on maps page 2025-06-13 21:57:03 -07:00
23c78902df web: chat button 2025-06-13 21:44:59 -07:00
dbc2096c22 web: mock button 2025-06-13 21:44:59 -07:00
781ab32e54 submissions: asset location endpoint 2025-06-13 21:28:31 -07:00
5120af820f submissions: add roblox api client 2025-06-13 21:20:08 -07:00
cf7d11c80a openapi: generate 2025-06-13 21:20:08 -07:00
cf39ac5b61 openapi: asset location endpoint 2025-06-13 21:19:44 -07:00
40c2ccad15 submissions: asset location api 2025-06-13 21:08:39 -07:00
ed7109270f Audit Event CheckList ()
Depends on , , .

Closes .

This introduces a new type of audit event: the CheckList.  This is a list of map checks that the validator performed.  The intention is to update the web interface to display  check marks for every check passed and  for every check failed, and also include the summary of why the check failed.  ~~The `Details` field would be the complete internal structure of the check in json, but I'm thinking it's unnecessary and should just be omitted.~~ The `Details` field has been removed.

```go
type Check struct {
	Name    string `json:"name"`
	Summary string `json:"summary"`
	Passed  bool   `json:"passed"`
}

type AuditEventDataCheckList struct {
	CheckList []Check `json:"check_list"`
}
```

This is created instead of the Error audit event when the validator requests changes, but the Error audit event can still be created for other purposes.

- [x] Make a proper error instead of hijacking a CheckList

Reviewed-on: 
Reviewed-by: itzaname <itzaname@noreply@itzana.me>
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-14 02:33:19 +00:00
abd233ce65 Validation: Make Assets Loadable on Maptest ()
Closes .

This is a very bare bones implementation, but gets us started on https://git.itzana.me/StrafesNET/maps-service/milestone/3

This will break production as written!  A proper implementation requires a separate api key since the maptest places are to be hosted on a different group.

Edit: It will actually not break, because it is using cookie access. The staging cookie has permission to edit StrafesNET Maptest asset permissions via StrafesNET_CI3, while prod also has access via StrafesNET_CI2.  Both staging and prod versions of the website will add maptest asset access to the same places on StrafesNET Maptest.
Reviewed-on: 
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-13 03:58:01 +00:00
215c39000b Replace bypass-submit with trigger-submit-unchecked ()
Bypassing the submit process means that the map revision is not updated.  Change the endpoint and include a flag to skip the map checks but update the revision.

Reviewed-on: 
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-13 00:15:16 +00:00
c4d97b6537 Change Error to Explicit Endpoint ()
This changes the way that the internal api works.  The backend used to implicitly create an error for specifc endpoints, but now the validator explicitly creates the error itself.

Reviewed-on: 
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-12 00:55:09 +00:00
0834400c05 Compartmentalize Monolith ()
This isn't the full job, notably Operations are still sprinkled about, and having some code sharing between `service` and `service_internal` would be nice, but that is sketchy without the explicitness of Rust's traits.

Reviewed-on: 
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-12 00:19:56 +00:00
463d14d2b5 submissions-api: type all ids ()
Reviewed-on: 
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-11 05:11:14 +00:00
6a52166901 submissions-api: Add Releaser Endpoints ()
Also uses enums over ints for GameID.

Reviewed-on: 
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-11 04:11:21 +00:00
64 changed files with 5982 additions and 2676 deletions

7
Cargo.lock generated

@ -1379,9 +1379,9 @@ dependencies = [
[[package]]
name = "rbx_asset"
version = "0.4.5"
version = "0.4.6"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "b448bf22f70748215c2a937158f83790bf3f4df81e2af8521a089bc821155360"
checksum = "860909d8375a54deb2a50187b1b792dcf88c0d2e21c18f0c1d44b34e2f027f36"
dependencies = [
"bytes",
"chrono",
@ -1903,8 +1903,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "submissions-api"
version = "0.7.2"
version = "0.8.1"
dependencies = [
"chrono",
"reqwest",
"serde",
"serde_json",

@ -65,6 +65,53 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/error:
post:
summary: Validator posts an error to the audit log
operationId: createMapfixAuditError
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: ErrorMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/checklist:
post:
summary: Validator posts a checklist to the audit log
operationId: createMapfixAuditCheckList
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CheckList'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-submitted:
post:
summary: (Internal endpoint) Role Validator changes status from Submitting -> Submitted
@ -116,13 +163,6 @@ paths:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: ErrorMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
@ -157,13 +197,6 @@ paths:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: ErrorMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
@ -270,6 +303,53 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/error:
post:
summary: Validator posts an error to the audit log
operationId: createSubmissionAuditError
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: ErrorMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/checklist:
post:
summary: Validator posts a checklist to the audit log
operationId: createSubmissionAuditCheckList
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CheckList'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/validator-submitted:
post:
summary: (Internal endpoint) Role Validator changes status from Submitting -> Submitted
@ -321,13 +401,6 @@ paths:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: ErrorMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
@ -362,13 +435,6 @@ paths:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: ErrorMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
@ -847,6 +913,25 @@ components:
type: integer
format: int32
minimum: 0
Check:
required:
- Name
- Summary
- Passed
type: object
properties:
Name:
type: string
maxLength: 128
Summary:
type: string
maxLength: 4096
Passed:
type: boolean
CheckList:
type: array
items:
$ref: "#/components/schemas/Check"
Error:
description: Represents error object
type: object

@ -6,6 +6,8 @@ info:
servers:
- url: https://submissions.strafes.net/v1
tags:
- name: Assets
description: Asset operations
- name: Mapfixes
description: Mapfix operations
- name: Maps
@ -80,6 +82,34 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/asset/{AssetID}/location:
get:
summary: Get location of asset
operationId: assetLocation
tags:
- Assets
parameters:
- name: AssetID
in: path
required: true
schema:
type: integer
format: int64
minimum: 0
responses:
"200":
description: Successful response
content:
text/plain:
schema:
type: string
maxLength: 1024
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/maps:
get:
summary: Get list of maps
@ -421,10 +451,10 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/bypass-submit:
/mapfixes/{MapfixID}/status/trigger-submit-unchecked:
post:
summary: Role Reviewer changes status from ChangesRequested -> Submitted
operationId: actionMapfixBypassSubmit
summary: Role Reviewer changes status from ChangesRequested -> Submitting
operationId: actionMapfixTriggerSubmitUnchecked
tags:
- Mapfixes
parameters:
@ -901,10 +931,10 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/bypass-submit:
/submissions/{SubmissionID}/status/trigger-submit-unchecked:
post:
summary: Role Reviewer changes status from ChangesRequested -> Submitted
operationId: actionSubmissionBypassSubmit
summary: Role Reviewer changes status from ChangesRequested -> Submitting
operationId: actionSubmissionTriggerSubmitUnchecked
tags:
- Submissions
parameters:

@ -35,12 +35,6 @@ type Invoker interface {
//
// POST /mapfixes/{MapfixID}/status/reset-validating
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixBypassSubmit invokes actionMapfixBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/bypass-submit
ActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) error
// ActionMapfixReject invokes actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@ -78,6 +72,12 @@ type Invoker interface {
//
// POST /mapfixes/{MapfixID}/status/trigger-submit
ActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) error
// ActionMapfixTriggerSubmitUnchecked invokes actionMapfixTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/trigger-submit-unchecked
ActionMapfixTriggerSubmitUnchecked(ctx context.Context, params ActionMapfixTriggerSubmitUncheckedParams) error
// ActionMapfixTriggerUpload invokes actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@ -102,12 +102,6 @@ type Invoker interface {
//
// POST /submissions/{SubmissionID}/status/reset-validating
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionBypassSubmit invokes actionSubmissionBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/bypass-submit
ActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) error
// ActionSubmissionReject invokes actionSubmissionReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@ -145,6 +139,12 @@ type Invoker interface {
//
// POST /submissions/{SubmissionID}/status/trigger-submit
ActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) error
// ActionSubmissionTriggerSubmitUnchecked invokes actionSubmissionTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/trigger-submit-unchecked
ActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params ActionSubmissionTriggerSubmitUncheckedParams) error
// ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@ -163,6 +163,12 @@ type Invoker interface {
//
// POST /submissions/{SubmissionID}/status/reset-uploading
ActionSubmissionValidated(ctx context.Context, params ActionSubmissionValidatedParams) error
// AssetLocation invokes assetLocation operation.
//
// Get location of asset.
//
// GET /asset/{AssetID}/location
AssetLocation(ctx context.Context, params AssetLocationParams) (AssetLocationOK, error)
// CreateMapfix invokes createMapfix operation.
//
// Trigger the validator to create a mapfix.
@ -530,130 +536,6 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf
return result, nil
}
// ActionMapfixBypassSubmit invokes actionMapfixBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/bypass-submit
func (c *Client) ActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) error {
_, err := c.sendActionMapfixBypassSubmit(ctx, params)
return err
}
func (c *Client) sendActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) (res *ActionMapfixBypassSubmitNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixBypassSubmit"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/bypass-submit"),
}
// 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, ActionMapfixBypassSubmitOperation,
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/bypass-submit"
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, ActionMapfixBypassSubmitOperation, 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 := decodeActionMapfixBypassSubmitResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionMapfixReject invokes actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@ -1399,6 +1281,130 @@ func (c *Client) sendActionMapfixTriggerSubmit(ctx context.Context, params Actio
return result, nil
}
// ActionMapfixTriggerSubmitUnchecked invokes actionMapfixTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/trigger-submit-unchecked
func (c *Client) ActionMapfixTriggerSubmitUnchecked(ctx context.Context, params ActionMapfixTriggerSubmitUncheckedParams) error {
_, err := c.sendActionMapfixTriggerSubmitUnchecked(ctx, params)
return err
}
func (c *Client) sendActionMapfixTriggerSubmitUnchecked(ctx context.Context, params ActionMapfixTriggerSubmitUncheckedParams) (res *ActionMapfixTriggerSubmitUncheckedNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixTriggerSubmitUnchecked"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/trigger-submit-unchecked"),
}
// 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, ActionMapfixTriggerSubmitUncheckedOperation,
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/trigger-submit-unchecked"
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, ActionMapfixTriggerSubmitUncheckedOperation, 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 := decodeActionMapfixTriggerSubmitUncheckedResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionMapfixTriggerUpload invokes actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@ -1895,130 +1901,6 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
return result, nil
}
// ActionSubmissionBypassSubmit invokes actionSubmissionBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/bypass-submit
func (c *Client) ActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) error {
_, err := c.sendActionSubmissionBypassSubmit(ctx, params)
return err
}
func (c *Client) sendActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) (res *ActionSubmissionBypassSubmitNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionSubmissionBypassSubmit"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/bypass-submit"),
}
// 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, ActionSubmissionBypassSubmitOperation,
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/bypass-submit"
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, ActionSubmissionBypassSubmitOperation, 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 := decodeActionSubmissionBypassSubmitResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionSubmissionReject invokes actionSubmissionReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@ -2764,6 +2646,130 @@ func (c *Client) sendActionSubmissionTriggerSubmit(ctx context.Context, params A
return result, nil
}
// ActionSubmissionTriggerSubmitUnchecked invokes actionSubmissionTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/trigger-submit-unchecked
func (c *Client) ActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params ActionSubmissionTriggerSubmitUncheckedParams) error {
_, err := c.sendActionSubmissionTriggerSubmitUnchecked(ctx, params)
return err
}
func (c *Client) sendActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params ActionSubmissionTriggerSubmitUncheckedParams) (res *ActionSubmissionTriggerSubmitUncheckedNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionSubmissionTriggerSubmitUnchecked"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/trigger-submit-unchecked"),
}
// 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, ActionSubmissionTriggerSubmitUncheckedOperation,
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/trigger-submit-unchecked"
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, ActionSubmissionTriggerSubmitUncheckedOperation, 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 := decodeActionSubmissionTriggerSubmitUncheckedResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@ -3136,6 +3142,130 @@ func (c *Client) sendActionSubmissionValidated(ctx context.Context, params Actio
return result, nil
}
// AssetLocation invokes assetLocation operation.
//
// Get location of asset.
//
// GET /asset/{AssetID}/location
func (c *Client) AssetLocation(ctx context.Context, params AssetLocationParams) (AssetLocationOK, error) {
res, err := c.sendAssetLocation(ctx, params)
return res, err
}
func (c *Client) sendAssetLocation(ctx context.Context, params AssetLocationParams) (res AssetLocationOK, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("assetLocation"),
semconv.HTTPRequestMethodKey.String("GET"),
semconv.HTTPRouteKey.String("/asset/{AssetID}/location"),
}
// 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, AssetLocationOperation,
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] = "/asset/"
{
// Encode "AssetID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "AssetID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.AssetID))
}(); 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] = "/location"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "GET", 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, AssetLocationOperation, 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 := decodeAssetLocationResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// CreateMapfix invokes createMapfix operation.
//
// Trigger the validator to create a mapfix.

File diff suppressed because it is too large Load Diff

@ -6,58 +6,59 @@ package api
type OperationName = string
const (
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
ActionMapfixBypassSubmitOperation OperationName = "ActionMapfixBypassSubmit"
ActionMapfixRejectOperation OperationName = "ActionMapfixReject"
ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges"
ActionMapfixResetSubmittingOperation OperationName = "ActionMapfixResetSubmitting"
ActionMapfixRetryValidateOperation OperationName = "ActionMapfixRetryValidate"
ActionMapfixRevokeOperation OperationName = "ActionMapfixRevoke"
ActionMapfixTriggerSubmitOperation OperationName = "ActionMapfixTriggerSubmit"
ActionMapfixTriggerUploadOperation OperationName = "ActionMapfixTriggerUpload"
ActionMapfixTriggerValidateOperation OperationName = "ActionMapfixTriggerValidate"
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionBypassSubmitOperation OperationName = "ActionSubmissionBypassSubmit"
ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject"
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
ActionSubmissionResetSubmittingOperation OperationName = "ActionSubmissionResetSubmitting"
ActionSubmissionRetryValidateOperation OperationName = "ActionSubmissionRetryValidate"
ActionSubmissionRevokeOperation OperationName = "ActionSubmissionRevoke"
ActionSubmissionTriggerSubmitOperation OperationName = "ActionSubmissionTriggerSubmit"
ActionSubmissionTriggerUploadOperation OperationName = "ActionSubmissionTriggerUpload"
ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateMapfixOperation OperationName = "CreateMapfix"
CreateMapfixAuditCommentOperation OperationName = "CreateMapfixAuditComment"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
CreateSubmissionAdminOperation OperationName = "CreateSubmissionAdmin"
CreateSubmissionAuditCommentOperation OperationName = "CreateSubmissionAuditComment"
DeleteScriptOperation OperationName = "DeleteScript"
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"
GetMapOperation OperationName = "GetMap"
GetMapfixOperation OperationName = "GetMapfix"
GetOperationOperation OperationName = "GetOperation"
GetScriptOperation OperationName = "GetScript"
GetScriptPolicyOperation OperationName = "GetScriptPolicy"
GetSubmissionOperation OperationName = "GetSubmission"
ListMapfixAuditEventsOperation OperationName = "ListMapfixAuditEvents"
ListMapfixesOperation OperationName = "ListMapfixes"
ListMapsOperation OperationName = "ListMaps"
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"
ListSubmissionAuditEventsOperation OperationName = "ListSubmissionAuditEvents"
ListSubmissionsOperation OperationName = "ListSubmissions"
ReleaseSubmissionsOperation OperationName = "ReleaseSubmissions"
SessionRolesOperation OperationName = "SessionRoles"
SessionUserOperation OperationName = "SessionUser"
SessionValidateOperation OperationName = "SessionValidate"
SetMapfixCompletedOperation OperationName = "SetMapfixCompleted"
SetSubmissionCompletedOperation OperationName = "SetSubmissionCompleted"
UpdateMapfixModelOperation OperationName = "UpdateMapfixModel"
UpdateScriptOperation OperationName = "UpdateScript"
UpdateScriptPolicyOperation OperationName = "UpdateScriptPolicy"
UpdateSubmissionModelOperation OperationName = "UpdateSubmissionModel"
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
ActionMapfixRejectOperation OperationName = "ActionMapfixReject"
ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges"
ActionMapfixResetSubmittingOperation OperationName = "ActionMapfixResetSubmitting"
ActionMapfixRetryValidateOperation OperationName = "ActionMapfixRetryValidate"
ActionMapfixRevokeOperation OperationName = "ActionMapfixRevoke"
ActionMapfixTriggerSubmitOperation OperationName = "ActionMapfixTriggerSubmit"
ActionMapfixTriggerSubmitUncheckedOperation OperationName = "ActionMapfixTriggerSubmitUnchecked"
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"
ActionSubmissionTriggerSubmitOperation OperationName = "ActionSubmissionTriggerSubmit"
ActionSubmissionTriggerSubmitUncheckedOperation OperationName = "ActionSubmissionTriggerSubmitUnchecked"
ActionSubmissionTriggerUploadOperation OperationName = "ActionSubmissionTriggerUpload"
ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
AssetLocationOperation OperationName = "AssetLocation"
CreateMapfixOperation OperationName = "CreateMapfix"
CreateMapfixAuditCommentOperation OperationName = "CreateMapfixAuditComment"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
CreateSubmissionAdminOperation OperationName = "CreateSubmissionAdmin"
CreateSubmissionAuditCommentOperation OperationName = "CreateSubmissionAuditComment"
DeleteScriptOperation OperationName = "DeleteScript"
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"
GetMapOperation OperationName = "GetMap"
GetMapfixOperation OperationName = "GetMapfix"
GetOperationOperation OperationName = "GetOperation"
GetScriptOperation OperationName = "GetScript"
GetScriptPolicyOperation OperationName = "GetScriptPolicy"
GetSubmissionOperation OperationName = "GetSubmission"
ListMapfixAuditEventsOperation OperationName = "ListMapfixAuditEvents"
ListMapfixesOperation OperationName = "ListMapfixes"
ListMapsOperation OperationName = "ListMaps"
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"
ListSubmissionAuditEventsOperation OperationName = "ListSubmissionAuditEvents"
ListSubmissionsOperation OperationName = "ListSubmissions"
ReleaseSubmissionsOperation OperationName = "ReleaseSubmissions"
SessionRolesOperation OperationName = "SessionRoles"
SessionUserOperation OperationName = "SessionUser"
SessionValidateOperation OperationName = "SessionValidate"
SetMapfixCompletedOperation OperationName = "SetMapfixCompleted"
SetSubmissionCompletedOperation OperationName = "SetSubmissionCompleted"
UpdateMapfixModelOperation OperationName = "UpdateMapfixModel"
UpdateScriptOperation OperationName = "UpdateScript"
UpdateScriptPolicyOperation OperationName = "UpdateScriptPolicy"
UpdateSubmissionModelOperation OperationName = "UpdateSubmissionModel"
)

@ -98,89 +98,6 @@ func decodeActionMapfixAcceptedParams(args [1]string, argsEscaped bool, r *http.
return params, nil
}
// ActionMapfixBypassSubmitParams is parameters of actionMapfixBypassSubmit operation.
type ActionMapfixBypassSubmitParams struct {
// The unique identifier for a mapfix.
MapfixID int64
}
func unpackActionMapfixBypassSubmitParams(packed middleware.Parameters) (params ActionMapfixBypassSubmitParams) {
{
key := middleware.ParameterKey{
Name: "MapfixID",
In: "path",
}
params.MapfixID = packed[key].(int64)
}
return params
}
func decodeActionMapfixBypassSubmitParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixBypassSubmitParams, _ 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
}
// ActionMapfixRejectParams is parameters of actionMapfixReject operation.
type ActionMapfixRejectParams struct {
// The unique identifier for a mapfix.
@ -679,6 +596,89 @@ func decodeActionMapfixTriggerSubmitParams(args [1]string, argsEscaped bool, r *
return params, nil
}
// ActionMapfixTriggerSubmitUncheckedParams is parameters of actionMapfixTriggerSubmitUnchecked operation.
type ActionMapfixTriggerSubmitUncheckedParams struct {
// The unique identifier for a mapfix.
MapfixID int64
}
func unpackActionMapfixTriggerSubmitUncheckedParams(packed middleware.Parameters) (params ActionMapfixTriggerSubmitUncheckedParams) {
{
key := middleware.ParameterKey{
Name: "MapfixID",
In: "path",
}
params.MapfixID = packed[key].(int64)
}
return params
}
func decodeActionMapfixTriggerSubmitUncheckedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixTriggerSubmitUncheckedParams, _ 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
}
// ActionMapfixTriggerUploadParams is parameters of actionMapfixTriggerUpload operation.
type ActionMapfixTriggerUploadParams struct {
// The unique identifier for a mapfix.
@ -1011,89 +1011,6 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
return params, nil
}
// ActionSubmissionBypassSubmitParams is parameters of actionSubmissionBypassSubmit operation.
type ActionSubmissionBypassSubmitParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
func unpackActionSubmissionBypassSubmitParams(packed middleware.Parameters) (params ActionSubmissionBypassSubmitParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
In: "path",
}
params.SubmissionID = packed[key].(int64)
}
return params
}
func decodeActionSubmissionBypassSubmitParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionBypassSubmitParams, _ 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
}
// ActionSubmissionRejectParams is parameters of actionSubmissionReject operation.
type ActionSubmissionRejectParams struct {
// The unique identifier for a submission.
@ -1592,6 +1509,89 @@ func decodeActionSubmissionTriggerSubmitParams(args [1]string, argsEscaped bool,
return params, nil
}
// ActionSubmissionTriggerSubmitUncheckedParams is parameters of actionSubmissionTriggerSubmitUnchecked operation.
type ActionSubmissionTriggerSubmitUncheckedParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
func unpackActionSubmissionTriggerSubmitUncheckedParams(packed middleware.Parameters) (params ActionSubmissionTriggerSubmitUncheckedParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
In: "path",
}
params.SubmissionID = packed[key].(int64)
}
return params
}
func decodeActionSubmissionTriggerSubmitUncheckedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionTriggerSubmitUncheckedParams, _ 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
}
// ActionSubmissionTriggerUploadParams is parameters of actionSubmissionTriggerUpload operation.
type ActionSubmissionTriggerUploadParams struct {
// The unique identifier for a submission.
@ -1841,6 +1841,88 @@ func decodeActionSubmissionValidatedParams(args [1]string, argsEscaped bool, r *
return params, nil
}
// AssetLocationParams is parameters of assetLocation operation.
type AssetLocationParams struct {
AssetID int64
}
func unpackAssetLocationParams(packed middleware.Parameters) (params AssetLocationParams) {
{
key := middleware.ParameterKey{
Name: "AssetID",
In: "path",
}
params.AssetID = packed[key].(int64)
}
return params
}
func decodeAssetLocationParams(args [1]string, argsEscaped bool, r *http.Request) (params AssetLocationParams, _ error) {
// Decode path: AssetID.
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: "AssetID",
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.AssetID = 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.AssetID)); 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: "AssetID",
In: "path",
Err: err,
}
}
return params, nil
}
// CreateMapfixAuditCommentParams is parameters of createMapfixAuditComment operation.
type CreateMapfixAuditCommentParams struct {
// The unique identifier for a mapfix.

@ -3,6 +3,7 @@
package api
import (
"bytes"
"fmt"
"io"
"mime"
@ -75,66 +76,6 @@ func decodeActionMapfixAcceptedResponse(resp *http.Response) (res *ActionMapfixA
return res, errors.Wrap(defRes, "error")
}
func decodeActionMapfixBypassSubmitResponse(resp *http.Response) (res *ActionMapfixBypassSubmitNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionMapfixBypassSubmitNoContent{}, 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 decodeActionMapfixRejectResponse(resp *http.Response) (res *ActionMapfixRejectNoContent, _ error) {
switch resp.StatusCode {
case 204:
@ -495,6 +436,66 @@ func decodeActionMapfixTriggerSubmitResponse(resp *http.Response) (res *ActionMa
return res, errors.Wrap(defRes, "error")
}
func decodeActionMapfixTriggerSubmitUncheckedResponse(resp *http.Response) (res *ActionMapfixTriggerSubmitUncheckedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionMapfixTriggerSubmitUncheckedNoContent{}, 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 decodeActionMapfixTriggerUploadResponse(resp *http.Response) (res *ActionMapfixTriggerUploadNoContent, _ error) {
switch resp.StatusCode {
case 204:
@ -735,66 +736,6 @@ func decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSub
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionBypassSubmitResponse(resp *http.Response) (res *ActionSubmissionBypassSubmitNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionBypassSubmitNoContent{}, 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 decodeActionSubmissionRejectResponse(resp *http.Response) (res *ActionSubmissionRejectNoContent, _ error) {
switch resp.StatusCode {
case 204:
@ -1155,6 +1096,66 @@ func decodeActionSubmissionTriggerSubmitResponse(resp *http.Response) (res *Acti
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionTriggerSubmitUncheckedResponse(resp *http.Response) (res *ActionSubmissionTriggerSubmitUncheckedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionTriggerSubmitUncheckedNoContent{}, 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 decodeActionSubmissionTriggerUploadResponse(resp *http.Response) (res *ActionSubmissionTriggerUploadNoContent, _ error) {
switch resp.StatusCode {
case 204:
@ -1335,6 +1336,82 @@ func decodeActionSubmissionValidatedResponse(resp *http.Response) (res *ActionSu
return res, errors.Wrap(defRes, "error")
}
func decodeAssetLocationResponse(resp *http.Response) (res AssetLocationOK, _ error) {
switch resp.StatusCode {
case 200:
// Code 200.
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "text/plain":
reader := resp.Body
b, err := io.ReadAll(reader)
if err != nil {
return res, err
}
response := AssetLocationOK{Data: bytes.NewReader(b)}
return response, nil
default:
return res, validate.InvalidContentType(ct)
}
}
// 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 decodeCreateMapfixResponse(resp *http.Response) (res *OperationID, _ error) {
switch resp.StatusCode {
case 201:

@ -3,6 +3,7 @@
package api
import (
"io"
"net/http"
"github.com/go-faster/errors"
@ -20,13 +21,6 @@ func encodeActionMapfixAcceptedResponse(response *ActionMapfixAcceptedNoContent,
return nil
}
func encodeActionMapfixBypassSubmitResponse(response *ActionMapfixBypassSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixRejectResponse(response *ActionMapfixRejectNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -69,6 +63,13 @@ func encodeActionMapfixTriggerSubmitResponse(response *ActionMapfixTriggerSubmit
return nil
}
func encodeActionMapfixTriggerSubmitUncheckedResponse(response *ActionMapfixTriggerSubmitUncheckedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixTriggerUploadResponse(response *ActionMapfixTriggerUploadNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -97,13 +98,6 @@ func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNo
return nil
}
func encodeActionSubmissionBypassSubmitResponse(response *ActionSubmissionBypassSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionRejectResponse(response *ActionSubmissionRejectNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -146,6 +140,13 @@ func encodeActionSubmissionTriggerSubmitResponse(response *ActionSubmissionTrigg
return nil
}
func encodeActionSubmissionTriggerSubmitUncheckedResponse(response *ActionSubmissionTriggerSubmitUncheckedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionTriggerUploadResponse(response *ActionSubmissionTriggerUploadNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -167,6 +168,22 @@ func encodeActionSubmissionValidatedResponse(response *ActionSubmissionValidated
return nil
}
func encodeAssetLocationResponse(response AssetLocationOK, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
writer := w
if closer, ok := response.Data.(io.Closer); ok {
defer closer.Close()
}
if _, err := io.Copy(writer, response); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeCreateMapfixResponse(response *OperationID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)

@ -61,6 +61,51 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break
}
switch elem[0] {
case 'a': // Prefix: "asset/"
if l := len("asset/"); len(elem) >= l && elem[0:l] == "asset/" {
elem = elem[l:]
} else {
break
}
// Param: "AssetID"
// Match until "/"
idx := strings.IndexByte(elem, '/')
if idx < 0 {
idx = len(elem)
}
args[0] = elem[:idx]
elem = elem[idx:]
if len(elem) == 0 {
break
}
switch elem[0] {
case '/': // Prefix: "/location"
if l := len("/location"); len(elem) >= l && elem[0:l] == "/location" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "GET":
s.handleAssetLocationRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "GET")
}
return
}
}
case 'm': // Prefix: "map"
if l := len("map"); len(elem) >= l && elem[0:l] == "map" {
@ -250,28 +295,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break
}
switch elem[0] {
case 'b': // Prefix: "bypass-submit"
if l := len("bypass-submit"); len(elem) >= l && elem[0:l] == "bypass-submit" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionMapfixBypassSubmitRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'r': // Prefix: "re"
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
@ -475,7 +498,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionMapfixTriggerSubmitRequest([1]string{
@ -487,6 +509,30 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
switch elem[0] {
case '-': // Prefix: "-unchecked"
if l := len("-unchecked"); len(elem) >= l && elem[0:l] == "-unchecked" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionMapfixTriggerSubmitUncheckedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
}
case 'u': // Prefix: "upload"
@ -1068,28 +1114,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break
}
switch elem[0] {
case 'b': // Prefix: "bypass-submit"
if l := len("bypass-submit"); len(elem) >= l && elem[0:l] == "bypass-submit" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionBypassSubmitRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'r': // Prefix: "re"
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
@ -1293,7 +1317,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionTriggerSubmitRequest([1]string{
@ -1305,6 +1328,30 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
switch elem[0] {
case '-': // Prefix: "-unchecked"
if l := len("-unchecked"); len(elem) >= l && elem[0:l] == "-unchecked" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionTriggerSubmitUncheckedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
}
case 'u': // Prefix: "upload"
@ -1456,6 +1503,53 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break
}
switch elem[0] {
case 'a': // Prefix: "asset/"
if l := len("asset/"); len(elem) >= l && elem[0:l] == "asset/" {
elem = elem[l:]
} else {
break
}
// Param: "AssetID"
// Match until "/"
idx := strings.IndexByte(elem, '/')
if idx < 0 {
idx = len(elem)
}
args[0] = elem[:idx]
elem = elem[idx:]
if len(elem) == 0 {
break
}
switch elem[0] {
case '/': // Prefix: "/location"
if l := len("/location"); len(elem) >= l && elem[0:l] == "/location" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "GET":
r.name = AssetLocationOperation
r.summary = "Get location of asset"
r.operationID = "assetLocation"
r.pathPattern = "/asset/{AssetID}/location"
r.args = args
r.count = 1
return r, true
default:
return
}
}
}
case 'm': // Prefix: "map"
if l := len("map"); len(elem) >= l && elem[0:l] == "map" {
@ -1665,30 +1759,6 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break
}
switch elem[0] {
case 'b': // Prefix: "bypass-submit"
if l := len("bypass-submit"); len(elem) >= l && elem[0:l] == "bypass-submit" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionMapfixBypassSubmitOperation
r.summary = "Role Reviewer changes status from ChangesRequested -> Submitted"
r.operationID = "actionMapfixBypassSubmit"
r.pathPattern = "/mapfixes/{MapfixID}/status/bypass-submit"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'r': // Prefix: "re"
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
@ -1906,7 +1976,6 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionMapfixTriggerSubmitOperation
@ -1920,6 +1989,32 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
return
}
}
switch elem[0] {
case '-': // Prefix: "-unchecked"
if l := len("-unchecked"); len(elem) >= l && elem[0:l] == "-unchecked" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionMapfixTriggerSubmitUncheckedOperation
r.summary = "Role Reviewer changes status from ChangesRequested -> Submitting"
r.operationID = "actionMapfixTriggerSubmitUnchecked"
r.pathPattern = "/mapfixes/{MapfixID}/status/trigger-submit-unchecked"
r.args = args
r.count = 1
return r, true
default:
return
}
}
}
case 'u': // Prefix: "upload"
@ -2593,30 +2688,6 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break
}
switch elem[0] {
case 'b': // Prefix: "bypass-submit"
if l := len("bypass-submit"); len(elem) >= l && elem[0:l] == "bypass-submit" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionBypassSubmitOperation
r.summary = "Role Reviewer changes status from ChangesRequested -> Submitted"
r.operationID = "actionSubmissionBypassSubmit"
r.pathPattern = "/submissions/{SubmissionID}/status/bypass-submit"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'r': // Prefix: "re"
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
@ -2834,7 +2905,6 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionTriggerSubmitOperation
@ -2848,6 +2918,32 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
return
}
}
switch elem[0] {
case '-': // Prefix: "-unchecked"
if l := len("-unchecked"); len(elem) >= l && elem[0:l] == "-unchecked" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionTriggerSubmitUncheckedOperation
r.summary = "Role Reviewer changes status from ChangesRequested -> Submitting"
r.operationID = "actionSubmissionTriggerSubmitUnchecked"
r.pathPattern = "/submissions/{SubmissionID}/status/trigger-submit-unchecked"
r.args = args
r.count = 1
return r, true
default:
return
}
}
}
case 'u': // Prefix: "upload"

@ -17,9 +17,6 @@ func (s *ErrorStatusCode) Error() string {
// ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation.
type ActionMapfixAcceptedNoContent struct{}
// ActionMapfixBypassSubmitNoContent is response for ActionMapfixBypassSubmit operation.
type ActionMapfixBypassSubmitNoContent struct{}
// ActionMapfixRejectNoContent is response for ActionMapfixReject operation.
type ActionMapfixRejectNoContent struct{}
@ -38,6 +35,9 @@ type ActionMapfixRevokeNoContent struct{}
// ActionMapfixTriggerSubmitNoContent is response for ActionMapfixTriggerSubmit operation.
type ActionMapfixTriggerSubmitNoContent struct{}
// ActionMapfixTriggerSubmitUncheckedNoContent is response for ActionMapfixTriggerSubmitUnchecked operation.
type ActionMapfixTriggerSubmitUncheckedNoContent struct{}
// ActionMapfixTriggerUploadNoContent is response for ActionMapfixTriggerUpload operation.
type ActionMapfixTriggerUploadNoContent struct{}
@ -50,9 +50,6 @@ type ActionMapfixValidatedNoContent struct{}
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
type ActionSubmissionAcceptedNoContent struct{}
// ActionSubmissionBypassSubmitNoContent is response for ActionSubmissionBypassSubmit operation.
type ActionSubmissionBypassSubmitNoContent struct{}
// ActionSubmissionRejectNoContent is response for ActionSubmissionReject operation.
type ActionSubmissionRejectNoContent struct{}
@ -71,6 +68,9 @@ type ActionSubmissionRevokeNoContent struct{}
// ActionSubmissionTriggerSubmitNoContent is response for ActionSubmissionTriggerSubmit operation.
type ActionSubmissionTriggerSubmitNoContent struct{}
// ActionSubmissionTriggerSubmitUncheckedNoContent is response for ActionSubmissionTriggerSubmitUnchecked operation.
type ActionSubmissionTriggerSubmitUncheckedNoContent struct{}
// ActionSubmissionTriggerUploadNoContent is response for ActionSubmissionTriggerUpload operation.
type ActionSubmissionTriggerUploadNoContent struct{}
@ -80,6 +80,20 @@ type ActionSubmissionTriggerValidateNoContent struct{}
// ActionSubmissionValidatedNoContent is response for ActionSubmissionValidated operation.
type ActionSubmissionValidatedNoContent struct{}
type AssetLocationOK struct {
Data io.Reader
}
// Read reads data from the Data reader.
//
// Kept to satisfy the io.Reader interface.
func (s AssetLocationOK) Read(p []byte) (n int, err error) {
if s.Data == nil {
return 0, io.EOF
}
return s.Data.Read(p)
}
// Ref: #/components/schemas/AuditEvent
type AuditEvent struct {
ID int64 `json:"ID"`

@ -34,48 +34,49 @@ func findAuthorization(h http.Header, prefix string) (string, bool) {
}
var operationRolesCookieAuth = map[string][]string{
ActionMapfixAcceptedOperation: []string{},
ActionMapfixBypassSubmitOperation: []string{},
ActionMapfixRejectOperation: []string{},
ActionMapfixRequestChangesOperation: []string{},
ActionMapfixResetSubmittingOperation: []string{},
ActionMapfixRetryValidateOperation: []string{},
ActionMapfixRevokeOperation: []string{},
ActionMapfixTriggerSubmitOperation: []string{},
ActionMapfixTriggerUploadOperation: []string{},
ActionMapfixTriggerValidateOperation: []string{},
ActionMapfixValidatedOperation: []string{},
ActionSubmissionAcceptedOperation: []string{},
ActionSubmissionBypassSubmitOperation: []string{},
ActionSubmissionRejectOperation: []string{},
ActionSubmissionRequestChangesOperation: []string{},
ActionSubmissionResetSubmittingOperation: []string{},
ActionSubmissionRetryValidateOperation: []string{},
ActionSubmissionRevokeOperation: []string{},
ActionSubmissionTriggerSubmitOperation: []string{},
ActionSubmissionTriggerUploadOperation: []string{},
ActionSubmissionTriggerValidateOperation: []string{},
ActionSubmissionValidatedOperation: []string{},
CreateMapfixOperation: []string{},
CreateMapfixAuditCommentOperation: []string{},
CreateScriptOperation: []string{},
CreateScriptPolicyOperation: []string{},
CreateSubmissionOperation: []string{},
CreateSubmissionAdminOperation: []string{},
CreateSubmissionAuditCommentOperation: []string{},
DeleteScriptOperation: []string{},
DeleteScriptPolicyOperation: []string{},
GetOperationOperation: []string{},
ReleaseSubmissionsOperation: []string{},
SessionRolesOperation: []string{},
SessionUserOperation: []string{},
SessionValidateOperation: []string{},
SetMapfixCompletedOperation: []string{},
SetSubmissionCompletedOperation: []string{},
UpdateMapfixModelOperation: []string{},
UpdateScriptOperation: []string{},
UpdateScriptPolicyOperation: []string{},
UpdateSubmissionModelOperation: []string{},
ActionMapfixAcceptedOperation: []string{},
ActionMapfixRejectOperation: []string{},
ActionMapfixRequestChangesOperation: []string{},
ActionMapfixResetSubmittingOperation: []string{},
ActionMapfixRetryValidateOperation: []string{},
ActionMapfixRevokeOperation: []string{},
ActionMapfixTriggerSubmitOperation: []string{},
ActionMapfixTriggerSubmitUncheckedOperation: []string{},
ActionMapfixTriggerUploadOperation: []string{},
ActionMapfixTriggerValidateOperation: []string{},
ActionMapfixValidatedOperation: []string{},
ActionSubmissionAcceptedOperation: []string{},
ActionSubmissionRejectOperation: []string{},
ActionSubmissionRequestChangesOperation: []string{},
ActionSubmissionResetSubmittingOperation: []string{},
ActionSubmissionRetryValidateOperation: []string{},
ActionSubmissionRevokeOperation: []string{},
ActionSubmissionTriggerSubmitOperation: []string{},
ActionSubmissionTriggerSubmitUncheckedOperation: []string{},
ActionSubmissionTriggerUploadOperation: []string{},
ActionSubmissionTriggerValidateOperation: []string{},
ActionSubmissionValidatedOperation: []string{},
AssetLocationOperation: []string{},
CreateMapfixOperation: []string{},
CreateMapfixAuditCommentOperation: []string{},
CreateScriptOperation: []string{},
CreateScriptPolicyOperation: []string{},
CreateSubmissionOperation: []string{},
CreateSubmissionAdminOperation: []string{},
CreateSubmissionAuditCommentOperation: []string{},
DeleteScriptOperation: []string{},
DeleteScriptPolicyOperation: []string{},
GetOperationOperation: []string{},
ReleaseSubmissionsOperation: []string{},
SessionRolesOperation: []string{},
SessionUserOperation: []string{},
SessionValidateOperation: []string{},
SetMapfixCompletedOperation: []string{},
SetSubmissionCompletedOperation: []string{},
UpdateMapfixModelOperation: []string{},
UpdateScriptOperation: []string{},
UpdateScriptPolicyOperation: []string{},
UpdateSubmissionModelOperation: []string{},
}
func (s *Server) securityCookieAuth(ctx context.Context, operationName OperationName, req *http.Request) (context.Context, bool, error) {

@ -14,12 +14,6 @@ type Handler interface {
//
// POST /mapfixes/{MapfixID}/status/reset-validating
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixBypassSubmit implements actionMapfixBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/bypass-submit
ActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) error
// ActionMapfixReject implements actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@ -57,6 +51,12 @@ type Handler interface {
//
// POST /mapfixes/{MapfixID}/status/trigger-submit
ActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) error
// ActionMapfixTriggerSubmitUnchecked implements actionMapfixTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/trigger-submit-unchecked
ActionMapfixTriggerSubmitUnchecked(ctx context.Context, params ActionMapfixTriggerSubmitUncheckedParams) error
// ActionMapfixTriggerUpload implements actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@ -81,12 +81,6 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/reset-validating
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionBypassSubmit implements actionSubmissionBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/bypass-submit
ActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) error
// ActionSubmissionReject implements actionSubmissionReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@ -124,6 +118,12 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/trigger-submit
ActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) error
// ActionSubmissionTriggerSubmitUnchecked implements actionSubmissionTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/trigger-submit-unchecked
ActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params ActionSubmissionTriggerSubmitUncheckedParams) error
// ActionSubmissionTriggerUpload implements actionSubmissionTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@ -142,6 +142,12 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/reset-uploading
ActionSubmissionValidated(ctx context.Context, params ActionSubmissionValidatedParams) error
// AssetLocation implements assetLocation operation.
//
// Get location of asset.
//
// GET /asset/{AssetID}/location
AssetLocation(ctx context.Context, params AssetLocationParams) (AssetLocationOK, error)
// CreateMapfix implements createMapfix operation.
//
// Trigger the validator to create a mapfix.

@ -22,15 +22,6 @@ func (UnimplementedHandler) ActionMapfixAccepted(ctx context.Context, params Act
return ht.ErrNotImplemented
}
// ActionMapfixBypassSubmit implements actionMapfixBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/bypass-submit
func (UnimplementedHandler) ActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixReject implements actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@ -86,6 +77,15 @@ func (UnimplementedHandler) ActionMapfixTriggerSubmit(ctx context.Context, param
return ht.ErrNotImplemented
}
// ActionMapfixTriggerSubmitUnchecked implements actionMapfixTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/trigger-submit-unchecked
func (UnimplementedHandler) ActionMapfixTriggerSubmitUnchecked(ctx context.Context, params ActionMapfixTriggerSubmitUncheckedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixTriggerUpload implements actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@ -122,15 +122,6 @@ func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params
return ht.ErrNotImplemented
}
// ActionSubmissionBypassSubmit implements actionSubmissionBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/bypass-submit
func (UnimplementedHandler) ActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionReject implements actionSubmissionReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@ -186,6 +177,15 @@ func (UnimplementedHandler) ActionSubmissionTriggerSubmit(ctx context.Context, p
return ht.ErrNotImplemented
}
// ActionSubmissionTriggerSubmitUnchecked implements actionSubmissionTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/trigger-submit-unchecked
func (UnimplementedHandler) ActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params ActionSubmissionTriggerSubmitUncheckedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionTriggerUpload implements actionSubmissionTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@ -213,6 +213,15 @@ func (UnimplementedHandler) ActionSubmissionValidated(ctx context.Context, param
return ht.ErrNotImplemented
}
// AssetLocation implements assetLocation operation.
//
// Get location of asset.
//
// GET /asset/{AssetID}/location
func (UnimplementedHandler) AssetLocation(ctx context.Context, params AssetLocationParams) (r AssetLocationOK, _ error) {
return r, ht.ErrNotImplemented
}
// CreateMapfix implements createMapfix operation.
//
// Trigger the validator to create a mapfix.

@ -10,6 +10,7 @@ import (
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/roblox"
"git.itzana.me/strafesnet/maps-service/pkg/service"
"git.itzana.me/strafesnet/maps-service/pkg/service_internal"
"github.com/nats-io/nats.go"
@ -91,6 +92,12 @@ func NewServeCommand() *cli.Command {
EnvVars: []string{"NATS_HOST"},
Value: "nats:4222",
},
&cli.StringFlag{
Name: "rbx-api-key",
Usage: "API Key for downloading asset locations",
EnvVars: []string{"RBX_API_KEY"},
Required: true,
},
},
}
}
@ -128,6 +135,10 @@ func serve(ctx *cli.Context) error {
Nats: js,
Maps: maps.NewMapsServiceClient(conn),
Users: users.NewUsersServiceClient(conn),
Roblox: roblox.Client{
HttpClient: http.DefaultClient,
ApiKey: ctx.String("rbx-api-key"),
},
}
conn, err = grpc.Dial(ctx.String("auth-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials()))

@ -100,6 +100,18 @@ type Invoker interface {
//
// POST /mapfixes
CreateMapfix(ctx context.Context, request *MapfixCreate) (*MapfixID, error)
// CreateMapfixAuditCheckList invokes createMapfixAuditCheckList operation.
//
// Validator posts a checklist to the audit log.
//
// POST /mapfixes/{MapfixID}/checklist
CreateMapfixAuditCheckList(ctx context.Context, request CheckList, params CreateMapfixAuditCheckListParams) error
// CreateMapfixAuditError invokes createMapfixAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /mapfixes/{MapfixID}/error
CreateMapfixAuditError(ctx context.Context, params CreateMapfixAuditErrorParams) error
// CreateScript invokes createScript operation.
//
// Create a new script.
@ -118,6 +130,18 @@ type Invoker interface {
//
// POST /submissions
CreateSubmission(ctx context.Context, request *SubmissionCreate) (*SubmissionID, error)
// CreateSubmissionAuditCheckList invokes createSubmissionAuditCheckList operation.
//
// Validator posts a checklist to the audit log.
//
// POST /submissions/{SubmissionID}/checklist
CreateSubmissionAuditCheckList(ctx context.Context, request CheckList, params CreateSubmissionAuditCheckListParams) error
// CreateSubmissionAuditError invokes createSubmissionAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /submissions/{SubmissionID}/error
CreateSubmissionAuditError(ctx context.Context, params CreateSubmissionAuditErrorParams) error
// GetScript invokes getScript operation.
//
// Get the specified script by ID.
@ -266,24 +290,6 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf
pathParts[2] = "/status/validator-failed"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "ErrorMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
@ -375,24 +381,6 @@ func (c *Client) sendActionMapfixRequestChanges(ctx context.Context, params Acti
pathParts[2] = "/status/validator-request-changes"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "ErrorMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
@ -926,24 +914,6 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
pathParts[2] = "/status/validator-failed"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "ErrorMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
@ -1035,24 +1005,6 @@ func (c *Client) sendActionSubmissionRequestChanges(ctx context.Context, params
pathParts[2] = "/status/validator-request-changes"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "ErrorMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
@ -1501,6 +1453,209 @@ func (c *Client) sendCreateMapfix(ctx context.Context, request *MapfixCreate) (r
return result, nil
}
// CreateMapfixAuditCheckList invokes createMapfixAuditCheckList operation.
//
// Validator posts a checklist to the audit log.
//
// POST /mapfixes/{MapfixID}/checklist
func (c *Client) CreateMapfixAuditCheckList(ctx context.Context, request CheckList, params CreateMapfixAuditCheckListParams) error {
_, err := c.sendCreateMapfixAuditCheckList(ctx, request, params)
return err
}
func (c *Client) sendCreateMapfixAuditCheckList(ctx context.Context, request CheckList, params CreateMapfixAuditCheckListParams) (res *CreateMapfixAuditCheckListNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createMapfixAuditCheckList"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/checklist"),
}
// 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, CreateMapfixAuditCheckListOperation,
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] = "/checklist"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
if err := encodeCreateMapfixAuditCheckListRequest(request, r); err != nil {
return res, errors.Wrap(err, "encode 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 := decodeCreateMapfixAuditCheckListResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// CreateMapfixAuditError invokes createMapfixAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /mapfixes/{MapfixID}/error
func (c *Client) CreateMapfixAuditError(ctx context.Context, params CreateMapfixAuditErrorParams) error {
_, err := c.sendCreateMapfixAuditError(ctx, params)
return err
}
func (c *Client) sendCreateMapfixAuditError(ctx context.Context, params CreateMapfixAuditErrorParams) (res *CreateMapfixAuditErrorNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createMapfixAuditError"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/error"),
}
// 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, CreateMapfixAuditErrorOperation,
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] = "/error"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "ErrorMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
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 := decodeCreateMapfixAuditErrorResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// CreateScript invokes createScript operation.
//
// Create a new script.
@ -1726,6 +1881,209 @@ func (c *Client) sendCreateSubmission(ctx context.Context, request *SubmissionCr
return result, nil
}
// CreateSubmissionAuditCheckList invokes createSubmissionAuditCheckList operation.
//
// Validator posts a checklist to the audit log.
//
// POST /submissions/{SubmissionID}/checklist
func (c *Client) CreateSubmissionAuditCheckList(ctx context.Context, request CheckList, params CreateSubmissionAuditCheckListParams) error {
_, err := c.sendCreateSubmissionAuditCheckList(ctx, request, params)
return err
}
func (c *Client) sendCreateSubmissionAuditCheckList(ctx context.Context, request CheckList, params CreateSubmissionAuditCheckListParams) (res *CreateSubmissionAuditCheckListNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createSubmissionAuditCheckList"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/checklist"),
}
// 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, CreateSubmissionAuditCheckListOperation,
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] = "/checklist"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
if err := encodeCreateSubmissionAuditCheckListRequest(request, r); err != nil {
return res, errors.Wrap(err, "encode 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 := decodeCreateSubmissionAuditCheckListResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// CreateSubmissionAuditError invokes createSubmissionAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /submissions/{SubmissionID}/error
func (c *Client) CreateSubmissionAuditError(ctx context.Context, params CreateSubmissionAuditErrorParams) error {
_, err := c.sendCreateSubmissionAuditError(ctx, params)
return err
}
func (c *Client) sendCreateSubmissionAuditError(ctx context.Context, params CreateSubmissionAuditErrorParams) (res *CreateSubmissionAuditErrorNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createSubmissionAuditError"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/error"),
}
// 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, CreateSubmissionAuditErrorOperation,
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] = "/error"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "ErrorMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
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 := decodeCreateSubmissionAuditErrorResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// GetScript invokes getScript operation.
//
// Get the specified script by ID.

@ -128,10 +128,6 @@ func (s *Server) handleActionMapfixAcceptedRequest(args [1]string, argsEscaped b
Name: "MapfixID",
In: "path",
}: params.MapfixID,
{
Name: "ErrorMessage",
In: "query",
}: params.ErrorMessage,
},
Raw: r,
}
@ -281,10 +277,6 @@ func (s *Server) handleActionMapfixRequestChangesRequest(args [1]string, argsEsc
Name: "MapfixID",
In: "path",
}: params.MapfixID,
{
Name: "ErrorMessage",
In: "query",
}: params.ErrorMessage,
},
Raw: r,
}
@ -1050,10 +1042,6 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
Name: "SubmissionID",
In: "path",
}: params.SubmissionID,
{
Name: "ErrorMessage",
In: "query",
}: params.ErrorMessage,
},
Raw: r,
}
@ -1203,10 +1191,6 @@ func (s *Server) handleActionSubmissionRequestChangesRequest(args [1]string, arg
Name: "SubmissionID",
In: "path",
}: params.SubmissionID,
{
Name: "ErrorMessage",
In: "query",
}: params.ErrorMessage,
},
Raw: r,
}
@ -1874,6 +1858,323 @@ func (s *Server) handleCreateMapfixRequest(args [0]string, argsEscaped bool, w h
}
}
// handleCreateMapfixAuditCheckListRequest handles createMapfixAuditCheckList operation.
//
// Validator posts a checklist to the audit log.
//
// POST /mapfixes/{MapfixID}/checklist
func (s *Server) handleCreateMapfixAuditCheckListRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createMapfixAuditCheckList"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/checklist"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), CreateMapfixAuditCheckListOperation,
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: CreateMapfixAuditCheckListOperation,
ID: "createMapfixAuditCheckList",
}
)
params, err := decodeCreateMapfixAuditCheckListParams(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
}
request, close, err := s.decodeCreateMapfixAuditCheckListRequest(r)
if err != nil {
err = &ogenerrors.DecodeRequestError{
OperationContext: opErrContext,
Err: err,
}
defer recordError("DecodeRequest", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
defer func() {
if err := close(); err != nil {
recordError("CloseRequest", err)
}
}()
var response *CreateMapfixAuditCheckListNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: CreateMapfixAuditCheckListOperation,
OperationSummary: "Validator posts a checklist to the audit log",
OperationID: "createMapfixAuditCheckList",
Body: request,
Params: middleware.Parameters{
{
Name: "MapfixID",
In: "path",
}: params.MapfixID,
},
Raw: r,
}
type (
Request = CheckList
Params = CreateMapfixAuditCheckListParams
Response = *CreateMapfixAuditCheckListNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackCreateMapfixAuditCheckListParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.CreateMapfixAuditCheckList(ctx, request, params)
return response, err
},
)
} else {
err = s.h.CreateMapfixAuditCheckList(ctx, request, 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 := encodeCreateMapfixAuditCheckListResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleCreateMapfixAuditErrorRequest handles createMapfixAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /mapfixes/{MapfixID}/error
func (s *Server) handleCreateMapfixAuditErrorRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createMapfixAuditError"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/error"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), CreateMapfixAuditErrorOperation,
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: CreateMapfixAuditErrorOperation,
ID: "createMapfixAuditError",
}
)
params, err := decodeCreateMapfixAuditErrorParams(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 *CreateMapfixAuditErrorNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: CreateMapfixAuditErrorOperation,
OperationSummary: "Validator posts an error to the audit log",
OperationID: "createMapfixAuditError",
Body: nil,
Params: middleware.Parameters{
{
Name: "MapfixID",
In: "path",
}: params.MapfixID,
{
Name: "ErrorMessage",
In: "query",
}: params.ErrorMessage,
},
Raw: r,
}
type (
Request = struct{}
Params = CreateMapfixAuditErrorParams
Response = *CreateMapfixAuditErrorNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackCreateMapfixAuditErrorParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.CreateMapfixAuditError(ctx, params)
return response, err
},
)
} else {
err = s.h.CreateMapfixAuditError(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 := encodeCreateMapfixAuditErrorResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleCreateScriptRequest handles createScript operation.
//
// Create a new script.
@ -2321,6 +2622,323 @@ func (s *Server) handleCreateSubmissionRequest(args [0]string, argsEscaped bool,
}
}
// handleCreateSubmissionAuditCheckListRequest handles createSubmissionAuditCheckList operation.
//
// Validator posts a checklist to the audit log.
//
// POST /submissions/{SubmissionID}/checklist
func (s *Server) handleCreateSubmissionAuditCheckListRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createSubmissionAuditCheckList"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/checklist"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), CreateSubmissionAuditCheckListOperation,
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: CreateSubmissionAuditCheckListOperation,
ID: "createSubmissionAuditCheckList",
}
)
params, err := decodeCreateSubmissionAuditCheckListParams(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
}
request, close, err := s.decodeCreateSubmissionAuditCheckListRequest(r)
if err != nil {
err = &ogenerrors.DecodeRequestError{
OperationContext: opErrContext,
Err: err,
}
defer recordError("DecodeRequest", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
defer func() {
if err := close(); err != nil {
recordError("CloseRequest", err)
}
}()
var response *CreateSubmissionAuditCheckListNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: CreateSubmissionAuditCheckListOperation,
OperationSummary: "Validator posts a checklist to the audit log",
OperationID: "createSubmissionAuditCheckList",
Body: request,
Params: middleware.Parameters{
{
Name: "SubmissionID",
In: "path",
}: params.SubmissionID,
},
Raw: r,
}
type (
Request = CheckList
Params = CreateSubmissionAuditCheckListParams
Response = *CreateSubmissionAuditCheckListNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackCreateSubmissionAuditCheckListParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.CreateSubmissionAuditCheckList(ctx, request, params)
return response, err
},
)
} else {
err = s.h.CreateSubmissionAuditCheckList(ctx, request, 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 := encodeCreateSubmissionAuditCheckListResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleCreateSubmissionAuditErrorRequest handles createSubmissionAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /submissions/{SubmissionID}/error
func (s *Server) handleCreateSubmissionAuditErrorRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createSubmissionAuditError"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/error"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), CreateSubmissionAuditErrorOperation,
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: CreateSubmissionAuditErrorOperation,
ID: "createSubmissionAuditError",
}
)
params, err := decodeCreateSubmissionAuditErrorParams(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 *CreateSubmissionAuditErrorNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: CreateSubmissionAuditErrorOperation,
OperationSummary: "Validator posts an error to the audit log",
OperationID: "createSubmissionAuditError",
Body: nil,
Params: middleware.Parameters{
{
Name: "SubmissionID",
In: "path",
}: params.SubmissionID,
{
Name: "ErrorMessage",
In: "query",
}: params.ErrorMessage,
},
Raw: r,
}
type (
Request = struct{}
Params = CreateSubmissionAuditErrorParams
Response = *CreateSubmissionAuditErrorNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackCreateSubmissionAuditErrorParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.CreateSubmissionAuditError(ctx, params)
return response, err
},
)
} else {
err = s.h.CreateSubmissionAuditError(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 := encodeCreateSubmissionAuditErrorResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleGetScriptRequest handles getScript operation.
//
// Get the specified script by ID.

@ -12,6 +12,186 @@ import (
"github.com/ogen-go/ogen/validate"
)
// Encode implements json.Marshaler.
func (s *Check) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *Check) encodeFields(e *jx.Encoder) {
{
e.FieldStart("Name")
e.Str(s.Name)
}
{
e.FieldStart("Summary")
e.Str(s.Summary)
}
{
e.FieldStart("Passed")
e.Bool(s.Passed)
}
}
var jsonFieldsNameOfCheck = [3]string{
0: "Name",
1: "Summary",
2: "Passed",
}
// Decode decodes Check from json.
func (s *Check) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode Check to nil")
}
var requiredBitSet [1]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "Name":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Str()
s.Name = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Name\"")
}
case "Summary":
requiredBitSet[0] |= 1 << 1
if err := func() error {
v, err := d.Str()
s.Summary = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Summary\"")
}
case "Passed":
requiredBitSet[0] |= 1 << 2
if err := func() error {
v, err := d.Bool()
s.Passed = bool(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Passed\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode Check")
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00000111,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
//
// If XOR result is not zero, result is not equal to expected, so some fields are missed.
// Bits of fields which would be set are actually bits of missed fields.
missed := bits.OnesCount8(result)
for bitN := 0; bitN < missed; bitN++ {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfCheck) {
name = jsonFieldsNameOfCheck[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
failures = append(failures, validate.FieldError{
Name: name,
Error: validate.ErrFieldRequired,
})
// Reset bit.
result &^= 1 << bitIdx
}
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *Check) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *Check) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode encodes CheckList as json.
func (s CheckList) Encode(e *jx.Encoder) {
unwrapped := []Check(s)
e.ArrStart()
for _, elem := range unwrapped {
elem.Encode(e)
}
e.ArrEnd()
}
// Decode decodes CheckList from json.
func (s *CheckList) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode CheckList to nil")
}
var unwrapped []Check
if err := func() error {
unwrapped = make([]Check, 0)
if err := d.Arr(func(d *jx.Decoder) error {
var elem Check
if err := elem.Decode(d); err != nil {
return err
}
unwrapped = append(unwrapped, elem)
return nil
}); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "alias")
}
*s = CheckList(unwrapped)
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s CheckList) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *CheckList) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *Error) Encode(e *jx.Encoder) {
e.ObjStart()

@ -18,9 +18,13 @@ const (
ActionSubmissionUploadedOperation OperationName = "ActionSubmissionUploaded"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateMapfixOperation OperationName = "CreateMapfix"
CreateMapfixAuditCheckListOperation OperationName = "CreateMapfixAuditCheckList"
CreateMapfixAuditErrorOperation OperationName = "CreateMapfixAuditError"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
CreateSubmissionAuditCheckListOperation OperationName = "CreateSubmissionAuditCheckList"
CreateSubmissionAuditErrorOperation OperationName = "CreateSubmissionAuditError"
GetScriptOperation OperationName = "GetScript"
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"

@ -18,8 +18,7 @@ import (
// ActionMapfixAcceptedParams is parameters of actionMapfixAccepted operation.
type ActionMapfixAcceptedParams struct {
// The unique identifier for a submission.
MapfixID int64
ErrorMessage string
MapfixID int64
}
func unpackActionMapfixAcceptedParams(packed middleware.Parameters) (params ActionMapfixAcceptedParams) {
@ -30,18 +29,10 @@ func unpackActionMapfixAcceptedParams(packed middleware.Parameters) (params Acti
}
params.MapfixID = packed[key].(int64)
}
{
key := middleware.ParameterKey{
Name: "ErrorMessage",
In: "query",
}
params.ErrorMessage = packed[key].(string)
}
return params
}
func decodeActionMapfixAcceptedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixAcceptedParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query())
// Decode path: MapfixID.
if err := func() error {
param := args[0]
@ -104,66 +95,13 @@ func decodeActionMapfixAcceptedParams(args [1]string, argsEscaped bool, r *http.
Err: err,
}
}
// Decode query: ErrorMessage.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToString(val)
if err != nil {
return err
}
params.ErrorMessage = c
return nil
}); err != nil {
return err
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: true,
MaxLength: 4096,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(params.ErrorMessage)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
return err
}
} else {
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ErrorMessage",
In: "query",
Err: err,
}
}
return params, nil
}
// ActionMapfixRequestChangesParams is parameters of actionMapfixRequestChanges operation.
type ActionMapfixRequestChangesParams struct {
// The unique identifier for a submission.
MapfixID int64
ErrorMessage string
MapfixID int64
}
func unpackActionMapfixRequestChangesParams(packed middleware.Parameters) (params ActionMapfixRequestChangesParams) {
@ -174,18 +112,10 @@ func unpackActionMapfixRequestChangesParams(packed middleware.Parameters) (param
}
params.MapfixID = packed[key].(int64)
}
{
key := middleware.ParameterKey{
Name: "ErrorMessage",
In: "query",
}
params.ErrorMessage = packed[key].(string)
}
return params
}
func decodeActionMapfixRequestChangesParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixRequestChangesParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query())
// Decode path: MapfixID.
if err := func() error {
param := args[0]
@ -248,58 +178,6 @@ func decodeActionMapfixRequestChangesParams(args [1]string, argsEscaped bool, r
Err: err,
}
}
// Decode query: ErrorMessage.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToString(val)
if err != nil {
return err
}
params.ErrorMessage = c
return nil
}); err != nil {
return err
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: true,
MaxLength: 4096,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(params.ErrorMessage)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
return err
}
} else {
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ErrorMessage",
In: "query",
Err: err,
}
}
return params, nil
}
@ -943,7 +821,6 @@ func decodeActionOperationFailedParams(args [1]string, argsEscaped bool, r *http
type ActionSubmissionAcceptedParams struct {
// The unique identifier for a submission.
SubmissionID int64
ErrorMessage string
}
func unpackActionSubmissionAcceptedParams(packed middleware.Parameters) (params ActionSubmissionAcceptedParams) {
@ -954,18 +831,10 @@ func unpackActionSubmissionAcceptedParams(packed middleware.Parameters) (params
}
params.SubmissionID = packed[key].(int64)
}
{
key := middleware.ParameterKey{
Name: "ErrorMessage",
In: "query",
}
params.ErrorMessage = packed[key].(string)
}
return params
}
func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionAcceptedParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query())
// Decode path: SubmissionID.
if err := func() error {
param := args[0]
@ -1028,58 +897,6 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
Err: err,
}
}
// Decode query: ErrorMessage.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToString(val)
if err != nil {
return err
}
params.ErrorMessage = c
return nil
}); err != nil {
return err
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: true,
MaxLength: 4096,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(params.ErrorMessage)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
return err
}
} else {
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ErrorMessage",
In: "query",
Err: err,
}
}
return params, nil
}
@ -1087,7 +904,6 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
type ActionSubmissionRequestChangesParams struct {
// The unique identifier for a submission.
SubmissionID int64
ErrorMessage string
}
func unpackActionSubmissionRequestChangesParams(packed middleware.Parameters) (params ActionSubmissionRequestChangesParams) {
@ -1098,18 +914,10 @@ func unpackActionSubmissionRequestChangesParams(packed middleware.Parameters) (p
}
params.SubmissionID = packed[key].(int64)
}
{
key := middleware.ParameterKey{
Name: "ErrorMessage",
In: "query",
}
params.ErrorMessage = packed[key].(string)
}
return params
}
func decodeActionSubmissionRequestChangesParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionRequestChangesParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query())
// Decode path: SubmissionID.
if err := func() error {
param := args[0]
@ -1172,58 +980,6 @@ func decodeActionSubmissionRequestChangesParams(args [1]string, argsEscaped bool
Err: err,
}
}
// Decode query: ErrorMessage.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToString(val)
if err != nil {
return err
}
params.ErrorMessage = c
return nil
}); err != nil {
return err
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: true,
MaxLength: 4096,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(params.ErrorMessage)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
return err
}
} else {
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ErrorMessage",
In: "query",
Err: err,
}
}
return params, nil
}
@ -1781,6 +1537,460 @@ func decodeActionSubmissionValidatedParams(args [1]string, argsEscaped bool, r *
return params, nil
}
// CreateMapfixAuditCheckListParams is parameters of createMapfixAuditCheckList operation.
type CreateMapfixAuditCheckListParams struct {
// The unique identifier for a submission.
MapfixID int64
}
func unpackCreateMapfixAuditCheckListParams(packed middleware.Parameters) (params CreateMapfixAuditCheckListParams) {
{
key := middleware.ParameterKey{
Name: "MapfixID",
In: "path",
}
params.MapfixID = packed[key].(int64)
}
return params
}
func decodeCreateMapfixAuditCheckListParams(args [1]string, argsEscaped bool, r *http.Request) (params CreateMapfixAuditCheckListParams, _ 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
}
// CreateMapfixAuditErrorParams is parameters of createMapfixAuditError operation.
type CreateMapfixAuditErrorParams struct {
// The unique identifier for a submission.
MapfixID int64
ErrorMessage string
}
func unpackCreateMapfixAuditErrorParams(packed middleware.Parameters) (params CreateMapfixAuditErrorParams) {
{
key := middleware.ParameterKey{
Name: "MapfixID",
In: "path",
}
params.MapfixID = packed[key].(int64)
}
{
key := middleware.ParameterKey{
Name: "ErrorMessage",
In: "query",
}
params.ErrorMessage = packed[key].(string)
}
return params
}
func decodeCreateMapfixAuditErrorParams(args [1]string, argsEscaped bool, r *http.Request) (params CreateMapfixAuditErrorParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query())
// 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,
}
}
// Decode query: ErrorMessage.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToString(val)
if err != nil {
return err
}
params.ErrorMessage = c
return nil
}); err != nil {
return err
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: true,
MaxLength: 4096,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(params.ErrorMessage)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
return err
}
} else {
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ErrorMessage",
In: "query",
Err: err,
}
}
return params, nil
}
// CreateSubmissionAuditCheckListParams is parameters of createSubmissionAuditCheckList operation.
type CreateSubmissionAuditCheckListParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
func unpackCreateSubmissionAuditCheckListParams(packed middleware.Parameters) (params CreateSubmissionAuditCheckListParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
In: "path",
}
params.SubmissionID = packed[key].(int64)
}
return params
}
func decodeCreateSubmissionAuditCheckListParams(args [1]string, argsEscaped bool, r *http.Request) (params CreateSubmissionAuditCheckListParams, _ 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
}
// CreateSubmissionAuditErrorParams is parameters of createSubmissionAuditError operation.
type CreateSubmissionAuditErrorParams struct {
// The unique identifier for a submission.
SubmissionID int64
ErrorMessage string
}
func unpackCreateSubmissionAuditErrorParams(packed middleware.Parameters) (params CreateSubmissionAuditErrorParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
In: "path",
}
params.SubmissionID = packed[key].(int64)
}
{
key := middleware.ParameterKey{
Name: "ErrorMessage",
In: "query",
}
params.ErrorMessage = packed[key].(string)
}
return params
}
func decodeCreateSubmissionAuditErrorParams(args [1]string, argsEscaped bool, r *http.Request) (params CreateSubmissionAuditErrorParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query())
// 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,
}
}
// Decode query: ErrorMessage.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToString(val)
if err != nil {
return err
}
params.ErrorMessage = c
return nil
}); err != nil {
return err
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: true,
MaxLength: 4096,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(params.ErrorMessage)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
return err
}
} else {
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ErrorMessage",
In: "query",
Err: err,
}
}
return params, nil
}
// GetScriptParams is parameters of getScript operation.
type GetScriptParams struct {
// The unique identifier for a script.

@ -85,6 +85,77 @@ func (s *Server) decodeCreateMapfixRequest(r *http.Request) (
}
}
func (s *Server) decodeCreateMapfixAuditCheckListRequest(r *http.Request) (
req CheckList,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
if r.ContentLength == 0 {
return req, close, validate.ErrBodyRequired
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return req, close, err
}
if len(buf) == 0 {
return req, close, validate.ErrBodyRequired
}
d := jx.DecodeBytes(buf)
var request CheckList
if err := func() error {
if err := request.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 req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeCreateScriptRequest(r *http.Request) (
req *ScriptCreate,
close func() error,
@ -297,3 +368,74 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeCreateSubmissionAuditCheckListRequest(r *http.Request) (
req CheckList,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
if r.ContentLength == 0 {
return req, close, validate.ErrBodyRequired
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return req, close, err
}
if len(buf) == 0 {
return req, close, validate.ErrBodyRequired
}
d := jx.DecodeBytes(buf)
var request CheckList
if err := func() error {
if err := request.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 req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}

@ -25,6 +25,20 @@ func encodeCreateMapfixRequest(
return nil
}
func encodeCreateMapfixAuditCheckListRequest(
req CheckList,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeCreateScriptRequest(
req *ScriptCreate,
r *http.Request,
@ -66,3 +80,17 @@ func encodeCreateSubmissionRequest(
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeCreateSubmissionAuditCheckListRequest(
req CheckList,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}

@ -776,6 +776,126 @@ func decodeCreateMapfixResponse(resp *http.Response) (res *MapfixID, _ error) {
return res, errors.Wrap(defRes, "error")
}
func decodeCreateMapfixAuditCheckListResponse(resp *http.Response) (res *CreateMapfixAuditCheckListNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &CreateMapfixAuditCheckListNoContent{}, 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 decodeCreateMapfixAuditErrorResponse(resp *http.Response) (res *CreateMapfixAuditErrorNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &CreateMapfixAuditErrorNoContent{}, 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 decodeCreateScriptResponse(resp *http.Response) (res *ScriptID, _ error) {
switch resp.StatusCode {
case 201:
@ -1079,6 +1199,126 @@ func decodeCreateSubmissionResponse(resp *http.Response) (res *SubmissionID, _ e
return res, errors.Wrap(defRes, "error")
}
func decodeCreateSubmissionAuditCheckListResponse(resp *http.Response) (res *CreateSubmissionAuditCheckListNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &CreateSubmissionAuditCheckListNoContent{}, 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 decodeCreateSubmissionAuditErrorResponse(resp *http.Response) (res *CreateSubmissionAuditErrorNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &CreateSubmissionAuditErrorNoContent{}, 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 decodeGetScriptResponse(resp *http.Response) (res *Script, _ error) {
switch resp.StatusCode {
case 200:

@ -104,6 +104,20 @@ func encodeCreateMapfixResponse(response *MapfixID, w http.ResponseWriter, span
return nil
}
func encodeCreateMapfixAuditCheckListResponse(response *CreateMapfixAuditCheckListNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeCreateMapfixAuditErrorResponse(response *CreateMapfixAuditErrorNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeCreateScriptResponse(response *ScriptID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
@ -146,6 +160,20 @@ func encodeCreateSubmissionResponse(response *SubmissionID, w http.ResponseWrite
return nil
}
func encodeCreateSubmissionAuditCheckListResponse(response *CreateSubmissionAuditCheckListNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeCreateSubmissionAuditErrorResponse(response *CreateSubmissionAuditErrorNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeGetScriptResponse(response *Script, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)

@ -113,6 +113,50 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break
}
switch elem[0] {
case 'c': // Prefix: "checklist"
if l := len("checklist"); len(elem) >= l && elem[0:l] == "checklist" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleCreateMapfixAuditCheckListRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'e': // Prefix: "error"
if l := len("error"); len(elem) >= l && elem[0:l] == "error" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleCreateMapfixAuditErrorRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 's': // Prefix: "status/validator-"
if l := len("status/validator-"); len(elem) >= l && elem[0:l] == "status/validator-" {
@ -464,6 +508,50 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break
}
switch elem[0] {
case 'c': // Prefix: "checklist"
if l := len("checklist"); len(elem) >= l && elem[0:l] == "checklist" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleCreateSubmissionAuditCheckListRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'e': // Prefix: "error"
if l := len("error"); len(elem) >= l && elem[0:l] == "error" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleCreateSubmissionAuditErrorRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 's': // Prefix: "status/validator-"
if l := len("status/validator-"); len(elem) >= l && elem[0:l] == "status/validator-" {
@ -768,6 +856,54 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break
}
switch elem[0] {
case 'c': // Prefix: "checklist"
if l := len("checklist"); len(elem) >= l && elem[0:l] == "checklist" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = CreateMapfixAuditCheckListOperation
r.summary = "Validator posts a checklist to the audit log"
r.operationID = "createMapfixAuditCheckList"
r.pathPattern = "/mapfixes/{MapfixID}/checklist"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'e': // Prefix: "error"
if l := len("error"); len(elem) >= l && elem[0:l] == "error" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = CreateMapfixAuditErrorOperation
r.summary = "Validator posts an error to the audit log"
r.operationID = "createMapfixAuditError"
r.pathPattern = "/mapfixes/{MapfixID}/error"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 's': // Prefix: "status/validator-"
if l := len("status/validator-"); len(elem) >= l && elem[0:l] == "status/validator-" {
@ -1159,6 +1295,54 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break
}
switch elem[0] {
case 'c': // Prefix: "checklist"
if l := len("checklist"); len(elem) >= l && elem[0:l] == "checklist" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = CreateSubmissionAuditCheckListOperation
r.summary = "Validator posts a checklist to the audit log"
r.operationID = "createSubmissionAuditCheckList"
r.pathPattern = "/submissions/{SubmissionID}/checklist"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'e': // Prefix: "error"
if l := len("error"); len(elem) >= l && elem[0:l] == "error" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = CreateSubmissionAuditErrorOperation
r.summary = "Validator posts an error to the audit log"
r.operationID = "createSubmissionAuditError"
r.pathPattern = "/submissions/{SubmissionID}/error"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 's': // Prefix: "status/validator-"
if l := len("status/validator-"); len(elem) >= l && elem[0:l] == "status/validator-" {

@ -43,6 +43,57 @@ type ActionSubmissionUploadedNoContent struct{}
// ActionSubmissionValidatedNoContent is response for ActionSubmissionValidated operation.
type ActionSubmissionValidatedNoContent struct{}
// Ref: #/components/schemas/Check
type Check struct {
Name string `json:"Name"`
Summary string `json:"Summary"`
Passed bool `json:"Passed"`
}
// GetName returns the value of Name.
func (s *Check) GetName() string {
return s.Name
}
// GetSummary returns the value of Summary.
func (s *Check) GetSummary() string {
return s.Summary
}
// GetPassed returns the value of Passed.
func (s *Check) GetPassed() bool {
return s.Passed
}
// SetName sets the value of Name.
func (s *Check) SetName(val string) {
s.Name = val
}
// SetSummary sets the value of Summary.
func (s *Check) SetSummary(val string) {
s.Summary = val
}
// SetPassed sets the value of Passed.
func (s *Check) SetPassed(val bool) {
s.Passed = val
}
type CheckList []Check
// CreateMapfixAuditCheckListNoContent is response for CreateMapfixAuditCheckList operation.
type CreateMapfixAuditCheckListNoContent struct{}
// CreateMapfixAuditErrorNoContent is response for CreateMapfixAuditError operation.
type CreateMapfixAuditErrorNoContent struct{}
// CreateSubmissionAuditCheckListNoContent is response for CreateSubmissionAuditCheckList operation.
type CreateSubmissionAuditCheckListNoContent struct{}
// CreateSubmissionAuditErrorNoContent is response for CreateSubmissionAuditError operation.
type CreateSubmissionAuditErrorNoContent struct{}
// Represents error object.
// Ref: #/components/schemas/Error
type Error struct {

@ -80,6 +80,18 @@ type Handler interface {
//
// POST /mapfixes
CreateMapfix(ctx context.Context, req *MapfixCreate) (*MapfixID, error)
// CreateMapfixAuditCheckList implements createMapfixAuditCheckList operation.
//
// Validator posts a checklist to the audit log.
//
// POST /mapfixes/{MapfixID}/checklist
CreateMapfixAuditCheckList(ctx context.Context, req CheckList, params CreateMapfixAuditCheckListParams) error
// CreateMapfixAuditError implements createMapfixAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /mapfixes/{MapfixID}/error
CreateMapfixAuditError(ctx context.Context, params CreateMapfixAuditErrorParams) error
// CreateScript implements createScript operation.
//
// Create a new script.
@ -98,6 +110,18 @@ type Handler interface {
//
// POST /submissions
CreateSubmission(ctx context.Context, req *SubmissionCreate) (*SubmissionID, error)
// CreateSubmissionAuditCheckList implements createSubmissionAuditCheckList operation.
//
// Validator posts a checklist to the audit log.
//
// POST /submissions/{SubmissionID}/checklist
CreateSubmissionAuditCheckList(ctx context.Context, req CheckList, params CreateSubmissionAuditCheckListParams) error
// CreateSubmissionAuditError implements createSubmissionAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /submissions/{SubmissionID}/error
CreateSubmissionAuditError(ctx context.Context, params CreateSubmissionAuditErrorParams) error
// GetScript implements getScript operation.
//
// Get the specified script by ID.

@ -121,6 +121,24 @@ func (UnimplementedHandler) CreateMapfix(ctx context.Context, req *MapfixCreate)
return r, ht.ErrNotImplemented
}
// CreateMapfixAuditCheckList implements createMapfixAuditCheckList operation.
//
// Validator posts a checklist to the audit log.
//
// POST /mapfixes/{MapfixID}/checklist
func (UnimplementedHandler) CreateMapfixAuditCheckList(ctx context.Context, req CheckList, params CreateMapfixAuditCheckListParams) error {
return ht.ErrNotImplemented
}
// CreateMapfixAuditError implements createMapfixAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /mapfixes/{MapfixID}/error
func (UnimplementedHandler) CreateMapfixAuditError(ctx context.Context, params CreateMapfixAuditErrorParams) error {
return ht.ErrNotImplemented
}
// CreateScript implements createScript operation.
//
// Create a new script.
@ -148,6 +166,24 @@ func (UnimplementedHandler) CreateSubmission(ctx context.Context, req *Submissio
return r, ht.ErrNotImplemented
}
// CreateSubmissionAuditCheckList implements createSubmissionAuditCheckList operation.
//
// Validator posts a checklist to the audit log.
//
// POST /submissions/{SubmissionID}/checklist
func (UnimplementedHandler) CreateSubmissionAuditCheckList(ctx context.Context, req CheckList, params CreateSubmissionAuditCheckListParams) error {
return ht.ErrNotImplemented
}
// CreateSubmissionAuditError implements createSubmissionAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /submissions/{SubmissionID}/error
func (UnimplementedHandler) CreateSubmissionAuditError(ctx context.Context, params CreateSubmissionAuditErrorParams) error {
return ht.ErrNotImplemented
}
// GetScript implements getScript operation.
//
// Get the specified script by ID.

@ -3,11 +3,88 @@
package api
import (
"fmt"
"github.com/go-faster/errors"
"github.com/ogen-go/ogen/validate"
)
func (s *Check) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Name)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Name",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 4096,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Summary)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Summary",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s CheckList) Validate() error {
alias := ([]Check)(s)
if alias == nil {
return errors.New("nil is invalid value")
}
var failures []validate.FieldError
for i, elem := range alias {
if err := func() error {
if err := elem.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: fmt.Sprintf("[%d]", i),
Error: err,
})
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *Error) Validate() error {
if s == nil {
return validate.ErrNilPointer

@ -48,6 +48,19 @@ type AuditEventDataError struct {
Error string `json:"error"`
}
type Check struct {
Name string `json:"name"`
Summary string `json:"summary"`
Passed bool `json:"passed"`
}
// Validator map checks details
const AuditEventTypeCheckList AuditEventType = 7
type AuditEventDataCheckList struct {
CheckList []Check `json:"check_list"`
}
type AuditEvent struct {
ID int64 `gorm:"primaryKey"`
CreatedAt time.Time

@ -27,11 +27,13 @@ type CreateMapfixRequest struct {
type CheckSubmissionRequest struct{
SubmissionID int64
ModelID uint64
SkipChecks bool
}
type CheckMapfixRequest struct{
MapfixID int64
ModelID uint64
MapfixID int64
ModelID uint64
SkipChecks bool
}
type ValidateSubmissionRequest struct {

13
pkg/model/resource.go Normal file

@ -0,0 +1,13 @@
package model
type ResourceType int32
const (
ResourceUnknown ResourceType = 0
ResourceMapfix ResourceType = 1
ResourceSubmission ResourceType = 2
)
type Resource struct{
ID int64
Type ResourceType
}

@ -23,13 +23,6 @@ func HashParse(hash string) (uint64, error){
return strconv.ParseUint(hash, 16, 64)
}
type ResourceType int32
const (
ResourceUnknown ResourceType = 0
ResourceMapfix ResourceType = 1
ResourceSubmission ResourceType = 2
)
type Script struct {
ID int64 `gorm:"primaryKey"`
Name string

@ -0,0 +1,72 @@
package roblox
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"io"
)
// Struct equivalent to Rust's AssetLocationInfo
type AssetLocationInfo struct {
Location string `json:"location"`
RequestId string `json:"requestId"`
IsHashDynamic bool `json:"IsHashDynamic"`
IsCopyrightProtected bool `json:"IsCopyrightProtected"`
IsArchived bool `json:"isArchived"`
AssetTypeId uint32 `json:"assetTypeId"`
}
// Input struct for getAssetLocation
type GetAssetLatestRequest struct {
AssetID uint64
}
// Custom error type if needed
type GetError string
func (e GetError) Error() string { return string(e) }
// Example client with a Get method
type Client struct {
HttpClient *http.Client
ApiKey string
}
func (c *Client) GetAssetLocation(config GetAssetLatestRequest) (*AssetLocationInfo, error) {
rawURL := fmt.Sprintf("https://apis.roblox.com/asset-delivery-api/v1/assetId/%d", config.AssetID)
parsedURL, err := url.Parse(rawURL)
if err != nil {
return nil, GetError("ParseError: " + err.Error())
}
req, err := http.NewRequest("GET", parsedURL.String(), nil)
if err != nil {
return nil, GetError("RequestCreationError: " + err.Error())
}
req.Header.Set("x-api-key", c.ApiKey)
resp, err := c.HttpClient.Do(req)
if err != nil {
return nil, GetError("ReqwestError: " + err.Error())
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, GetError(fmt.Sprintf("ResponseError: status code %d", resp.StatusCode))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, GetError("ReadBodyError: " + err.Error())
}
var info AssetLocationInfo
if err := json.Unmarshal(body, &info); err != nil {
return nil, GetError("JSONError: " + err.Error())
}
return &info, nil
}

26
pkg/service/asset.go Normal file

@ -0,0 +1,26 @@
package service
import (
"context"
"strings"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/roblox"
)
// AssetLocation invokes assetLocation operation.
//
// Get location of asset.
//
// GET /asset/{AssetID}/location
func (svc *Service) AssetLocation(ctx context.Context, params api.AssetLocationParams) (ok api.AssetLocationOK, err error) {
info, err := svc.Roblox.GetAssetLocation(roblox.GetAssetLatestRequest{
AssetID: uint64(params.AssetID),
})
if err != nil{
return ok, err
}
ok.Data = strings.NewReader(info.Location)
return ok, nil
}

@ -3,7 +3,6 @@ package service
import (
"context"
"encoding/json"
"io"
"git.itzana.me/strafesnet/go-grpc/users"
"git.itzana.me/strafesnet/maps-service/pkg/api"
@ -11,83 +10,13 @@ import (
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
// CreateMapfixAuditComment implements createMapfixAuditComment operation.
//
// Post a comment to the audit log
//
// POST /mapfixes/{MapfixID}/comment
func (svc *Service) CreateMapfixAuditComment(ctx context.Context, req api.CreateMapfixAuditCommentReq, params api.CreateMapfixAuditCommentParams) (error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleMapfixReview()
if err != nil {
return err
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
if !has_role {
// Submitter has special permission to comment on their mapfix
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
if err != nil {
return err
}
if mapfix.Submitter != userId {
return ErrPermissionDeniedNeedRoleMapfixReview
}
}
data, err := io.ReadAll(req)
if err != nil {
return err
}
event_data := model.AuditEventDataComment{
Comment: string(data),
}
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.AuditEventTypeComment,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ListMapfixAuditEvents invokes listMapfixAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /mapfixes/{MapfixID}/audit-events
func (svc *Service) ListMapfixAuditEvents(ctx context.Context, params api.ListMapfixAuditEventsParams) ([]api.AuditEvent, error) {
func (svc *Service) ListAuditEvents(ctx context.Context, resource model.Resource, page model.Page) ([]api.AuditEvent, error){
filter := datastore.Optional()
filter.Add("resource_type", model.ResourceMapfix)
filter.Add("resource_id", params.MapfixID)
filter.Add("resource_type", resource.Type)
filter.Add("resource_id", resource.ID)
items, err := svc.DB.AuditEvents().List(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
})
items, err := svc.DB.AuditEvents().List(ctx, filter, page)
if err != nil {
return nil, err
}
@ -139,49 +68,8 @@ func (svc *Service) ListMapfixAuditEvents(ctx context.Context, params api.ListMa
return resp, nil
}
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
//
// Post a comment to the audit log
//
// POST /submissions/{SubmissionID}/comment
func (svc *Service) CreateSubmissionAuditComment(ctx context.Context, req api.CreateSubmissionAuditCommentReq, params api.CreateSubmissionAuditCommentParams) (error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionReview()
if err != nil {
return err
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
if !has_role {
// Submitter has special permission to comment on their submission
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil {
return err
}
if submission.Submitter != userId {
return ErrPermissionDeniedNeedRoleSubmissionReview
}
}
data, err := io.ReadAll(req)
if err != nil {
return err
}
event_data := model.AuditEventDataComment{
Comment: string(data),
}
EventData, err := json.Marshal(&event_data)
func (svc *Service) CreateAuditEventAction(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataAction) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
@ -189,8 +77,29 @@ func (svc *Service) CreateSubmissionAuditComment(ctx context.Context, req api.Cr
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventComment(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataComment) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeComment,
EventData: EventData,
})
@ -201,68 +110,23 @@ func (svc *Service) CreateSubmissionAuditComment(ctx context.Context, req api.Cr
return nil
}
// ListSubmissionAuditEvents invokes listSubmissionAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /submissions/{SubmissionID}/audit-events
func (svc *Service) ListSubmissionAuditEvents(ctx context.Context, params api.ListSubmissionAuditEventsParams) ([]api.AuditEvent, error) {
filter := datastore.Optional()
func (svc *Service) CreateAuditEventChangeModel(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataChangeModel) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
filter.Add("resource_type", model.ResourceSubmission)
filter.Add("resource_id", params.SubmissionID)
items, err := svc.DB.AuditEvents().List(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeChangeModel,
EventData: EventData,
})
if err != nil {
return nil, err
return err
}
idMap := make(map[int64]bool)
for _, item := range items {
idMap[int64(item.User)] = true
}
var idList users.IdList
idList.ID = make([]int64,len(idMap))
for userId := range idMap {
idList.ID = append(idList.ID, userId)
}
userList, err := svc.Users.GetList(ctx, &idList)
if err != nil {
return nil, err
}
userMap := make(map[int64]*users.UserResponse)
for _,user := range userList.Users {
userMap[user.ID] = user
}
var resp []api.AuditEvent
for _, item := range items {
EventData := api.AuditEventEventData{}
err = EventData.UnmarshalJSON(item.EventData)
if err != nil {
return nil, err
}
username := ""
if userMap[int64(item.User)] != nil {
username = userMap[int64(item.User)].Username
}
resp = append(resp, api.AuditEvent{
ID: item.ID,
Date: item.CreatedAt.Unix(),
User: int64(item.User),
Username: username,
ResourceType: int32(item.ResourceType),
ResourceID: item.ResourceID,
EventType: int32(item.EventType),
EventData: EventData,
})
}
return resp, nil
return nil
}

@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"time"
"git.itzana.me/strafesnet/go-grpc/maps"
@ -298,24 +299,15 @@ func (svc *Service) UpdateMapfixModel(ctx context.Context, params api.UpdateMapf
NewModelVersion: NewModelVersion,
}
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.AuditEventTypeChangeModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
return svc.CreateAuditEventChangeModel(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixReject invokes actionMapfixReject operation.
@ -356,24 +348,15 @@ func (svc *Service) ActionMapfixReject(ctx context.Context, params api.ActionMap
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixRequestChanges invokes actionMapfixRequestChanges operation.
@ -414,24 +397,15 @@ func (svc *Service) ActionMapfixRequestChanges(ctx context.Context, params api.A
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixRevoke invokes actionMapfixRevoke operation.
@ -475,24 +449,15 @@ func (svc *Service) ActionMapfixRevoke(ctx context.Context, params api.ActionMap
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixTriggerSubmit invokes actionMapfixTriggerSubmit operation.
@ -533,8 +498,9 @@ func (svc *Service) ActionMapfixTriggerSubmit(ctx context.Context, params api.Ac
}
validate_request := model.CheckMapfixRequest{
MapfixID: mapfix.ID,
ModelID: mapfix.AssetID,
MapfixID: mapfix.ID,
ModelID: mapfix.AssetID,
SkipChecks: false,
}
j, err := json.Marshal(validate_request)
@ -551,32 +517,23 @@ func (svc *Service) ActionMapfixTriggerSubmit(ctx context.Context, params api.Ac
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixBypassSubmit invokes actionMapfixBypassSubmit operation.
// ActionMapfixTriggerSubmitUnchecked invokes actionMapfixTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/bypass-submit
func (svc *Service) ActionMapfixBypassSubmit(ctx context.Context, params api.ActionMapfixBypassSubmitParams) error {
// POST /mapfixes/{MapfixID}/status/trigger-submit-unchecked
func (svc *Service) ActionMapfixTriggerSubmitUnchecked(ctx context.Context, params api.ActionMapfixTriggerSubmitUncheckedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
@ -609,7 +566,7 @@ func (svc *Service) ActionMapfixBypassSubmit(ctx context.Context, params api.Act
}
// transaction
target_status := model.MapfixStatusSubmitted
target_status := model.MapfixStatusSubmitting
smap := datastore.Optional()
smap.Add("status_id", target_status)
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusChangesRequested}, smap)
@ -617,28 +574,35 @@ func (svc *Service) ActionMapfixBypassSubmit(ctx context.Context, params api.Act
return err
}
validate_request := model.CheckMapfixRequest{
MapfixID: mapfix.ID,
ModelID: mapfix.AssetID,
SkipChecks: true,
}
j, err := json.Marshal(validate_request)
if err != nil {
return err
}
_, err = svc.Nats.Publish("maptest.mapfixes.check", []byte(j))
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixResetSubmitting implements actionMapfixResetSubmitting operation.
@ -686,24 +650,15 @@ func (svc *Service) ActionMapfixResetSubmitting(ctx context.Context, params api.
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixTriggerUpload invokes actionMapfixTriggerUpload operation.
@ -762,24 +717,15 @@ func (svc *Service) ActionMapfixTriggerUpload(ctx context.Context, params api.Ac
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixValidate invokes actionMapfixValidate operation.
@ -830,24 +776,15 @@ func (svc *Service) ActionMapfixValidated(ctx context.Context, params api.Action
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixTriggerValidate invokes actionMapfixTriggerValidate operation.
@ -939,24 +876,15 @@ func (svc *Service) ActionMapfixTriggerValidate(ctx context.Context, params api.
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixRetryValidate invokes actionMapfixRetryValidate operation.
@ -1019,24 +947,15 @@ func (svc *Service) ActionMapfixRetryValidate(ctx context.Context, params api.Ac
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixAccepted implements actionMapfixAccepted operation.
@ -1087,22 +1006,85 @@ func (svc *Service) ActionMapfixAccepted(ctx context.Context, params api.ActionM
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// CreateMapfixAuditComment implements createMapfixAuditComment operation.
//
// Post a comment to the audit log
//
// POST /mapfixes/{MapfixID}/comment
func (svc *Service) CreateMapfixAuditComment(ctx context.Context, req api.CreateMapfixAuditCommentReq, params api.CreateMapfixAuditCommentParams) (error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleMapfixReview()
if err != nil {
return err
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
if !has_role {
// Submitter has special permission to comment on their mapfix
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
if err != nil {
return err
}
if mapfix.Submitter != userId {
return ErrPermissionDeniedNeedRoleMapfixReview
}
}
data, err := io.ReadAll(req)
if err != nil {
return err
}
event_data := model.AuditEventDataComment{
Comment: string(data),
}
return svc.CreateAuditEventComment(
ctx,
userId,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ListMapfixAuditEvents invokes listMapfixAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /mapfixes/{MapfixID}/audit-events
func (svc *Service) ListMapfixAuditEvents(ctx context.Context, params api.ListMapfixAuditEventsParams) ([]api.AuditEvent, error) {
return svc.ListAuditEvents(
ctx,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
model.Page{
Number: params.Page,
Size: params.Limit,
},
)
}

@ -9,6 +9,7 @@ import (
"git.itzana.me/strafesnet/go-grpc/users"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/roblox"
"github.com/nats-io/nats.go"
)
@ -35,6 +36,7 @@ type Service struct {
Nats nats.JetStreamContext
Maps maps.MapsServiceClient
Users users.UsersServiceClient
Roblox roblox.Client
}
// NewError creates *ErrorStatusCode from error returned by handler.

@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"time"
"git.itzana.me/strafesnet/go-grpc/maps"
@ -365,24 +366,15 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
NewModelVersion: NewModelVersion,
}
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: submission.ID,
EventType: model.AuditEventTypeChangeModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
return svc.CreateAuditEventChangeModel(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionReject invokes actionSubmissionReject operation.
@ -423,24 +415,15 @@ func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.Actio
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
@ -481,24 +464,15 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params a
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionRevoke invokes actionSubmissionRevoke operation.
@ -542,24 +516,15 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionTriggerSubmit invokes actionSubmissionTriggerSubmit operation.
@ -610,6 +575,7 @@ func (svc *Service) ActionSubmissionTriggerSubmit(ctx context.Context, params ap
validate_request := model.CheckSubmissionRequest{
SubmissionID: submission.ID,
ModelID: submission.AssetID,
SkipChecks: false,
}
j, err := json.Marshal(validate_request)
@ -626,32 +592,23 @@ func (svc *Service) ActionSubmissionTriggerSubmit(ctx context.Context, params ap
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionBypassSubmit invokes actionSubmissionBypassSubmit operation.
// ActionSubmissionTriggerSubmitUnchecked invokes actionSubmissionTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/bypass-submit
func (svc *Service) ActionSubmissionBypassSubmit(ctx context.Context, params api.ActionSubmissionBypassSubmitParams) error {
// POST /submissions/{SubmissionID}/status/trigger-submit-unchecked
func (svc *Service) ActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params api.ActionSubmissionTriggerSubmitUncheckedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
@ -684,7 +641,7 @@ func (svc *Service) ActionSubmissionBypassSubmit(ctx context.Context, params api
}
// transaction
target_status := model.SubmissionStatusSubmitted
target_status := model.SubmissionStatusSubmitting
smap := datastore.Optional()
smap.Add("status_id", target_status)
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusChangesRequested}, smap)
@ -692,28 +649,35 @@ func (svc *Service) ActionSubmissionBypassSubmit(ctx context.Context, params api
return err
}
validate_request := model.CheckSubmissionRequest{
SubmissionID: submission.ID,
ModelID: submission.AssetID,
SkipChecks: true,
}
j, err := json.Marshal(validate_request)
if err != nil {
return err
}
_, err = svc.Nats.Publish("maptest.submissions.check", []byte(j))
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionResetSubmitting implements actionSubmissionResetSubmitting operation.
@ -761,24 +725,15 @@ func (svc *Service) ActionSubmissionResetSubmitting(ctx context.Context, params
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation.
@ -844,24 +799,15 @@ func (svc *Service) ActionSubmissionTriggerUpload(ctx context.Context, params ap
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
@ -912,24 +858,15 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params api.Ac
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation.
@ -1004,24 +941,15 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionRetryValidate invokes actionSubmissionRetryValidate operation.
@ -1084,24 +1012,15 @@ func (svc *Service) ActionSubmissionRetryValidate(ctx context.Context, params ap
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
@ -1152,24 +1071,15 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params api.Act
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
return svc.CreateAuditEventAction(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ReleaseSubmissions invokes releaseSubmissions operation.
@ -1239,3 +1149,75 @@ func (svc *Service) ReleaseSubmissions(ctx context.Context, request []api.Releas
return nil
}
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
//
// Post a comment to the audit log
//
// POST /submissions/{SubmissionID}/comment
func (svc *Service) CreateSubmissionAuditComment(ctx context.Context, req api.CreateSubmissionAuditCommentReq, params api.CreateSubmissionAuditCommentParams) (error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionReview()
if err != nil {
return err
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
if !has_role {
// Submitter has special permission to comment on their submission
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil {
return err
}
if submission.Submitter != userId {
return ErrPermissionDeniedNeedRoleSubmissionReview
}
}
data, err := io.ReadAll(req)
if err != nil {
return err
}
event_data := model.AuditEventDataComment{
Comment: string(data),
}
return svc.CreateAuditEventComment(
ctx,
userId,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ListSubmissionAuditEvents invokes listSubmissionAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /submissions/{SubmissionID}/audit-events
func (svc *Service) ListSubmissionAuditEvents(ctx context.Context, params api.ListSubmissionAuditEventsParams) ([]api.AuditEvent, error) {
return svc.ListAuditEvents(
ctx,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
model.Page{
Number: params.Page,
Size: params.Limit,
},
)
}

@ -0,0 +1,92 @@
package service_internal
import (
"context"
"encoding/json"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
func (svc *Service) CreateAuditEventAction(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataAction) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventChangeValidatedModel(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataChangeValidatedModel) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeChangeValidatedModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventError(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataError) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeError,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventCheckList(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataCheckList) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeCheckList,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}

@ -2,7 +2,6 @@ package service_internal
import (
"context"
"encoding/json"
"errors"
"fmt"
@ -54,24 +53,15 @@ func (svc *Service) UpdateMapfixValidatedModel(ctx context.Context, params inter
ValidatedModelVersion: ValidatedModelVersion,
}
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.AuditEventTypeChangeValidatedModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
return svc.CreateAuditEventChangeValidatedModel(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
@ -97,24 +87,15 @@ func (svc *Service) ActionMapfixSubmitted(ctx context.Context, params internal.A
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
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
@ -132,51 +113,19 @@ func (svc *Service) ActionMapfixRequestChanges(ctx context.Context, params inter
return err
}
{
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
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.AuditEventTypeError,
EventData: EventData,
})
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
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixValidate invokes actionMapfixValidate operation.
@ -206,53 +155,20 @@ func (svc *Service) ActionMapfixAccepted(ctx context.Context, params internal.Ac
return err
}
//push an error audit event
{
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
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.AuditEventTypeError,
EventData: EventData,
})
if err != nil {
return err
}
}
// push an action audit event
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
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixUploaded implements actionMapfixUploaded operation.
@ -274,24 +190,66 @@ func (svc *Service) ActionMapfixUploaded(ctx context.Context, params internal.Ac
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// CreateMapfixAuditError implements createMapfixAuditError operation.
//
// Post an error to the audit log
//
// POST /mapfixes/{MapfixID}/error
func (svc *Service) CreateMapfixAuditError(ctx context.Context, params internal.CreateMapfixAuditErrorParams) (error) {
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
_, 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 svc.CreateAuditEventError(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// CreateMapfixAuditCheckList implements createMapfixAuditCheckList operation.
//
// Post a checklist to the audit log
//
// POST /mapfixes/{MapfixID}/checklist
func (svc *Service) CreateMapfixAuditCheckList(ctx context.Context, check_list internal.CheckList, params internal.CreateMapfixAuditCheckListParams) (error) {
check_list2 := make([]model.Check, len(check_list))
for i, check := range check_list {
check_list2[i] = model.Check{
Name: check.Name,
Summary: check.Summary,
Passed: check.Passed,
}
}
return nil
event_data := model.AuditEventDataCheckList{
CheckList: check_list2,
}
return svc.CreateAuditEventCheckList(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// POST /mapfixes

@ -2,7 +2,6 @@ package service_internal
import (
"context"
"encoding/json"
"errors"
"fmt"
@ -54,24 +53,15 @@ func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params i
ValidatedModelVersion: ValidatedModelVersion,
}
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.AuditEventTypeChangeValidatedModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
return svc.CreateAuditEventChangeValidatedModel(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
@ -97,24 +87,15 @@ func (svc *Service) ActionSubmissionSubmitted(ctx context.Context, params intern
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
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
@ -132,53 +113,20 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params i
return err
}
//push an error audit event
{
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
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.AuditEventTypeError,
EventData: EventData,
})
if err != nil {
return err
}
}
// push an action audit event
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
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
@ -200,24 +148,15 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params intern
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
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
@ -235,54 +174,20 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params interna
return err
}
//push an error audit event
{
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
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.AuditEventTypeError,
EventData: EventData,
})
if err != nil {
return err
}
}
// push an action audit event
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
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
@ -305,24 +210,66 @@ func (svc *Service) ActionSubmissionUploaded(ctx context.Context, params interna
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// CreateSubmissionAuditError implements createSubmissionAuditError operation.
//
// Post an error to the audit log
//
// POST /submissions/{SubmissionID}/error
func (svc *Service) CreateSubmissionAuditError(ctx context.Context, params internal.CreateSubmissionAuditErrorParams) (error) {
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
_, 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 svc.CreateAuditEventError(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// CreateSubmissionAuditCheckList implements createSubmissionAuditCheckList operation.
//
// Post a checklist to the audit log
//
// POST /submissions/{SubmissionID}/checklist
func (svc *Service) CreateSubmissionAuditCheckList(ctx context.Context, check_list internal.CheckList, params internal.CreateSubmissionAuditCheckListParams) (error) {
check_list2 := make([]model.Check, len(check_list))
for i, check := range check_list {
check_list2[i] = model.Check{
Name: check.Name,
Summary: check.Summary,
Passed: check.Passed,
}
}
return nil
event_data := model.AuditEventDataCheckList{
CheckList: check_list2,
}
return svc.CreateAuditEventCheckList(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// POST /submissions

@ -7,7 +7,7 @@ edition = "2021"
submissions-api = { path = "api", features = ["internal"], default-features = false, registry = "strafesnet" }
async-nats = "0.41.0"
futures = "0.3.31"
rbx_asset = { version = "0.4.5", registry = "strafesnet" }
rbx_asset = { version = "0.4.6", registry = "strafesnet" }
rbx_binary = "1.0.0"
rbx_dom_weak = "3.0.0"
rbx_reflection_database = "1.0.3"

@ -1,6 +1,6 @@
[package]
name = "submissions-api"
version = "0.7.2"
version = "0.8.1"
edition = "2021"
publish = ["strafesnet"]
repository = "https://git.itzana.me/StrafesNET/maps-service"
@ -11,6 +11,7 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = { version = "0.4.41", features = ["serde"] }
reqwest = { version = "0", features = ["json"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

@ -36,7 +36,7 @@ impl Context{
if let Some(resource_type)=config.ResourceType{
query_pairs.append_pair("ResourceType",(resource_type as i32).to_string().as_str());
}
if let Some(resource_id)=config.ResourceID{
if let Some(ResourceID(resource_id))=config.ResourceID{
query_pairs.append_pair("ResourceID",resource_id.to_string().as_str());
}
}
@ -150,6 +150,84 @@ impl Context{
self.0.delete(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
pub async fn get_submissions(&self,config:GetSubmissionsRequest<'_>)->Result<SubmissionsResponse,Error>{
let url_raw=format!("{}/submissions",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
let mut query_pairs=url.query_pairs_mut();
query_pairs.append_pair("Page",config.Page.to_string().as_str());
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
if let Some(sort)=config.Sort{
query_pairs.append_pair("Sort",(sort as u8).to_string().as_str());
}
if let Some(display_name)=config.DisplayName{
query_pairs.append_pair("DisplayName",display_name);
}
if let Some(creator)=config.Creator{
query_pairs.append_pair("Creator",creator);
}
if let Some(game_id)=config.GameID{
query_pairs.append_pair("GameID",(game_id as u8).to_string().as_str());
}
if let Some(submitter)=config.Submitter{
query_pairs.append_pair("Submitter",submitter.to_string().as_str());
}
if let Some(asset_id)=config.AssetID{
query_pairs.append_pair("AssetID",asset_id.to_string().as_str());
}
if let Some(uploaded_asset_id)=config.UploadedAssetID{
query_pairs.append_pair("UploadedAssetID",uploaded_asset_id.to_string().as_str());
}
if let Some(status_id)=config.StatusID{
query_pairs.append_pair("StatusID",(status_id as u8).to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_maps(&self,config:GetMapsRequest<'_>)->Result<Vec<MapResponse>,Error>{
let url_raw=format!("{}/maps",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
let mut query_pairs=url.query_pairs_mut();
query_pairs.append_pair("Page",config.Page.to_string().as_str());
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
if let Some(sort)=config.Sort{
query_pairs.append_pair("Sort",(sort as u8).to_string().as_str());
}
if let Some(display_name)=config.DisplayName{
query_pairs.append_pair("DisplayName",display_name);
}
if let Some(creator)=config.Creator{
query_pairs.append_pair("Creator",creator);
}
if let Some(game_id)=config.GameID{
query_pairs.append_pair("GameID",(game_id as u8).to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn release_submissions(&self,config:ReleaseRequest<'_>)->Result<(),Error>{
let url_raw=format!("{}/release-submissions",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
let body=serde_json::to_string(config.schedule).map_err(Error::JSON)?;
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
}

@ -66,7 +66,7 @@ impl Context{
if let Some(resource_type)=config.ResourceType{
query_pairs.append_pair("ResourceType",(resource_type as i32).to_string().as_str());
}
if let Some(resource_id)=config.ResourceID{
if let Some(ResourceID(resource_id))=config.ResourceID{
query_pairs.append_pair("ResourceID",resource_id.to_string().as_str());
}
}
@ -161,25 +161,36 @@ impl Context{
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn create_submission_audit_check_list(&self,config:CreateSubmissionAuditCheckListRequest<'_>)->Result<(),Error>{
let url_raw=format!("{}/submissions/{}/checklist",self.0.base_url,config.SubmissionID.0);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
let body=serde_json::to_string(&config.CheckList).map_err(Error::JSON)?;
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
// simple submission endpoints
action!("submissions",action_submission_request_changes,config,ActionSubmissionRequestChangesRequest,"status/validator-request-changes",config.SubmissionID,
("ErrorMessage",config.ErrorMessage.as_str())
);
action!("submissions",action_submission_submitted,config,ActionSubmissionSubmittedRequest,"status/validator-submitted",config.SubmissionID,
action!("submissions",action_submission_request_changes,config,ActionSubmissionRequestChangesRequest,"status/validator-request-changes",config.SubmissionID.0,);
action!("submissions",action_submission_submitted,config,ActionSubmissionSubmittedRequest,"status/validator-submitted",config.SubmissionID.0,
("ModelVersion",config.ModelVersion.to_string().as_str())
("DisplayName",config.DisplayName.as_str())
("Creator",config.Creator.as_str())
("GameID",config.GameID.to_string().as_str())
("GameID",(config.GameID as u8).to_string().as_str())
);
action!("submissions",action_submission_validated,config,SubmissionID,"status/validator-validated",config.0,);
action!("submissions",update_submission_validated_model,config,UpdateSubmissionModelRequest,"validated-model",config.SubmissionID,
action!("submissions",update_submission_validated_model,config,UpdateSubmissionModelRequest,"validated-model",config.SubmissionID.0,
("ValidatedModelID",config.ModelID.to_string().as_str())
("ValidatedModelVersion",config.ModelVersion.to_string().as_str())
);
action!("submissions",action_submission_uploaded,config,ActionSubmissionUploadedRequest,"status/validator-uploaded",config.SubmissionID,
action!("submissions",action_submission_uploaded,config,ActionSubmissionUploadedRequest,"status/validator-uploaded",config.SubmissionID.0,
("UploadedAssetID",config.UploadedAssetID.to_string().as_str())
);
action!("submissions",action_submission_accepted,config,ActionSubmissionAcceptedRequest,"status/validator-failed",config.SubmissionID,
action!("submissions",action_submission_accepted,config,ActionSubmissionAcceptedRequest,"status/validator-failed",config.SubmissionID.0,);
action!("submissions",create_submission_audit_error,config,CreateSubmissionAuditErrorRequest,"error",config.SubmissionID.0,
("ErrorMessage",config.ErrorMessage.as_str())
);
pub async fn create_mapfix(&self,config:CreateMapfixRequest<'_>)->Result<MapfixIDResponse,Error>{
@ -193,27 +204,38 @@ impl Context{
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn create_mapfix_audit_check_list(&self,config:CreateMapfixAuditCheckListRequest<'_>)->Result<(),Error>{
let url_raw=format!("{}/mapfixes/{}/checklist",self.0.base_url,config.MapfixID.0);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
let body=serde_json::to_string(&config.CheckList).map_err(Error::JSON)?;
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
// simple mapfixes endpoints
action!("mapfixes",action_mapfix_request_changes,config,ActionMapfixRequestChangesRequest,"status/validator-request-changes",config.MapfixID,
("ErrorMessage",config.ErrorMessage.as_str())
);
action!("mapfixes",action_mapfix_submitted,config,ActionMapfixSubmittedRequest,"status/validator-submitted",config.MapfixID,
action!("mapfixes",action_mapfix_request_changes,config,ActionMapfixRequestChangesRequest,"status/validator-request-changes",config.MapfixID.0,);
action!("mapfixes",action_mapfix_submitted,config,ActionMapfixSubmittedRequest,"status/validator-submitted",config.MapfixID.0,
("ModelVersion",config.ModelVersion.to_string().as_str())
("DisplayName",config.DisplayName.as_str())
("Creator",config.Creator.as_str())
("GameID",config.GameID.to_string().as_str())
("GameID",(config.GameID as u8).to_string().as_str())
);
action!("mapfixes",action_mapfix_validated,config,MapfixID,"status/validator-validated",config.0,);
action!("mapfixes",update_mapfix_validated_model,config,UpdateMapfixModelRequest,"validated-model",config.MapfixID,
action!("mapfixes",update_mapfix_validated_model,config,UpdateMapfixModelRequest,"validated-model",config.MapfixID.0,
("ValidatedModelID",config.ModelID.to_string().as_str())
("ValidatedModelVersion",config.ModelVersion.to_string().as_str())
);
action!("mapfixes",action_mapfix_uploaded,config,ActionMapfixUploadedRequest,"status/validator-uploaded",config.MapfixID,);
action!("mapfixes",action_mapfix_accepted,config,ActionMapfixAcceptedRequest,"status/validator-failed",config.MapfixID,
("ErrorMessage",config.ErrorMessage.as_str())
);
action!("mapfixes",action_mapfix_uploaded,config,ActionMapfixUploadedRequest,"status/validator-uploaded",config.MapfixID.0,);
action!("mapfixes",action_mapfix_accepted,config,ActionMapfixAcceptedRequest,"status/validator-failed",config.MapfixID.0,);
// simple operation endpoint
action!("operations",action_operation_failed,config,ActionOperationFailedRequest,"status/operation-failed",config.OperationID,
action!("operations",action_operation_failed,config,ActionOperationFailedRequest,"status/operation-failed",config.OperationID.0,
("StatusMessage",config.StatusMessage.as_str())
);
action!("mapfixes",create_mapfix_audit_error,config,CreateMapfixAuditErrorRequest,"error",config.MapfixID.0,
("ErrorMessage",config.ErrorMessage.as_str())
);
}

@ -67,15 +67,23 @@ pub async fn response_ok(response:reqwest::Response)->Result<reqwest::Response,R
}
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq,Ord,PartialOrd,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
#[repr(u8)]
pub enum GameID{
Bhop=1,
Surf=2,
KreedzClimb=3,
FlyTrials=5,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct CreateMapfixRequest<'a>{
pub OperationID:i32,
pub OperationID:OperationID,
pub AssetOwner:i64,
pub DisplayName:&'a str,
pub Creator:&'a str,
pub GameID:i32,
pub GameID:GameID,
pub AssetID:u64,
pub AssetVersion:u64,
pub TargetAssetID:u64,
@ -90,11 +98,11 @@ pub struct MapfixIDResponse{
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct CreateSubmissionRequest<'a>{
pub OperationID:i32,
pub OperationID:OperationID,
pub AssetOwner:i64,
pub DisplayName:&'a str,
pub Creator:&'a str,
pub GameID:i32,
pub GameID:GameID,
pub AssetID:u64,
pub AssetVersion:u64,
pub Status:u32,
@ -137,7 +145,7 @@ pub struct GetScriptsRequest<'a>{
#[serde(skip_serializing_if="Option::is_none")]
pub ResourceType:Option<ResourceType>,
#[serde(skip_serializing_if="Option::is_none")]
pub ResourceID:Option<i64>,
pub ResourceID:Option<ResourceID>,
}
#[derive(Clone,Copy,Debug)]
pub struct HashRequest<'a>{
@ -151,7 +159,7 @@ pub struct ScriptResponse{
pub Hash:String,
pub Source:String,
pub ResourceType:ResourceType,
pub ResourceID:i64,
pub ResourceID:ResourceID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
@ -160,7 +168,7 @@ pub struct CreateScriptRequest<'a>{
pub Source:&'a str,
pub ResourceType:ResourceType,
#[serde(skip_serializing_if="Option::is_none")]
pub ResourceID:Option<i64>,
pub ResourceID:Option<ResourceID>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
@ -230,49 +238,160 @@ pub struct UpdateScriptPolicyRequest{
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct UpdateSubmissionModelRequest{
pub SubmissionID:i64,
pub SubmissionID:SubmissionID,
pub ModelID:u64,
pub ModelVersion:u64,
}
#[derive(Clone,Debug)]
pub enum Sort{
Disabled=0,
DisplayNameAscending=1,
DisplayNameDescending=2,
DateAscending=3,
DateDescending=4,
}
#[derive(Clone,Debug,serde_repr::Deserialize_repr)]
#[repr(u8)]
pub enum SubmissionStatus{
// Phase: Creation
UnderConstruction=0,
ChangesRequested=1,
// Phase: Review
Submitting=2,
Submitted=3,
// Phase: Testing
AcceptedUnvalidated=4, // pending script review, can re-trigger validation
Validating=5,
Validated=6,
Uploading=7,
Uploaded=8, // uploaded to the group, but pending release
// Phase: Final SubmissionStatus
Rejected=9,
Released=10,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct GetSubmissionsRequest<'a>{
pub Page:u32,
pub Limit:u32,
pub Sort:Option<Sort>,
pub DisplayName:Option<&'a str>,
pub Creator:Option<&'a str>,
pub GameID:Option<GameID>,
pub Submitter:Option<u64>,
pub AssetID:Option<u64>,
pub UploadedAssetID:Option<u64>,
pub StatusID:Option<SubmissionStatus>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct SubmissionResponse{
pub ID:SubmissionID,
pub DisplayName:String,
pub Creator:String,
pub GameID:GameID,
pub CreatedAt:i64,
pub UpdatedAt:i64,
pub Submitter:u64,
pub AssetID:u64,
pub AssetVersion:u64,
pub UploadedAssetID:u64,
pub StatusID:SubmissionStatus,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct SubmissionsResponse{
pub Total:u64,
pub Submissions:Vec<SubmissionResponse>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct GetMapsRequest<'a>{
pub Page:u32,
pub Limit:u32,
pub Sort:Option<Sort>,
pub DisplayName:Option<&'a str>,
pub Creator:Option<&'a str>,
pub GameID:Option<GameID>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct MapResponse{
pub ID:i64,
pub DisplayName:String,
pub Creator:String,
pub GameID:GameID,
pub Date:i64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct Check{
pub Name:&'static str,
pub Summary:String,
pub Passed:bool,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionSubmittedRequest{
pub SubmissionID:i64,
pub SubmissionID:SubmissionID,
pub ModelVersion:u64,
pub DisplayName:String,
pub Creator:String,
pub GameID:u32,
pub GameID:GameID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionRequestChangesRequest{
pub SubmissionID:i64,
pub ErrorMessage:String,
pub SubmissionID:SubmissionID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionUploadedRequest{
pub SubmissionID:i64,
pub SubmissionID:SubmissionID,
pub UploadedAssetID:u64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionAcceptedRequest{
pub SubmissionID:i64,
pub SubmissionID:SubmissionID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct CreateSubmissionAuditErrorRequest{
pub SubmissionID:SubmissionID,
pub ErrorMessage:String,
}
#[derive(Clone,Copy,Debug,serde::Deserialize)]
pub struct SubmissionID(pub i64);
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct CreateSubmissionAuditCheckListRequest<'a>{
pub SubmissionID:SubmissionID,
pub CheckList:&'a [Check],
}
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
pub struct SubmissionID(pub(crate)i64);
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct UpdateMapfixModelRequest{
pub MapfixID:i64,
pub MapfixID:MapfixID,
pub ModelID:u64,
pub ModelVersion:u64,
}
@ -280,39 +399,82 @@ pub struct UpdateMapfixModelRequest{
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixSubmittedRequest{
pub MapfixID:i64,
pub MapfixID:MapfixID,
pub ModelVersion:u64,
pub DisplayName:String,
pub Creator:String,
pub GameID:u32,
pub GameID:GameID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixRequestChangesRequest{
pub MapfixID:i64,
pub ErrorMessage:String,
pub MapfixID:MapfixID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixUploadedRequest{
pub MapfixID:i64,
pub MapfixID:MapfixID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixAcceptedRequest{
pub MapfixID:i64,
pub MapfixID:MapfixID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct CreateMapfixAuditErrorRequest{
pub MapfixID:MapfixID,
pub ErrorMessage:String,
}
#[derive(Clone,Copy,Debug,serde::Deserialize)]
pub struct MapfixID(pub i64);
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct CreateMapfixAuditCheckListRequest<'a>{
pub MapfixID:MapfixID,
pub CheckList:&'a [Check],
}
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
pub struct MapfixID(pub(crate)i64);
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionOperationFailedRequest{
pub OperationID:i32,
pub OperationID:OperationID,
pub StatusMessage:String,
}
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
pub struct OperationID(pub(crate)i64);
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
pub struct ResourceID(pub(crate)i64);
#[derive(Clone,Copy,Debug)]
pub enum Resource{
Submission(SubmissionID),
Mapfix(MapfixID),
}
impl Resource{
pub fn split(self)->(ResourceType,ResourceID){
match self{
Resource::Mapfix(MapfixID(mapfix_id))=>(ResourceType::Mapfix,ResourceID(mapfix_id)),
Resource::Submission(SubmissionID(submission_id))=>(ResourceType::Submission,ResourceID(submission_id)),
}
}
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct ReleaseInfo{
pub SubmissionID:SubmissionID,
pub Date:chrono::DateTime<chrono::Utc>,
}
pub struct ReleaseRequest<'a>{
pub schedule:&'a [ReleaseInfo],
}

@ -3,6 +3,7 @@ use crate::download::download_asset_version;
use crate::rbx_util::{get_mapinfo,get_root_instance,read_dom,ReadDomError,GameID,ParseGameIDError,MapInfo,GetRootInstanceError,StringValueError};
use heck::{ToSnakeCase,ToTitleCase};
use submissions_api::types::Check;
#[allow(dead_code)]
#[derive(Debug)]
@ -12,6 +13,7 @@ pub enum Error{
Download(crate::download::Error),
ModelFileDecode(ReadDomError),
GetRootInstance(GetRootInstanceError),
IntoMapInfoOwned(IntoMapInfoOwnedError),
ToJsonValue(serde_json::Error),
}
impl std::fmt::Display for Error{
@ -23,13 +25,15 @@ impl std::error::Error for Error{}
#[allow(nonstandard_style)]
pub struct CheckRequest{
pub ModelID:u64,
ModelID:u64,
SkipChecks:bool,
}
impl From<crate::nats_types::CheckMapfixRequest> for CheckRequest{
fn from(value:crate::nats_types::CheckMapfixRequest)->Self{
Self{
ModelID:value.ModelID,
SkipChecks:value.SkipChecks,
}
}
}
@ -37,6 +41,7 @@ impl From<crate::nats_types::CheckSubmissionRequest> for CheckRequest{
fn from(value:crate::nats_types::CheckSubmissionRequest)->Self{
Self{
ModelID:value.ModelID,
SkipChecks:value.SkipChecks,
}
}
}
@ -382,6 +387,22 @@ pub struct MapInfoOwned{
pub creator:String,
pub game_id:GameID,
}
#[derive(Debug)]
pub enum IntoMapInfoOwnedError{
DisplayName(StringValueError),
Creator(StringValueError),
GameID(ParseGameIDError),
}
impl TryFrom<MapInfo<'_>> for MapInfoOwned{
type Error=IntoMapInfoOwnedError;
fn try_from(value:MapInfo<'_>)->Result<Self,Self::Error>{
Ok(Self{
display_name:value.display_name.map_err(IntoMapInfoOwnedError::DisplayName)?.to_owned(),
creator:value.creator.map_err(IntoMapInfoOwnedError::Creator)?.to_owned(),
game_id:value.game_id.map_err(IntoMapInfoOwnedError::GameID)?,
})
}
}
// Named dummy types for readability
struct Exists;
@ -591,40 +612,31 @@ impl<D:std::fmt::Display> std::fmt::Display for Duplicates<D>{
}
}
#[derive(serde::Serialize)]
struct CheckSummary{
name:&'static str,
summary:String,
passed:bool,
details:serde_json::Value,
}
impl CheckSummary{
const fn passed(name:&'static str)->Self{
Self{
name,
summary:String::new(),
passed:true,
details:serde_json::Value::Null,
macro_rules! passed{
($name:literal)=>{
Check{
Name:$name,
Summary:String::new(),
Passed:true,
}
}
}
macro_rules! summary{
($name:literal,$summary:expr,$details:expr)=>{
CheckSummary{
name:$name,
summary:$summary,
passed:false,
details:serde_json::to_value($details)?,
($name:literal,$summary:expr)=>{
Check{
Name:$name,
Summary:$summary,
Passed:false,
}
};
}
macro_rules! summary_format{
($name:literal,$fmt:literal,$details:expr)=>{
CheckSummary{
name:$name,
summary:format!($fmt),
passed:false,
details:serde_json::to_value($details)?,
($name:literal,$fmt:literal)=>{
Check{
Name:$name,
Summary:format!($fmt),
Passed:false,
}
};
}
@ -634,134 +646,134 @@ macro_rules! summary_format{
impl MapCheck<'_>{
fn itemize(&self)->Result<MapCheckList,serde_json::Error>{
let model_class=match &self.model_class{
StringCheck(Ok(()))=>CheckSummary::passed("ModelClass"),
StringCheck(Err(context))=>summary_format!("ModelClass","Invalid model class: {context}",()),
StringCheck(Ok(()))=>passed!("ModelClass"),
StringCheck(Err(context))=>summary_format!("ModelClass","Invalid model class: {context}"),
};
let model_name=match &self.model_name{
StringCheck(Ok(()))=>CheckSummary::passed("ModelName"),
StringCheck(Err(context))=>summary_format!("ModelName","Model name must have snake_case: {context}",()),
StringCheck(Ok(()))=>passed!("ModelName"),
StringCheck(Err(context))=>summary_format!("ModelName","Model name must have snake_case: {context}"),
};
let display_name=match &self.display_name{
Ok(Ok(StringCheck(Ok(_))))=>CheckSummary::passed("DisplayName"),
Ok(Ok(StringCheck(Err(context))))=>summary_format!("DisplayName","DisplayName must have Title Case: {context}",()),
Ok(Err(context))=>summary_format!("DisplayName","Invalid DisplayName: {context}",()),
Err(StringValueError::ObjectNotFound)=>summary!("DisplayName","Missing DisplayName StringValue".to_owned(),()),
Err(StringValueError::ValueNotSet)=>summary!("DisplayName","DisplayName Value not set".to_owned(),()),
Err(StringValueError::NonStringValue)=>summary!("DisplayName","DisplayName Value is not a String".to_owned(),()),
Ok(Ok(StringCheck(Ok(_))))=>passed!("DisplayName"),
Ok(Ok(StringCheck(Err(context))))=>summary_format!("DisplayName","DisplayName must have Title Case: {context}"),
Ok(Err(context))=>summary_format!("DisplayName","Invalid DisplayName: {context}"),
Err(StringValueError::ObjectNotFound)=>summary!("DisplayName","Missing DisplayName StringValue".to_owned()),
Err(StringValueError::ValueNotSet)=>summary!("DisplayName","DisplayName Value not set".to_owned()),
Err(StringValueError::NonStringValue)=>summary!("DisplayName","DisplayName Value is not a String".to_owned()),
};
let creator=match &self.creator{
Ok(Ok(_))=>CheckSummary::passed("Creator"),
Ok(Err(context))=>summary_format!("Creator","Invalid Creator: {context}",()),
Err(StringValueError::ObjectNotFound)=>summary!("Creator","Missing Creator StringValue".to_owned(),()),
Err(StringValueError::ValueNotSet)=>summary!("Creator","Creator Value not set".to_owned(),()),
Err(StringValueError::NonStringValue)=>summary!("Creator","Creator Value is not a String".to_owned(),()),
Ok(Ok(_))=>passed!("Creator"),
Ok(Err(context))=>summary_format!("Creator","Invalid Creator: {context}"),
Err(StringValueError::ObjectNotFound)=>summary!("Creator","Missing Creator StringValue".to_owned()),
Err(StringValueError::ValueNotSet)=>summary!("Creator","Creator Value not set".to_owned()),
Err(StringValueError::NonStringValue)=>summary!("Creator","Creator Value is not a String".to_owned()),
};
let game_id=match &self.game_id{
Ok(_)=>CheckSummary::passed("GameID"),
Err(ParseGameIDError)=>summary!("GameID","Model name must be prefixed with bhop_ surf_ or flytrials_".to_owned(),()),
Ok(_)=>passed!("GameID"),
Err(ParseGameIDError)=>summary!("GameID","Model name must be prefixed with bhop_ surf_ or flytrials_".to_owned()),
};
let mapstart=match &self.mapstart{
Ok(Exists)=>CheckSummary::passed("MapStart"),
Err(Absent)=>summary_format!("MapStart","Model has no MapStart",()),
Ok(Exists)=>passed!("MapStart"),
Err(Absent)=>summary_format!("MapStart","Model has no MapStart"),
};
let duplicate_start=match &self.mode_start_counts{
DuplicateCheck(Ok(()))=>CheckSummary::passed("DuplicateStart"),
DuplicateCheck(Ok(()))=>passed!("DuplicateStart"),
DuplicateCheck(Err(DuplicateCheckContext(context)))=>{
let context=Separated::new(", ",||context.iter().map(|(&mode_id,names)|
Duplicates::new(ModeElement{zone:Zone::Start,mode_id},names.len())
));
summary_format!("DuplicateStart","Duplicate start zones: {context}",())
summary_format!("DuplicateStart","Duplicate start zones: {context}")
}
};
let (extra_finish,missing_finish)=match &self.mode_finish_counts{
SetDifferenceCheck(Ok(()))=>(CheckSummary::passed("ExtraFinish"),CheckSummary::passed("MissingFinish")),
SetDifferenceCheck(Ok(()))=>(passed!("DanglingFinish"),passed!("MissingFinish")),
SetDifferenceCheck(Err(context))=>(
if context.extra.is_empty(){
CheckSummary::passed("ExtraFinish")
passed!("DanglingFinish")
}else{
let plural=if context.extra.len()==1{"zone"}else{"zones"};
let context=Separated::new(", ",||context.extra.iter().map(|(&mode_id,_names)|
ModeElement{zone:Zone::Finish,mode_id}
));
summary_format!("ExtraFinish","No matching start zone for finish {plural}: {context}",())
summary_format!("DanglingFinish","No matching start zone for finish {plural}: {context}")
},
if context.missing.is_empty(){
CheckSummary::passed("MissingFinish")
passed!("MissingFinish")
}else{
let plural=if context.missing.len()==1{"zone"}else{"zones"};
let context=Separated::new(", ",||context.missing.iter().map(|&mode_id|
ModeElement{zone:Zone::Finish,mode_id}
));
summary_format!("MissingFinish","Missing finish {plural}: {context}",())
summary_format!("MissingFinish","Missing finish {plural}: {context}")
}
),
};
let dangling_anticheat=match &self.mode_anticheat_counts{
SetDifferenceCheck(Ok(()))=>CheckSummary::passed("DanglingAnticheat"),
SetDifferenceCheck(Ok(()))=>passed!("DanglingAnticheat"),
SetDifferenceCheck(Err(context))=>{
if context.extra.is_empty(){
CheckSummary::passed("DanglingAnticheat")
passed!("DanglingAnticheat")
}else{
let plural=if context.extra.len()==1{"zone"}else{"zones"};
let context=Separated::new(", ",||context.extra.iter().map(|(&mode_id,_names)|
ModeElement{zone:Zone::Anticheat,mode_id}
));
summary_format!("DanglingAnticheat","No matching start zone for anticheat {plural}: {context}",())
summary_format!("DanglingAnticheat","No matching start zone for anticheat {plural}: {context}")
}
}
};
let spawn1=match &self.spawn1{
Ok(Exists)=>CheckSummary::passed("Spawn1"),
Err(Absent)=>summary_format!("Spawn1","Model has no Spawn1",()),
Ok(Exists)=>passed!("Spawn1"),
Err(Absent)=>summary_format!("Spawn1","Model has no Spawn1"),
};
let dangling_teleport=match &self.teleport_counts{
SetDifferenceCheck(Ok(()))=>CheckSummary::passed("DanglingTeleport"),
SetDifferenceCheck(Ok(()))=>passed!("DanglingTeleport"),
SetDifferenceCheck(Err(context))=>{
let unique_names:HashSet<_>=context.extra.values().flat_map(|names|names.iter().copied()).collect();
let plural=if unique_names.len()==1{"object"}else{"objects"};
let context=Separated::new(", ",||&unique_names);
summary_format!("DanglingTeleport","No matching Spawn for {plural}: {context}",())
summary_format!("DanglingTeleport","No matching Spawn for {plural}: {context}")
}
};
let duplicate_spawns=match &self.spawn_counts{
DuplicateCheck(Ok(()))=>CheckSummary::passed("DuplicateSpawn"),
DuplicateCheck(Ok(()))=>passed!("DuplicateSpawn"),
DuplicateCheck(Err(DuplicateCheckContext(context)))=>{
let context=Separated::new(", ",||context.iter().map(|(&stage_id,&names)|
Duplicates::new(StageElement{behaviour:StageElementBehaviour::Spawn,stage_id},names as usize)
));
summary_format!("DuplicateSpawn","Duplicate Spawn: {context}",())
summary_format!("DuplicateSpawn","Duplicate Spawn: {context}")
}
};
let (extra_wormhole_in,missing_wormhole_in)=match &self.wormhole_in_counts{
SetDifferenceCheck(Ok(()))=>(CheckSummary::passed("ExtraWormholeIn"),CheckSummary::passed("MissingWormholeIn")),
SetDifferenceCheck(Ok(()))=>(passed!("ExtraWormholeIn"),passed!("MissingWormholeIn")),
SetDifferenceCheck(Err(context))=>(
if context.extra.is_empty(){
CheckSummary::passed("ExtraWormholeIn")
passed!("ExtraWormholeIn")
}else{
let context=Separated::new(", ",||context.extra.iter().map(|(&wormhole_id,_names)|
WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id}
));
summary_format!("ExtraWormholeIn","WormholeIn with no matching WormholeOut: {context}",())
summary_format!("ExtraWormholeIn","WormholeIn with no matching WormholeOut: {context}")
},
if context.missing.is_empty(){
CheckSummary::passed("MissingWormholeIn")
passed!("MissingWormholeIn")
}else{
// This counts WormholeIn objects, but
// flipped logic is easier to understand
let context=Separated::new(", ",||context.missing.iter().map(|&wormhole_id|
WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id}
));
summary_format!("MissingWormholeIn","WormholeOut with no matching WormholeIn: {context}",())
summary_format!("MissingWormholeIn","WormholeOut with no matching WormholeIn: {context}")
}
)
};
let duplicate_wormhole_out=match &self.wormhole_out_counts{
DuplicateCheck(Ok(()))=>CheckSummary::passed("DuplicateWormholeOut"),
DuplicateCheck(Ok(()))=>passed!("DuplicateWormholeOut"),
DuplicateCheck(Err(DuplicateCheckContext(context)))=>{
let context=Separated::new(", ",||context.iter().map(|(&wormhole_id,&names)|
Duplicates::new(WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id},names as usize)
));
summary_format!("DuplicateWormholeOut","Duplicate WormholeOut: {context}",())
summary_format!("DuplicateWormholeOut","Duplicate WormholeOut: {context}")
}
};
Ok(MapCheckList{checks:Box::new([
@ -786,29 +798,17 @@ impl MapCheck<'_>{
}
#[derive(serde::Serialize)]
struct MapCheckList{
checks:Box<[CheckSummary;16]>,
}
impl MapCheckList{
fn summary(&self)->String{
Separated::new("; ",||self.checks.iter().filter_map(|check|
(!check.passed).then_some(check.summary.as_str())
)).to_string()
}
pub struct MapCheckList{
pub checks:Box<[Check;16]>,
}
pub struct Summary{
pub summary:String,
pub json:serde_json::Value,
}
pub struct CheckReportAndVersion{
pub status:Result<MapInfoOwned,Summary>,
pub struct CheckListAndVersion{
pub status:Result<MapInfoOwned,MapCheckList>,
pub version:u64,
}
impl crate::message_handler::MessageHandler{
pub async fn check_inner(&self,check_info:CheckRequest)->Result<CheckReportAndVersion,Error>{
pub async fn check_inner(&self,check_info:CheckRequest)->Result<CheckListAndVersion,Error>{
// discover asset creator and latest version
let info=self.cloud_context.get_asset_info(
rbx_asset::cloud::GetAssetLatestRequest{asset_id:check_info.ModelID}
@ -833,6 +833,17 @@ impl crate::message_handler::MessageHandler{
// extract the root instance
let model_instance=get_root_instance(&dom).map_err(Error::GetRootInstance)?;
// skip checks
if check_info.SkipChecks{
// extract required fields
let map_info=get_mapinfo(&dom,model_instance);
let map_info_owned=map_info.try_into().map_err(Error::IntoMapInfoOwned)?;
let status=Ok(map_info_owned);
// return early
return Ok(CheckReportAndVersion{status,version});
}
// extract information from the model
let model_info=get_model_info(&dom,model_instance);
@ -842,13 +853,10 @@ impl crate::message_handler::MessageHandler{
// check the report, generate an error message if it fails the check
let status=match map_check.result(){
Ok(map_info)=>Ok(map_info),
Err(Ok(summary))=>Err(Summary{
summary:summary.summary(),
json:serde_json::to_value(&summary).map_err(Error::ToJsonValue)?,
}),
Err(Ok(check_list))=>Err(check_list),
Err(Err(e))=>return Err(Error::ToJsonValue(e)),
};
Ok(CheckReportAndVersion{status,version})
Ok(CheckListAndVersion{status,version})
}
}

@ -1,4 +1,4 @@
use crate::check::CheckReportAndVersion;
use crate::check::CheckListAndVersion;
use crate::nats_types::CheckMapfixRequest;
#[allow(dead_code)]
@ -21,20 +21,25 @@ impl crate::message_handler::MessageHandler{
// update the mapfix depending on the result
match check_result{
Ok(CheckReportAndVersion{status:Ok(map_info),version})=>self.api.action_mapfix_submitted(
submissions_api::types::ActionMapfixSubmittedRequest{
MapfixID:mapfix_id,
ModelVersion:version,
DisplayName:map_info.display_name,
Creator:map_info.creator,
GameID:map_info.game_id as u32,
}
).await.map_err(Error::ApiActionMapfixCheck)?,
Ok(CheckListAndVersion{status:Ok(map_info),version})=>{
self.api.action_mapfix_submitted(
submissions_api::types::ActionMapfixSubmittedRequest{
MapfixID:mapfix_id,
ModelVersion:version,
DisplayName:map_info.display_name,
Creator:map_info.creator,
GameID:map_info.game_id.into(),
}
).await.map_err(Error::ApiActionMapfixCheck)?;
// Do not proceed to request changes
return Ok(());
},
// update the mapfix model status to request changes
Ok(CheckReportAndVersion{status:Err(report),..})=>self.api.action_mapfix_request_changes(
submissions_api::types::ActionMapfixRequestChangesRequest{
Ok(CheckListAndVersion{status:Err(check_list),..})=>self.api.create_mapfix_audit_check_list(
submissions_api::types::CreateMapfixAuditCheckListRequest{
MapfixID:mapfix_id,
ErrorMessage:report.summary,
CheckList:check_list.checks.as_slice(),
}
).await.map_err(Error::ApiActionMapfixCheck)?,
// update the mapfix model status to request changes
@ -42,8 +47,8 @@ impl crate::message_handler::MessageHandler{
// log error
println!("[check_mapfix] Error: {e}");
self.api.action_mapfix_request_changes(
submissions_api::types::ActionMapfixRequestChangesRequest{
self.api.create_mapfix_audit_error(
submissions_api::types::CreateMapfixAuditErrorRequest{
MapfixID:mapfix_id,
ErrorMessage:e.to_string(),
}
@ -51,6 +56,12 @@ impl crate::message_handler::MessageHandler{
},
}
self.api.action_mapfix_request_changes(
submissions_api::types::ActionMapfixRequestChangesRequest{
MapfixID:mapfix_id,
}
).await.map_err(Error::ApiActionMapfixCheck)?;
Ok(())
}
}

@ -1,4 +1,4 @@
use crate::check::CheckReportAndVersion;
use crate::check::CheckListAndVersion;
use crate::nats_types::CheckSubmissionRequest;
#[allow(dead_code)]
@ -22,20 +22,25 @@ impl crate::message_handler::MessageHandler{
// update the submission depending on the result
match check_result{
// update the submission model status to submitted
Ok(CheckReportAndVersion{status:Ok(map_info),version})=>self.api.action_submission_submitted(
submissions_api::types::ActionSubmissionSubmittedRequest{
SubmissionID:submission_id,
ModelVersion:version,
DisplayName:map_info.display_name,
Creator:map_info.creator,
GameID:map_info.game_id as u32,
}
).await.map_err(Error::ApiActionSubmissionCheck)?,
Ok(CheckListAndVersion{status:Ok(map_info),version})=>{
self.api.action_submission_submitted(
submissions_api::types::ActionSubmissionSubmittedRequest{
SubmissionID:submission_id,
ModelVersion:version,
DisplayName:map_info.display_name,
Creator:map_info.creator,
GameID:map_info.game_id.into(),
}
).await.map_err(Error::ApiActionSubmissionCheck)?;
// Do not proceed to request changes
return Ok(());
},
// update the submission model status to request changes
Ok(CheckReportAndVersion{status:Err(report),..})=>self.api.action_submission_request_changes(
submissions_api::types::ActionSubmissionRequestChangesRequest{
Ok(CheckListAndVersion{status:Err(check_list),..})=>self.api.create_submission_audit_check_list(
submissions_api::types::CreateSubmissionAuditCheckListRequest{
SubmissionID:submission_id,
ErrorMessage:report.summary,
CheckList:check_list.checks.as_slice(),
}
).await.map_err(Error::ApiActionSubmissionCheck)?,
// update the submission model status to request changes
@ -43,8 +48,8 @@ impl crate::message_handler::MessageHandler{
// log error
println!("[check_submission] Error: {e}");
self.api.action_submission_request_changes(
submissions_api::types::ActionSubmissionRequestChangesRequest{
self.api.create_submission_audit_error(
submissions_api::types::CreateSubmissionAuditErrorRequest{
SubmissionID:submission_id,
ErrorMessage:e.to_string(),
}
@ -52,6 +57,12 @@ impl crate::message_handler::MessageHandler{
},
}
self.api.action_submission_request_changes(
submissions_api::types::ActionSubmissionRequestChangesRequest{
SubmissionID:submission_id,
}
).await.map_err(Error::ApiActionSubmissionCheck)?;
Ok(())
}
}

@ -9,6 +9,8 @@ pub enum Error{
Download(crate::download::Error),
ModelFileDecode(ReadDomError),
GetRootInstance(GetRootInstanceError),
InvalidGamePrefix,
LoadableOnMaptest(rbx_asset::cookie::SetAssetsPermissionsError),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@ -62,11 +64,24 @@ impl crate::message_handler::MessageHandler{
game_id,
}=get_mapinfo(&dom,model_instance);
let game_id=game_id.map_err(|_|Error::InvalidGamePrefix)?;
let universe_id=match &game_id{
GameID::Bhop=>4422715291,
GameID::Surf=>4422716026,
GameID::FlyTrials=>4419912257,
};
let config=rbx_asset::cookie::SetAssetsPermissionsRequest{
universe_id,
asset_ids:&[create_info.ModelID],
};
self.cookie_context.set_assets_permissions(config).await.map_err(Error::LoadableOnMaptest)?;
Ok(CreateResult{
AssetOwner:user_id,
DisplayName:display_name.ok().map(ToOwned::to_owned),
Creator:creator.ok().map(ToOwned::to_owned),
GameID:game_id.ok(),
GameID:Some(game_id),
AssetVersion:asset_version,
})
}

@ -28,7 +28,7 @@ impl crate::message_handler::MessageHandler{
DisplayName:create_request.DisplayName.as_deref().unwrap_or_default(),
Creator:create_request.Creator.as_deref().unwrap_or_default(),
// not great TODO: make this great
GameID:create_request.GameID.unwrap_or(crate::rbx_util::GameID::Bhop) as i32,
GameID:create_request.GameID.unwrap_or(crate::rbx_util::GameID::Bhop).into(),
AssetID:create_info.ModelID,
AssetVersion:create_request.AssetVersion,
TargetAssetID:create_info.TargetAssetID,

@ -42,7 +42,7 @@ impl crate::message_handler::MessageHandler{
AssetOwner:create_request.AssetOwner as i64,
DisplayName:display_name,
Creator:creator,
GameID:game_id as i32,
GameID:game_id.into(),
AssetID:create_info.ModelID,
AssetVersion:create_request.AssetVersion,
Status:create_info.Status,

@ -3,7 +3,6 @@ use futures::StreamExt;
mod rbx_util;
mod message_handler;
mod nats_types;
mod types;
mod download;
mod check;
mod check_mapfix;

@ -1,3 +1,5 @@
use submissions_api::types::{SubmissionID,MapfixID,OperationID};
// These represent the information needed in the nats message
// to perform the operation, not necessarily the over-the-wire format
@ -8,7 +10,7 @@
#[derive(serde::Deserialize)]
pub struct CreateSubmissionRequest{
// operation_id is passed back in the response message
pub OperationID:i32,
pub OperationID:OperationID,
pub ModelID:u64,
pub DisplayName:String,
pub Creator:String,
@ -21,7 +23,7 @@ pub struct CreateSubmissionRequest{
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct CreateMapfixRequest{
pub OperationID:i32,
pub OperationID:OperationID,
pub ModelID:u64,
pub TargetAssetID:u64,
pub Description:String,
@ -30,22 +32,24 @@ pub struct CreateMapfixRequest{
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct CheckSubmissionRequest{
pub SubmissionID:i64,
pub SubmissionID:SubmissionID,
pub ModelID:u64,
pub SkipChecks:bool,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct CheckMapfixRequest{
pub MapfixID:i64,
pub MapfixID:MapfixID,
pub ModelID:u64,
pub SkipChecks:bool,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct ValidateSubmissionRequest{
// submission_id is passed back in the response message
pub SubmissionID:i64,
pub SubmissionID:SubmissionID,
pub ModelID:u64,
pub ModelVersion:u64,
pub ValidatedModelID:Option<u64>,
@ -55,7 +59,7 @@ pub struct ValidateSubmissionRequest{
#[derive(serde::Deserialize)]
pub struct ValidateMapfixRequest{
// submission_id is passed back in the response message
pub MapfixID:i64,
pub MapfixID:MapfixID,
pub ModelID:u64,
pub ModelVersion:u64,
pub ValidatedModelID:Option<u64>,
@ -65,7 +69,7 @@ pub struct ValidateMapfixRequest{
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct UploadSubmissionRequest{
pub SubmissionID:i64,
pub SubmissionID:SubmissionID,
pub ModelID:u64,
pub ModelVersion:u64,
pub ModelName:String,
@ -74,7 +78,7 @@ pub struct UploadSubmissionRequest{
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct UploadMapfixRequest{
pub MapfixID:i64,
pub MapfixID:MapfixID,
pub ModelID:u64,
pub ModelVersion:u64,
pub TargetAssetID:u64,

@ -37,6 +37,15 @@ pub enum GameID{
Surf=2,
FlyTrials=5,
}
impl From<GameID> for submissions_api::types::GameID{
fn from(value:GameID)->Self{
match value{
GameID::Bhop=>submissions_api::types::GameID::Bhop,
GameID::Surf=>submissions_api::types::GameID::Surf,
GameID::FlyTrials=>submissions_api::types::GameID::FlyTrials,
}
}
}
#[derive(Debug)]
pub struct ParseGameIDError;
impl std::str::FromStr for GameID{

@ -1,4 +0,0 @@
pub enum ResourceID{
Mapfix(i64),
Submission(i64),
}

@ -22,17 +22,23 @@ impl crate::message_handler::MessageHandler{
Ok(())=>{
// update the mapfix model status to validated
self.api.action_mapfix_validated(
submissions_api::types::MapfixID(mapfix_id)
mapfix_id
).await.map_err(Error::ApiActionMapfixValidate)?;
},
Err(e)=>{
// log error
println!("[validate_mapfix] Error: {e}");
self.api.create_mapfix_audit_error(
submissions_api::types::CreateMapfixAuditErrorRequest{
MapfixID:mapfix_id,
ErrorMessage:e.to_string(),
}
).await.map_err(Error::ApiActionMapfixValidate)?;
// update the mapfix model status to accepted
self.api.action_mapfix_accepted(submissions_api::types::ActionMapfixAcceptedRequest{
MapfixID:mapfix_id,
ErrorMessage:e.to_string(),
}).await.map_err(Error::ApiActionMapfixValidate)?;
},
}

@ -22,17 +22,23 @@ impl crate::message_handler::MessageHandler{
Ok(())=>{
// update the submission model status to validated
self.api.action_submission_validated(
submissions_api::types::SubmissionID(submission_id)
submission_id
).await.map_err(Error::ApiActionSubmissionValidate)?;
},
Err(e)=>{
// log error
println!("[validate_submission] Error: {e}");
self.api.create_submission_audit_error(
submissions_api::types::CreateSubmissionAuditErrorRequest{
SubmissionID:submission_id,
ErrorMessage:e.to_string(),
}
).await.map_err(Error::ApiActionSubmissionValidate)?;
// update the submission model status to accepted
self.api.action_submission_accepted(submissions_api::types::ActionSubmissionAcceptedRequest{
SubmissionID:submission_id,
ErrorMessage:e.to_string(),
}).await.map_err(Error::ApiActionSubmissionValidate)?;
},
}

@ -1,9 +1,8 @@
use futures::TryStreamExt;
use submissions_api::types::ResourceType;
use submissions_api::types::Resource;
use crate::download::download_asset_version;
use crate::rbx_util::{read_dom,static_ustr,ReadDomError};
use crate::types::ResourceID;
const SCRIPT_CONCURRENCY:usize=16;
@ -52,6 +51,7 @@ pub enum Error{
ApiGetScriptFromHash(submissions_api::types::ScriptSingleItemError),
ApiUpdateMapfixModel(submissions_api::Error),
ApiUpdateSubmissionModel(submissions_api::Error),
LoadableOnMaptest(rbx_asset::cookie::SetAssetsPermissionsError),
ModelFileRootMustHaveOneChild,
ModelFileChildRefIsNil,
ModelFileEncode(rbx_binary::EncodeError),
@ -70,7 +70,7 @@ pub struct ValidateRequest{
pub ModelID:u64,
pub ModelVersion:u64,
pub ValidatedModelID:Option<u64>,
pub ResourceID:ResourceID,
pub Resource:Resource,
}
impl From<crate::nats_types::ValidateMapfixRequest> for ValidateRequest{
@ -79,7 +79,7 @@ impl From<crate::nats_types::ValidateMapfixRequest> for ValidateRequest{
ModelID:value.ModelID,
ModelVersion:value.ModelVersion,
ValidatedModelID:value.ValidatedModelID,
ResourceID:ResourceID::Mapfix(value.MapfixID),
Resource:Resource::Mapfix(value.MapfixID),
}
}
}
@ -89,7 +89,7 @@ impl From<crate::nats_types::ValidateSubmissionRequest> for ValidateRequest{
ModelID:value.ModelID,
ModelVersion:value.ModelVersion,
ValidatedModelID:value.ValidatedModelID,
ResourceID:ResourceID::Submission(value.SubmissionID),
Resource:Resource::Submission(value.SubmissionID),
}
}
}
@ -177,10 +177,7 @@ impl crate::message_handler::MessageHandler{
},
};
}else{
let (resource_type,resource_id)=match validate_info.ResourceID{
ResourceID::Mapfix(mapfix_id)=>(ResourceType::Mapfix,mapfix_id),
ResourceID::Submission(submission_id)=>(ResourceType::Submission,submission_id),
};
let (resource_type,resource_id)=validate_info.Resource.split();
// upload the script
let script=self.api.create_script(submissions_api::types::CreateScriptRequest{
@ -281,8 +278,8 @@ impl crate::message_handler::MessageHandler{
(validate_info.ModelID,validate_info.ModelVersion)
};
match validate_info.ResourceID{
ResourceID::Mapfix(mapfix_id)=>{
match validate_info.Resource{
Resource::Mapfix(mapfix_id)=>{
// update the mapfix to use the validated model
self.api.update_mapfix_validated_model(submissions_api::types::UpdateMapfixModelRequest{
MapfixID:mapfix_id,
@ -290,7 +287,7 @@ impl crate::message_handler::MessageHandler{
ModelVersion:validated_model_version,
}).await.map_err(Error::ApiUpdateMapfixModel)?;
},
ResourceID::Submission(submission_id)=>{
Resource::Submission(submission_id)=>{
// update the submission to use the validated model
self.api.update_submission_validated_model(submissions_api::types::UpdateSubmissionModelRequest{
SubmissionID:submission_id,
@ -300,6 +297,14 @@ impl crate::message_handler::MessageHandler{
},
}
// Map Staging
let universe_id=7895115682;
let config=rbx_asset::cookie::SetAssetsPermissionsRequest{
universe_id,
asset_ids:&[validated_model_id],
};
self.cookie_context.set_assets_permissions(config).await.map_err(Error::LoadableOnMaptest)?;
Ok(())
}
}

@ -0,0 +1,49 @@
import React, { useState } from 'react';
import { Button } from '@mui/material';
import BugReportIcon from '@mui/icons-material/BugReport';
interface DownloadButtonProps {
assetId: number;
assetName: string; // Used for a more readable file name
}
const DownloadButton: React.FC<DownloadButtonProps> = ({ assetId, assetName }) => {
const [downloading, setDownloading] = useState(false);
const handleDownload = async () => {
setDownloading(true);
try {
const res = await fetch(`/asset/${assetId}/location`);
if (!res.ok) throw new Error('Failed to fetch download location');
const location = await res.text();
const link = document.createElement('a');
link.href = location;
link.download = `${assetName}.rbxm`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (err) {
console.error('Download error:', err);
} finally {
setDownloading(false);
}
};
return (
<Button
fullWidth
variant="contained"
color="primary"
startIcon={<BugReportIcon />}
size="large"
onClick={handleDownload}
disabled={downloading}
>
{downloading ? 'Downloading...' : 'Download'}
</Button>
);
};
export default DownloadButton;

@ -20,7 +20,7 @@ interface ReviewButtonsProps {
const ReviewActions = {
Submit: {name:"Submit",action:"trigger-submit"} as ReviewAction,
BypassSubmit: {name:"Bypass Submit", action:"bypass-submit"} as ReviewAction,
SubmitUnchecked: {name:"Submit Unchecked", action:"trigger-submit-unchecked"} as ReviewAction,
ResetSubmitting: {name:"Reset Submitting",action:"reset-submitting"} as ReviewAction,
Revoke: {name:"Revoke",action:"revoke"} as ReviewAction,
Accept: {name:"Accept",action:"trigger-validate"} as ReviewAction,
@ -109,7 +109,7 @@ const ReviewButtons: React.FC<ReviewButtonsProps> = ({
if (status === Status.ChangesRequested) {
buttons.push({
action: ReviewActions.BypassSubmit,
action: ReviewActions.SubmitUnchecked,
color: "warning"
});
}
@ -152,4 +152,4 @@ const ReviewButtons: React.FC<ReviewButtonsProps> = ({
);
};
export default ReviewButtons;
export default ReviewButtons;

@ -30,6 +30,8 @@ import PersonIcon from "@mui/icons-material/Person";
import FlagIcon from "@mui/icons-material/Flag";
import BugReportIcon from "@mui/icons-material/BugReport";
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
import DownloadButton from "@/app/_components/downloadButton";
import { hasRole, RolesConstants } from "@/app/ts/Roles";
export default function MapDetails() {
const { mapId } = useParams();
@ -38,6 +40,7 @@ export default function MapDetails() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [copySuccess, setCopySuccess] = useState(false);
const [roles, setRoles] = useState(RolesConstants.Empty);
useEffect(() => {
async function getMap() {
@ -60,6 +63,25 @@ export default function MapDetails() {
getMap();
}, [mapId]);
useEffect(() => {
async function getRoles() {
try {
const rolesResponse = await fetch("/api/session/roles");
if (rolesResponse.ok) {
const rolesData = await rolesResponse.json();
setRoles(rolesData.Roles);
} else {
console.warn(`Failed to fetch roles: ${rolesResponse.status}`);
setRoles(RolesConstants.Empty);
}
} catch (error) {
console.warn("Error fetching roles data:", error);
setRoles(RolesConstants.Empty);
}
}
getRoles()
}, [mapId]);
const formatDate = (timestamp: number) => {
return new Date(timestamp * 1000).toLocaleDateString('en-US', {
year: 'numeric',
@ -315,6 +337,9 @@ export default function MapDetails() {
>
Submit a Mapfix
</Button>
{hasRole(roles,RolesConstants.MapDownload) && (
<DownloadButton assetId={map.ID} assetName={map.DisplayName} />
)}
</Paper>
</Grid>
</Grid>
@ -335,4 +360,4 @@ export default function MapDetails() {
</Container>
</Webpage>
);
}
}

@ -9,6 +9,7 @@ export const enum AuditEventType {
ChangeDisplayName = 4,
ChangeCreator = 5,
Error = 6,
CheckList = 7,
}
// Discriminated union types for each event
@ -19,7 +20,8 @@ export type AuditEventData =
| { EventType: AuditEventType.ChangeValidatedModel; EventData: AuditEventDataChangeValidatedModel; }
| { EventType: AuditEventType.ChangeDisplayName; EventData: AuditEventDataChangeName; }
| { EventType: AuditEventType.ChangeCreator; EventData: AuditEventDataChangeName; }
| { EventType: AuditEventType.Error; EventData: AuditEventDataError };
| { EventType: AuditEventType.Error; EventData: AuditEventDataError; }
| { EventType: AuditEventType.CheckList; EventData: AuditEventDataCheckList; };
// Concrete data interfaces
export interface AuditEventDataAction {
@ -51,6 +53,15 @@ export interface AuditEventDataError {
error: string;
}
export interface AuditEventDataCheck {
name: string
summary: string
passed: boolean
}
export interface AuditEventDataCheckList {
check_list: [AuditEventDataCheck];
}
// Full audit event type (mirroring the Go struct)
export interface AuditEvent {
Id: number;
@ -87,6 +98,13 @@ export function decodeAuditEvent(event: AuditEvent): string {
}case AuditEventType.Error:{
const data = event.EventData as AuditEventDataError;
return `Error: ${data.error}`;
}case AuditEventType.CheckList:{
const data = event.EventData as AuditEventDataCheckList;
const failedSummaries = data.check_list
.filter(check => !check.passed)
.map(check => check.summary)
.join('; ');
return `CheckList: ${failedSummaries}`;
}
default:
throw new Error(`Unknown EventType: ${event.EventType}`);