Compare commits
1 Commits
feature-sc
...
hide-reset
| Author | SHA1 | Date | |
|---|---|---|---|
|
c3f497be48
|
662
Cargo.lock
generated
662
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@ Prerequisite: bun installed
|
||||
The environment variables `API_HOST` and `AUTH_HOST` will need to be set for the middleware.
|
||||
Example `.env` in web's root:
|
||||
```
|
||||
API_HOST="http://localhost:8082/"
|
||||
API_HOST="http://localhost:8082/v1/"
|
||||
AUTH_HOST="http://localhost:8083/"
|
||||
```
|
||||
|
||||
|
||||
@@ -33,8 +33,6 @@ services:
|
||||
"--auth-rpc-host","authrpc:8081",
|
||||
"--data-rpc-host","dataservice:9000",
|
||||
]
|
||||
env_file:
|
||||
- ../auth-compose/strafesnet_staging.env
|
||||
depends_on:
|
||||
- authrpc
|
||||
- nats
|
||||
|
||||
@@ -65,53 +65,6 @@ 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
|
||||
@@ -163,6 +116,13 @@ 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
|
||||
@@ -197,6 +157,13 @@ 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
|
||||
@@ -303,53 +270,6 @@ 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
|
||||
@@ -401,6 +321,13 @@ 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
|
||||
@@ -435,6 +362,13 @@ 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
|
||||
@@ -913,25 +847,6 @@ 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
|
||||
|
||||
130
openapi.yaml
130
openapi.yaml
@@ -114,13 +114,6 @@ paths:
|
||||
format: int32
|
||||
minimum: 0
|
||||
maximum: 4
|
||||
description: >
|
||||
Sort order:
|
||||
* `0` - Disabled
|
||||
* `1` - DisplayNameAscending
|
||||
* `2` - DisplayNameDescending
|
||||
* `3` - DateAscending
|
||||
* `4` - DateDescending
|
||||
responses:
|
||||
"200":
|
||||
description: Successful response
|
||||
@@ -158,34 +151,6 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
/maps/{MapID}/location:
|
||||
get:
|
||||
summary: Get location of asset
|
||||
operationId: getMapAssetLocation
|
||||
tags:
|
||||
- Maps
|
||||
parameters:
|
||||
- name: MapID
|
||||
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"
|
||||
/mapfixes:
|
||||
get:
|
||||
summary: Get list of mapfixes
|
||||
@@ -213,11 +178,6 @@ paths:
|
||||
format: int32
|
||||
minimum: 1
|
||||
maximum: 5
|
||||
description: >
|
||||
Game ID:
|
||||
* `1` - Bhop
|
||||
* `2` - Surf
|
||||
* `5` - FlyTrials
|
||||
- name: Sort
|
||||
in: query
|
||||
schema:
|
||||
@@ -225,13 +185,6 @@ paths:
|
||||
format: int32
|
||||
minimum: 0
|
||||
maximum: 4
|
||||
description: >
|
||||
Sort order:
|
||||
* `0` - Disabled
|
||||
* `1` - DisplayNameAscending
|
||||
* `2` - DisplayNameDescending
|
||||
* `3` - DateAscending
|
||||
* `4` - DateDescending
|
||||
- name: Submitter
|
||||
in: query
|
||||
schema:
|
||||
@@ -257,24 +210,6 @@ paths:
|
||||
format: int32
|
||||
minimum: 0
|
||||
maximum: 9
|
||||
description: >
|
||||
// Phase: Creation
|
||||
* `0` - UnderConstruction
|
||||
* `1` - ChangesRequested
|
||||
|
||||
// Phase: Review
|
||||
* `2` - Submitting
|
||||
* `3` - Submitted
|
||||
|
||||
// Phase: Testing
|
||||
* `4` - AcceptedUnvalidated // pending script review, can re-trigger validation
|
||||
* `5` - Validating
|
||||
* `6` - Validated
|
||||
* `7` - Uploading
|
||||
|
||||
// Phase: Final MapfixStatus
|
||||
* `8` - Uploaded // uploaded to the group, but pending release
|
||||
* `9` - Rejected
|
||||
responses:
|
||||
"200":
|
||||
description: Successful response
|
||||
@@ -449,23 +384,6 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
/mapfixes/{MapfixID}/status/trigger-submit-unchecked:
|
||||
post:
|
||||
summary: Role Reviewer changes status from ChangesRequested -> Submitting
|
||||
operationId: actionMapfixTriggerSubmitUnchecked
|
||||
tags:
|
||||
- Mapfixes
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/MapfixID'
|
||||
responses:
|
||||
"204":
|
||||
description: Successful response
|
||||
default:
|
||||
description: General Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
/mapfixes/{MapfixID}/status/reset-submitting:
|
||||
post:
|
||||
summary: Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction
|
||||
@@ -667,11 +585,6 @@ paths:
|
||||
format: int32
|
||||
minimum: 1
|
||||
maximum: 5
|
||||
description: >
|
||||
Game ID:
|
||||
* `1` - Bhop
|
||||
* `2` - Surf
|
||||
* `5` - FlyTrials
|
||||
- name: Sort
|
||||
in: query
|
||||
schema:
|
||||
@@ -679,13 +592,6 @@ paths:
|
||||
format: int32
|
||||
minimum: 0
|
||||
maximum: 4
|
||||
description: >
|
||||
Sort order:
|
||||
* `0` - Disabled
|
||||
* `1` - DisplayNameAscending
|
||||
* `2` - DisplayNameDescending
|
||||
* `3` - DateAscending
|
||||
* `4` - DateDescending
|
||||
- name: Submitter
|
||||
in: query
|
||||
schema:
|
||||
@@ -711,25 +617,6 @@ paths:
|
||||
format: int32
|
||||
minimum: 0
|
||||
maximum: 10
|
||||
description: >
|
||||
// Phase: Creation
|
||||
* `0` - UnderConstruction
|
||||
* `1` - ChangesRequested
|
||||
|
||||
// Phase: Review
|
||||
* `2` - Submitting
|
||||
* `3` - Submitted
|
||||
|
||||
// Phase: Testing
|
||||
* `4` - AcceptedUnvalidated // pending script review, can re-trigger validation
|
||||
* `5` - Validating
|
||||
* `6` - Validated
|
||||
* `7` - Uploading
|
||||
* `8` - Uploaded // uploaded to the group, but pending release
|
||||
|
||||
// Phase: Final SubmissionStatus
|
||||
* `9` - Rejected
|
||||
* `10` - Released
|
||||
responses:
|
||||
"200":
|
||||
description: Successful response
|
||||
@@ -929,23 +816,6 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
/submissions/{SubmissionID}/status/trigger-submit-unchecked:
|
||||
post:
|
||||
summary: Role Reviewer changes status from ChangesRequested -> Submitting
|
||||
operationId: actionSubmissionTriggerSubmitUnchecked
|
||||
tags:
|
||||
- Submissions
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/SubmissionID'
|
||||
responses:
|
||||
"204":
|
||||
description: Successful response
|
||||
default:
|
||||
description: General Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
/submissions/{SubmissionID}/status/reset-submitting:
|
||||
post:
|
||||
summary: Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction
|
||||
|
||||
@@ -72,12 +72,6 @@ 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.
|
||||
@@ -139,12 +133,6 @@ 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.
|
||||
@@ -223,12 +211,6 @@ type Invoker interface {
|
||||
//
|
||||
// GET /maps/{MapID}
|
||||
GetMap(ctx context.Context, params GetMapParams) (*Map, error)
|
||||
// GetMapAssetLocation invokes getMapAssetLocation operation.
|
||||
//
|
||||
// Get location of asset.
|
||||
//
|
||||
// GET /maps/{MapID}/location
|
||||
GetMapAssetLocation(ctx context.Context, params GetMapAssetLocationParams) (GetMapAssetLocationOK, error)
|
||||
// GetMapfix invokes getMapfix operation.
|
||||
//
|
||||
// Retrieve map with ID.
|
||||
@@ -1281,130 +1263,6 @@ 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.
|
||||
@@ -2646,130 +2504,6 @@ 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.
|
||||
@@ -4272,130 +4006,6 @@ func (c *Client) sendGetMap(ctx context.Context, params GetMapParams) (res *Map,
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetMapAssetLocation invokes getMapAssetLocation operation.
|
||||
//
|
||||
// Get location of asset.
|
||||
//
|
||||
// GET /maps/{MapID}/location
|
||||
func (c *Client) GetMapAssetLocation(ctx context.Context, params GetMapAssetLocationParams) (GetMapAssetLocationOK, error) {
|
||||
res, err := c.sendGetMapAssetLocation(ctx, params)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (c *Client) sendGetMapAssetLocation(ctx context.Context, params GetMapAssetLocationParams) (res GetMapAssetLocationOK, err error) {
|
||||
otelAttrs := []attribute.KeyValue{
|
||||
otelogen.OperationID("getMapAssetLocation"),
|
||||
semconv.HTTPRequestMethodKey.String("GET"),
|
||||
semconv.HTTPRouteKey.String("/maps/{MapID}/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, GetMapAssetLocationOperation,
|
||||
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] = "/maps/"
|
||||
{
|
||||
// Encode "MapID" parameter.
|
||||
e := uri.NewPathEncoder(uri.PathEncoderConfig{
|
||||
Param: "MapID",
|
||||
Style: uri.PathStyleSimple,
|
||||
Explode: false,
|
||||
})
|
||||
if err := func() error {
|
||||
return e.EncodeValue(conv.Int64ToString(params.MapID))
|
||||
}(); 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, GetMapAssetLocationOperation, 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 := decodeGetMapAssetLocationResponse(resp)
|
||||
if err != nil {
|
||||
return res, errors.Wrap(err, "decode response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetMapfix invokes getMapfix operation.
|
||||
//
|
||||
// Retrieve map with ID.
|
||||
|
||||
@@ -1396,201 +1396,6 @@ func (s *Server) handleActionMapfixTriggerSubmitRequest(args [1]string, argsEsca
|
||||
}
|
||||
}
|
||||
|
||||
// handleActionMapfixTriggerSubmitUncheckedRequest handles actionMapfixTriggerSubmitUnchecked operation.
|
||||
//
|
||||
// Role Reviewer changes status from ChangesRequested -> Submitting.
|
||||
//
|
||||
// POST /mapfixes/{MapfixID}/status/trigger-submit-unchecked
|
||||
func (s *Server) handleActionMapfixTriggerSubmitUncheckedRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
|
||||
statusWriter := &codeRecorder{ResponseWriter: w}
|
||||
w = statusWriter
|
||||
otelAttrs := []attribute.KeyValue{
|
||||
otelogen.OperationID("actionMapfixTriggerSubmitUnchecked"),
|
||||
semconv.HTTPRequestMethodKey.String("POST"),
|
||||
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/trigger-submit-unchecked"),
|
||||
}
|
||||
|
||||
// Start a span for this request.
|
||||
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixTriggerSubmitUncheckedOperation,
|
||||
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: ActionMapfixTriggerSubmitUncheckedOperation,
|
||||
ID: "actionMapfixTriggerSubmitUnchecked",
|
||||
}
|
||||
)
|
||||
{
|
||||
type bitset = [1]uint8
|
||||
var satisfied bitset
|
||||
{
|
||||
sctx, ok, err := s.securityCookieAuth(ctx, ActionMapfixTriggerSubmitUncheckedOperation, r)
|
||||
if err != nil {
|
||||
err = &ogenerrors.SecurityError{
|
||||
OperationContext: opErrContext,
|
||||
Security: "CookieAuth",
|
||||
Err: err,
|
||||
}
|
||||
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
|
||||
defer recordError("Security:CookieAuth", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
satisfied[0] |= 1 << 0
|
||||
ctx = sctx
|
||||
}
|
||||
}
|
||||
|
||||
if ok := func() bool {
|
||||
nextRequirement:
|
||||
for _, requirement := range []bitset{
|
||||
{0b00000001},
|
||||
} {
|
||||
for i, mask := range requirement {
|
||||
if satisfied[i]&mask != mask {
|
||||
continue nextRequirement
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}(); !ok {
|
||||
err = &ogenerrors.SecurityError{
|
||||
OperationContext: opErrContext,
|
||||
Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied,
|
||||
}
|
||||
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
|
||||
defer recordError("Security", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
params, err := decodeActionMapfixTriggerSubmitUncheckedParams(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 *ActionMapfixTriggerSubmitUncheckedNoContent
|
||||
if m := s.cfg.Middleware; m != nil {
|
||||
mreq := middleware.Request{
|
||||
Context: ctx,
|
||||
OperationName: ActionMapfixTriggerSubmitUncheckedOperation,
|
||||
OperationSummary: "Role Reviewer changes status from ChangesRequested -> Submitting",
|
||||
OperationID: "actionMapfixTriggerSubmitUnchecked",
|
||||
Body: nil,
|
||||
Params: middleware.Parameters{
|
||||
{
|
||||
Name: "MapfixID",
|
||||
In: "path",
|
||||
}: params.MapfixID,
|
||||
},
|
||||
Raw: r,
|
||||
}
|
||||
|
||||
type (
|
||||
Request = struct{}
|
||||
Params = ActionMapfixTriggerSubmitUncheckedParams
|
||||
Response = *ActionMapfixTriggerSubmitUncheckedNoContent
|
||||
)
|
||||
response, err = middleware.HookMiddleware[
|
||||
Request,
|
||||
Params,
|
||||
Response,
|
||||
](
|
||||
m,
|
||||
mreq,
|
||||
unpackActionMapfixTriggerSubmitUncheckedParams,
|
||||
func(ctx context.Context, request Request, params Params) (response Response, err error) {
|
||||
err = s.h.ActionMapfixTriggerSubmitUnchecked(ctx, params)
|
||||
return response, err
|
||||
},
|
||||
)
|
||||
} else {
|
||||
err = s.h.ActionMapfixTriggerSubmitUnchecked(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 := encodeActionMapfixTriggerSubmitUncheckedResponse(response, w, span); err != nil {
|
||||
defer recordError("EncodeResponse", err)
|
||||
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
|
||||
s.cfg.ErrorHandler(ctx, w, r, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// handleActionMapfixTriggerUploadRequest handles actionMapfixTriggerUpload operation.
|
||||
//
|
||||
// Role Admin changes status from Validated -> Uploading.
|
||||
@@ -3542,201 +3347,6 @@ func (s *Server) handleActionSubmissionTriggerSubmitRequest(args [1]string, args
|
||||
}
|
||||
}
|
||||
|
||||
// handleActionSubmissionTriggerSubmitUncheckedRequest handles actionSubmissionTriggerSubmitUnchecked operation.
|
||||
//
|
||||
// Role Reviewer changes status from ChangesRequested -> Submitting.
|
||||
//
|
||||
// POST /submissions/{SubmissionID}/status/trigger-submit-unchecked
|
||||
func (s *Server) handleActionSubmissionTriggerSubmitUncheckedRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
|
||||
statusWriter := &codeRecorder{ResponseWriter: w}
|
||||
w = statusWriter
|
||||
otelAttrs := []attribute.KeyValue{
|
||||
otelogen.OperationID("actionSubmissionTriggerSubmitUnchecked"),
|
||||
semconv.HTTPRequestMethodKey.String("POST"),
|
||||
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/trigger-submit-unchecked"),
|
||||
}
|
||||
|
||||
// Start a span for this request.
|
||||
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionTriggerSubmitUncheckedOperation,
|
||||
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: ActionSubmissionTriggerSubmitUncheckedOperation,
|
||||
ID: "actionSubmissionTriggerSubmitUnchecked",
|
||||
}
|
||||
)
|
||||
{
|
||||
type bitset = [1]uint8
|
||||
var satisfied bitset
|
||||
{
|
||||
sctx, ok, err := s.securityCookieAuth(ctx, ActionSubmissionTriggerSubmitUncheckedOperation, r)
|
||||
if err != nil {
|
||||
err = &ogenerrors.SecurityError{
|
||||
OperationContext: opErrContext,
|
||||
Security: "CookieAuth",
|
||||
Err: err,
|
||||
}
|
||||
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
|
||||
defer recordError("Security:CookieAuth", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
satisfied[0] |= 1 << 0
|
||||
ctx = sctx
|
||||
}
|
||||
}
|
||||
|
||||
if ok := func() bool {
|
||||
nextRequirement:
|
||||
for _, requirement := range []bitset{
|
||||
{0b00000001},
|
||||
} {
|
||||
for i, mask := range requirement {
|
||||
if satisfied[i]&mask != mask {
|
||||
continue nextRequirement
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}(); !ok {
|
||||
err = &ogenerrors.SecurityError{
|
||||
OperationContext: opErrContext,
|
||||
Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied,
|
||||
}
|
||||
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
|
||||
defer recordError("Security", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
params, err := decodeActionSubmissionTriggerSubmitUncheckedParams(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 *ActionSubmissionTriggerSubmitUncheckedNoContent
|
||||
if m := s.cfg.Middleware; m != nil {
|
||||
mreq := middleware.Request{
|
||||
Context: ctx,
|
||||
OperationName: ActionSubmissionTriggerSubmitUncheckedOperation,
|
||||
OperationSummary: "Role Reviewer changes status from ChangesRequested -> Submitting",
|
||||
OperationID: "actionSubmissionTriggerSubmitUnchecked",
|
||||
Body: nil,
|
||||
Params: middleware.Parameters{
|
||||
{
|
||||
Name: "SubmissionID",
|
||||
In: "path",
|
||||
}: params.SubmissionID,
|
||||
},
|
||||
Raw: r,
|
||||
}
|
||||
|
||||
type (
|
||||
Request = struct{}
|
||||
Params = ActionSubmissionTriggerSubmitUncheckedParams
|
||||
Response = *ActionSubmissionTriggerSubmitUncheckedNoContent
|
||||
)
|
||||
response, err = middleware.HookMiddleware[
|
||||
Request,
|
||||
Params,
|
||||
Response,
|
||||
](
|
||||
m,
|
||||
mreq,
|
||||
unpackActionSubmissionTriggerSubmitUncheckedParams,
|
||||
func(ctx context.Context, request Request, params Params) (response Response, err error) {
|
||||
err = s.h.ActionSubmissionTriggerSubmitUnchecked(ctx, params)
|
||||
return response, err
|
||||
},
|
||||
)
|
||||
} else {
|
||||
err = s.h.ActionSubmissionTriggerSubmitUnchecked(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 := encodeActionSubmissionTriggerSubmitUncheckedResponse(response, w, span); err != nil {
|
||||
defer recordError("EncodeResponse", err)
|
||||
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
|
||||
s.cfg.ErrorHandler(ctx, w, r, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// handleActionSubmissionTriggerUploadRequest handles actionSubmissionTriggerUpload operation.
|
||||
//
|
||||
// Role Admin changes status from Validated -> Uploading.
|
||||
@@ -6256,201 +5866,6 @@ func (s *Server) handleGetMapRequest(args [1]string, argsEscaped bool, w http.Re
|
||||
}
|
||||
}
|
||||
|
||||
// handleGetMapAssetLocationRequest handles getMapAssetLocation operation.
|
||||
//
|
||||
// Get location of asset.
|
||||
//
|
||||
// GET /maps/{MapID}/location
|
||||
func (s *Server) handleGetMapAssetLocationRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
|
||||
statusWriter := &codeRecorder{ResponseWriter: w}
|
||||
w = statusWriter
|
||||
otelAttrs := []attribute.KeyValue{
|
||||
otelogen.OperationID("getMapAssetLocation"),
|
||||
semconv.HTTPRequestMethodKey.String("GET"),
|
||||
semconv.HTTPRouteKey.String("/maps/{MapID}/location"),
|
||||
}
|
||||
|
||||
// Start a span for this request.
|
||||
ctx, span := s.cfg.Tracer.Start(r.Context(), GetMapAssetLocationOperation,
|
||||
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: GetMapAssetLocationOperation,
|
||||
ID: "getMapAssetLocation",
|
||||
}
|
||||
)
|
||||
{
|
||||
type bitset = [1]uint8
|
||||
var satisfied bitset
|
||||
{
|
||||
sctx, ok, err := s.securityCookieAuth(ctx, GetMapAssetLocationOperation, r)
|
||||
if err != nil {
|
||||
err = &ogenerrors.SecurityError{
|
||||
OperationContext: opErrContext,
|
||||
Security: "CookieAuth",
|
||||
Err: err,
|
||||
}
|
||||
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
|
||||
defer recordError("Security:CookieAuth", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
satisfied[0] |= 1 << 0
|
||||
ctx = sctx
|
||||
}
|
||||
}
|
||||
|
||||
if ok := func() bool {
|
||||
nextRequirement:
|
||||
for _, requirement := range []bitset{
|
||||
{0b00000001},
|
||||
} {
|
||||
for i, mask := range requirement {
|
||||
if satisfied[i]&mask != mask {
|
||||
continue nextRequirement
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}(); !ok {
|
||||
err = &ogenerrors.SecurityError{
|
||||
OperationContext: opErrContext,
|
||||
Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied,
|
||||
}
|
||||
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
|
||||
defer recordError("Security", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
params, err := decodeGetMapAssetLocationParams(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 GetMapAssetLocationOK
|
||||
if m := s.cfg.Middleware; m != nil {
|
||||
mreq := middleware.Request{
|
||||
Context: ctx,
|
||||
OperationName: GetMapAssetLocationOperation,
|
||||
OperationSummary: "Get location of asset",
|
||||
OperationID: "getMapAssetLocation",
|
||||
Body: nil,
|
||||
Params: middleware.Parameters{
|
||||
{
|
||||
Name: "MapID",
|
||||
In: "path",
|
||||
}: params.MapID,
|
||||
},
|
||||
Raw: r,
|
||||
}
|
||||
|
||||
type (
|
||||
Request = struct{}
|
||||
Params = GetMapAssetLocationParams
|
||||
Response = GetMapAssetLocationOK
|
||||
)
|
||||
response, err = middleware.HookMiddleware[
|
||||
Request,
|
||||
Params,
|
||||
Response,
|
||||
](
|
||||
m,
|
||||
mreq,
|
||||
unpackGetMapAssetLocationParams,
|
||||
func(ctx context.Context, request Request, params Params) (response Response, err error) {
|
||||
response, err = s.h.GetMapAssetLocation(ctx, params)
|
||||
return response, err
|
||||
},
|
||||
)
|
||||
} else {
|
||||
response, err = s.h.GetMapAssetLocation(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 := encodeGetMapAssetLocationResponse(response, w, span); err != nil {
|
||||
defer recordError("EncodeResponse", err)
|
||||
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
|
||||
s.cfg.ErrorHandler(ctx, w, r, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// handleGetMapfixRequest handles getMapfix operation.
|
||||
//
|
||||
// Retrieve map with ID.
|
||||
|
||||
@@ -6,59 +6,56 @@ package api
|
||||
type OperationName = string
|
||||
|
||||
const (
|
||||
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"
|
||||
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"
|
||||
GetMapAssetLocationOperation OperationName = "GetMapAssetLocation"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
)
|
||||
|
||||
@@ -596,89 +596,6 @@ 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.
|
||||
@@ -1509,89 +1426,6 @@ 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.
|
||||
@@ -2256,88 +2090,6 @@ func decodeGetMapParams(args [1]string, argsEscaped bool, r *http.Request) (para
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// GetMapAssetLocationParams is parameters of getMapAssetLocation operation.
|
||||
type GetMapAssetLocationParams struct {
|
||||
MapID int64
|
||||
}
|
||||
|
||||
func unpackGetMapAssetLocationParams(packed middleware.Parameters) (params GetMapAssetLocationParams) {
|
||||
{
|
||||
key := middleware.ParameterKey{
|
||||
Name: "MapID",
|
||||
In: "path",
|
||||
}
|
||||
params.MapID = packed[key].(int64)
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
func decodeGetMapAssetLocationParams(args [1]string, argsEscaped bool, r *http.Request) (params GetMapAssetLocationParams, _ error) {
|
||||
// Decode path: MapID.
|
||||
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: "MapID",
|
||||
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.MapID = 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.MapID)); 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: "MapID",
|
||||
In: "path",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// GetMapfixParams is parameters of getMapfix operation.
|
||||
type GetMapfixParams struct {
|
||||
// The unique identifier for a mapfix.
|
||||
@@ -2961,25 +2713,16 @@ func decodeListMapfixAuditEventsParams(args [1]string, argsEscaped bool, r *http
|
||||
|
||||
// ListMapfixesParams is parameters of listMapfixes operation.
|
||||
type ListMapfixesParams struct {
|
||||
Page int32
|
||||
Limit int32
|
||||
DisplayName OptString
|
||||
Creator OptString
|
||||
// Game ID: * `1` - Bhop * `2` - Surf * `5` - FlyTrials.
|
||||
GameID OptInt32
|
||||
// Sort order: * `0` - Disabled * `1` - DisplayNameAscending * `2` - DisplayNameDescending * `3` -
|
||||
// DateAscending * `4` - DateDescending.
|
||||
Page int32
|
||||
Limit int32
|
||||
DisplayName OptString
|
||||
Creator OptString
|
||||
GameID OptInt32
|
||||
Sort OptInt32
|
||||
Submitter OptInt64
|
||||
AssetID OptInt64
|
||||
TargetAssetID OptInt64
|
||||
// // Phase: Creation * `0` - UnderConstruction * `1` - ChangesRequested
|
||||
// // Phase: Review * `2` - Submitting * `3` - Submitted
|
||||
// // Phase: Testing * `4` - AcceptedUnvalidated // pending script review, can re-trigger validation
|
||||
// * `5` - Validating * `6` - Validated * `7` - Uploading
|
||||
// // Phase: Final MapfixStatus * `8` - Uploaded // uploaded to the group, but pending release * `9`
|
||||
// - Rejected.
|
||||
StatusID OptInt32
|
||||
StatusID OptInt32
|
||||
}
|
||||
|
||||
func unpackListMapfixesParams(packed middleware.Parameters) (params ListMapfixesParams) {
|
||||
@@ -3708,9 +3451,7 @@ type ListMapsParams struct {
|
||||
DisplayName OptString
|
||||
Creator OptString
|
||||
GameID OptInt32
|
||||
// Sort order: * `0` - Disabled * `1` - DisplayNameAscending * `2` - DisplayNameDescending * `3` -
|
||||
// DateAscending * `4` - DateDescending.
|
||||
Sort OptInt32
|
||||
Sort OptInt32
|
||||
}
|
||||
|
||||
func unpackListMapsParams(packed middleware.Parameters) (params ListMapsParams) {
|
||||
@@ -5210,25 +4951,16 @@ func decodeListSubmissionAuditEventsParams(args [1]string, argsEscaped bool, r *
|
||||
|
||||
// ListSubmissionsParams is parameters of listSubmissions operation.
|
||||
type ListSubmissionsParams struct {
|
||||
Page int32
|
||||
Limit int32
|
||||
DisplayName OptString
|
||||
Creator OptString
|
||||
// Game ID: * `1` - Bhop * `2` - Surf * `5` - FlyTrials.
|
||||
GameID OptInt32
|
||||
// Sort order: * `0` - Disabled * `1` - DisplayNameAscending * `2` - DisplayNameDescending * `3` -
|
||||
// DateAscending * `4` - DateDescending.
|
||||
Page int32
|
||||
Limit int32
|
||||
DisplayName OptString
|
||||
Creator OptString
|
||||
GameID OptInt32
|
||||
Sort OptInt32
|
||||
Submitter OptInt64
|
||||
AssetID OptInt64
|
||||
UploadedAssetID OptInt64
|
||||
// // Phase: Creation * `0` - UnderConstruction * `1` - ChangesRequested
|
||||
// // Phase: Review * `2` - Submitting * `3` - Submitted
|
||||
// // Phase: Testing * `4` - AcceptedUnvalidated // pending script review, can re-trigger validation
|
||||
// * `5` - Validating * `6` - Validated * `7` - Uploading * `8` - Uploaded // uploaded to the group,
|
||||
// but pending release
|
||||
// // Phase: Final SubmissionStatus * `9` - Rejected * `10` - Released.
|
||||
StatusID OptInt32
|
||||
StatusID OptInt32
|
||||
}
|
||||
|
||||
func unpackListSubmissionsParams(packed middleware.Parameters) (params ListSubmissionsParams) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/go-faster/errors"
|
||||
"github.com/go-faster/jx"
|
||||
"go.uber.org/multierr"
|
||||
|
||||
"github.com/ogen-go/ogen/ogenerrors"
|
||||
"github.com/ogen-go/ogen/validate"
|
||||
@@ -26,13 +27,13 @@ func (s *Server) decodeCreateMapfixRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -97,13 +98,13 @@ func (s *Server) decodeCreateMapfixAuditCommentRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -131,13 +132,13 @@ func (s *Server) decodeCreateScriptRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -202,13 +203,13 @@ func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -273,13 +274,13 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -344,13 +345,13 @@ func (s *Server) decodeCreateSubmissionAdminRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -415,13 +416,13 @@ func (s *Server) decodeCreateSubmissionAuditCommentRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -449,13 +450,13 @@ func (s *Server) decodeReleaseSubmissionsRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -553,13 +554,13 @@ func (s *Server) decodeUpdateScriptRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -624,13 +625,13 @@ func (s *Server) decodeUpdateScriptPolicyRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
@@ -436,66 +435,6 @@ 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:
|
||||
@@ -1096,66 +1035,6 @@ 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:
|
||||
@@ -2182,82 +2061,6 @@ func decodeGetMapResponse(resp *http.Response) (res *Map, _ error) {
|
||||
return res, errors.Wrap(defRes, "error")
|
||||
}
|
||||
|
||||
func decodeGetMapAssetLocationResponse(resp *http.Response) (res GetMapAssetLocationOK, _ 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 := GetMapAssetLocationOK{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 decodeGetMapfixResponse(resp *http.Response) (res *Mapfix, _ error) {
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-faster/errors"
|
||||
@@ -63,13 +62,6 @@ 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))
|
||||
@@ -140,13 +132,6 @@ 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))
|
||||
@@ -280,22 +265,6 @@ func encodeGetMapResponse(response *Map, w http.ResponseWriter, span trace.Span)
|
||||
return nil
|
||||
}
|
||||
|
||||
func encodeGetMapAssetLocationResponse(response GetMapAssetLocationOK, 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 encodeGetMapfixResponse(response *Mapfix, w http.ResponseWriter, span trace.Span) error {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(200)
|
||||
|
||||
@@ -453,6 +453,7 @@ 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{
|
||||
@@ -464,30 +465,6 @@ 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"
|
||||
|
||||
@@ -571,15 +548,16 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Param: "MapID"
|
||||
// Match until "/"
|
||||
// Leaf parameter, slashes are prohibited
|
||||
idx := strings.IndexByte(elem, '/')
|
||||
if idx < 0 {
|
||||
idx = len(elem)
|
||||
if idx >= 0 {
|
||||
break
|
||||
}
|
||||
args[0] = elem[:idx]
|
||||
elem = elem[idx:]
|
||||
args[0] = elem
|
||||
elem = ""
|
||||
|
||||
if len(elem) == 0 {
|
||||
// Leaf node.
|
||||
switch r.Method {
|
||||
case "GET":
|
||||
s.handleGetMapRequest([1]string{
|
||||
@@ -591,30 +569,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
return
|
||||
}
|
||||
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.handleGetMapAssetLocationRequest([1]string{
|
||||
args[0],
|
||||
}, elemIsEscaped, w, r)
|
||||
default:
|
||||
s.notAllowed(w, r, "GET")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1295,6 +1249,7 @@ 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{
|
||||
@@ -1306,30 +1261,6 @@ 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"
|
||||
|
||||
@@ -1907,6 +1838,7 @@ 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,32 +1852,6 @@ 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"
|
||||
|
||||
@@ -2037,15 +1943,16 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
|
||||
}
|
||||
|
||||
// Param: "MapID"
|
||||
// Match until "/"
|
||||
// Leaf parameter, slashes are prohibited
|
||||
idx := strings.IndexByte(elem, '/')
|
||||
if idx < 0 {
|
||||
idx = len(elem)
|
||||
if idx >= 0 {
|
||||
break
|
||||
}
|
||||
args[0] = elem[:idx]
|
||||
elem = elem[idx:]
|
||||
args[0] = elem
|
||||
elem = ""
|
||||
|
||||
if len(elem) == 0 {
|
||||
// Leaf node.
|
||||
switch method {
|
||||
case "GET":
|
||||
r.name = GetMapOperation
|
||||
@@ -2059,32 +1966,6 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
|
||||
return
|
||||
}
|
||||
}
|
||||
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 = GetMapAssetLocationOperation
|
||||
r.summary = "Get location of asset"
|
||||
r.operationID = "getMapAssetLocation"
|
||||
r.pathPattern = "/maps/{MapID}/location"
|
||||
r.args = args
|
||||
r.count = 1
|
||||
return r, true
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2861,6 +2742,7 @@ 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
|
||||
@@ -2874,32 +2756,6 @@ 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"
|
||||
|
||||
|
||||
@@ -35,9 +35,6 @@ 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{}
|
||||
|
||||
@@ -68,9 +65,6 @@ 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{}
|
||||
|
||||
@@ -188,7 +182,6 @@ func (s *AuditEventEventData) init() AuditEventEventData {
|
||||
|
||||
type CookieAuth struct {
|
||||
APIKey string
|
||||
Roles []string
|
||||
}
|
||||
|
||||
// GetAPIKey returns the value of APIKey.
|
||||
@@ -196,21 +189,11 @@ func (s *CookieAuth) GetAPIKey() string {
|
||||
return s.APIKey
|
||||
}
|
||||
|
||||
// GetRoles returns the value of Roles.
|
||||
func (s *CookieAuth) GetRoles() []string {
|
||||
return s.Roles
|
||||
}
|
||||
|
||||
// SetAPIKey sets the value of APIKey.
|
||||
func (s *CookieAuth) SetAPIKey(val string) {
|
||||
s.APIKey = val
|
||||
}
|
||||
|
||||
// SetRoles sets the value of Roles.
|
||||
func (s *CookieAuth) SetRoles(val []string) {
|
||||
s.Roles = val
|
||||
}
|
||||
|
||||
// CreateMapfixAuditCommentNoContent is response for CreateMapfixAuditComment operation.
|
||||
type CreateMapfixAuditCommentNoContent struct{}
|
||||
|
||||
@@ -304,20 +287,6 @@ func (s *ErrorStatusCode) SetResponse(val Error) {
|
||||
s.Response = val
|
||||
}
|
||||
|
||||
type GetMapAssetLocationOK struct {
|
||||
Data io.Reader
|
||||
}
|
||||
|
||||
// Read reads data from the Data reader.
|
||||
//
|
||||
// Kept to satisfy the io.Reader interface.
|
||||
func (s GetMapAssetLocationOK) Read(p []byte) (n int, err error) {
|
||||
if s.Data == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return s.Data.Read(p)
|
||||
}
|
||||
|
||||
// Ref: #/components/schemas/Map
|
||||
type Map struct {
|
||||
ID int64 `json:"ID"`
|
||||
|
||||
@@ -33,52 +33,6 @@ func findAuthorization(h http.Header, prefix string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
var operationRolesCookieAuth = map[string][]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{},
|
||||
CreateMapfixOperation: []string{},
|
||||
CreateMapfixAuditCommentOperation: []string{},
|
||||
CreateScriptOperation: []string{},
|
||||
CreateScriptPolicyOperation: []string{},
|
||||
CreateSubmissionOperation: []string{},
|
||||
CreateSubmissionAdminOperation: []string{},
|
||||
CreateSubmissionAuditCommentOperation: []string{},
|
||||
DeleteScriptOperation: []string{},
|
||||
DeleteScriptPolicyOperation: []string{},
|
||||
GetMapAssetLocationOperation: []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) {
|
||||
var t CookieAuth
|
||||
const parameterName = "session_id"
|
||||
@@ -92,7 +46,6 @@ func (s *Server) securityCookieAuth(ctx context.Context, operationName Operation
|
||||
return nil, false, errors.Wrap(err, "get cookie value")
|
||||
}
|
||||
t.APIKey = value
|
||||
t.Roles = operationRolesCookieAuth[operationName]
|
||||
rctx, err := s.sec.HandleCookieAuth(ctx, operationName, t)
|
||||
if errors.Is(err, ogenerrors.ErrSkipServerSecurity) {
|
||||
return nil, false, nil
|
||||
|
||||
@@ -51,12 +51,6 @@ 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.
|
||||
@@ -118,12 +112,6 @@ 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.
|
||||
@@ -202,12 +190,6 @@ type Handler interface {
|
||||
//
|
||||
// GET /maps/{MapID}
|
||||
GetMap(ctx context.Context, params GetMapParams) (*Map, error)
|
||||
// GetMapAssetLocation implements getMapAssetLocation operation.
|
||||
//
|
||||
// Get location of asset.
|
||||
//
|
||||
// GET /maps/{MapID}/location
|
||||
GetMapAssetLocation(ctx context.Context, params GetMapAssetLocationParams) (GetMapAssetLocationOK, error)
|
||||
// GetMapfix implements getMapfix operation.
|
||||
//
|
||||
// Retrieve map with ID.
|
||||
|
||||
@@ -77,15 +77,6 @@ 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.
|
||||
@@ -177,15 +168,6 @@ 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.
|
||||
@@ -303,15 +285,6 @@ func (UnimplementedHandler) GetMap(ctx context.Context, params GetMapParams) (r
|
||||
return r, ht.ErrNotImplemented
|
||||
}
|
||||
|
||||
// GetMapAssetLocation implements getMapAssetLocation operation.
|
||||
//
|
||||
// Get location of asset.
|
||||
//
|
||||
// GET /maps/{MapID}/location
|
||||
func (UnimplementedHandler) GetMapAssetLocation(ctx context.Context, params GetMapAssetLocationParams) (r GetMapAssetLocationOK, _ error) {
|
||||
return r, ht.ErrNotImplemented
|
||||
}
|
||||
|
||||
// GetMapfix implements getMapfix operation.
|
||||
//
|
||||
// Retrieve map with ID.
|
||||
|
||||
@@ -10,7 +10,6 @@ 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"
|
||||
@@ -92,12 +91,6 @@ 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,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -135,10 +128,6 @@ 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()))
|
||||
|
||||
@@ -55,16 +55,11 @@ func (env *Mapfixes) Update(ctx context.Context, id int64, values datastore.Opti
|
||||
|
||||
// the update can only occur if the status matches one of the provided values.
|
||||
func (env *Mapfixes) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.MapfixStatus, values datastore.OptionalMap) error {
|
||||
result := env.db.Model(&model.Mapfix{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map())
|
||||
if result.Error != nil {
|
||||
if result.Error == gorm.ErrRecordNotFound {
|
||||
if err := env.db.Model(&model.Mapfix{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map()).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return datastore.ErrNotExist
|
||||
}
|
||||
return result.Error
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return datastore.ErroNoRowsAffected
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -55,16 +55,11 @@ func (env *Submissions) Update(ctx context.Context, id int64, values datastore.O
|
||||
|
||||
// the update can only occur if the status matches one of the provided values.
|
||||
func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.SubmissionStatus, values datastore.OptionalMap) error {
|
||||
result := env.db.Model(&model.Submission{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map())
|
||||
if result.Error != nil {
|
||||
if result.Error == gorm.ErrRecordNotFound {
|
||||
if err := env.db.Model(&model.Submission{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map()).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return datastore.ErrNotExist
|
||||
}
|
||||
return result.Error
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return datastore.ErroNoRowsAffected
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -100,18 +100,6 @@ 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.
|
||||
@@ -130,18 +118,6 @@ 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.
|
||||
@@ -290,6 +266,24 @@ 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 {
|
||||
@@ -381,6 +375,24 @@ 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 {
|
||||
@@ -914,6 +926,24 @@ 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 {
|
||||
@@ -1005,6 +1035,24 @@ 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 {
|
||||
@@ -1453,209 +1501,6 @@ 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.
|
||||
@@ -1881,209 +1726,6 @@ 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,6 +128,10 @@ func (s *Server) handleActionMapfixAcceptedRequest(args [1]string, argsEscaped b
|
||||
Name: "MapfixID",
|
||||
In: "path",
|
||||
}: params.MapfixID,
|
||||
{
|
||||
Name: "ErrorMessage",
|
||||
In: "query",
|
||||
}: params.ErrorMessage,
|
||||
},
|
||||
Raw: r,
|
||||
}
|
||||
@@ -277,6 +281,10 @@ func (s *Server) handleActionMapfixRequestChangesRequest(args [1]string, argsEsc
|
||||
Name: "MapfixID",
|
||||
In: "path",
|
||||
}: params.MapfixID,
|
||||
{
|
||||
Name: "ErrorMessage",
|
||||
In: "query",
|
||||
}: params.ErrorMessage,
|
||||
},
|
||||
Raw: r,
|
||||
}
|
||||
@@ -1042,6 +1050,10 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
|
||||
Name: "SubmissionID",
|
||||
In: "path",
|
||||
}: params.SubmissionID,
|
||||
{
|
||||
Name: "ErrorMessage",
|
||||
In: "query",
|
||||
}: params.ErrorMessage,
|
||||
},
|
||||
Raw: r,
|
||||
}
|
||||
@@ -1191,6 +1203,10 @@ func (s *Server) handleActionSubmissionRequestChangesRequest(args [1]string, arg
|
||||
Name: "SubmissionID",
|
||||
In: "path",
|
||||
}: params.SubmissionID,
|
||||
{
|
||||
Name: "ErrorMessage",
|
||||
In: "query",
|
||||
}: params.ErrorMessage,
|
||||
},
|
||||
Raw: r,
|
||||
}
|
||||
@@ -1858,323 +1874,6 @@ 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.
|
||||
@@ -2622,323 +2321,6 @@ 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,186 +12,6 @@ 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,13 +18,9 @@ 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,7 +18,8 @@ import (
|
||||
// ActionMapfixAcceptedParams is parameters of actionMapfixAccepted operation.
|
||||
type ActionMapfixAcceptedParams struct {
|
||||
// The unique identifier for a submission.
|
||||
MapfixID int64
|
||||
MapfixID int64
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
func unpackActionMapfixAcceptedParams(packed middleware.Parameters) (params ActionMapfixAcceptedParams) {
|
||||
@@ -29,10 +30,18 @@ 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]
|
||||
@@ -95,13 +104,66 @@ 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
|
||||
MapfixID int64
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
func unpackActionMapfixRequestChangesParams(packed middleware.Parameters) (params ActionMapfixRequestChangesParams) {
|
||||
@@ -112,10 +174,18 @@ 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]
|
||||
@@ -178,6 +248,58 @@ 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
|
||||
}
|
||||
|
||||
@@ -821,6 +943,7 @@ 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) {
|
||||
@@ -831,10 +954,18 @@ 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]
|
||||
@@ -897,6 +1028,58 @@ 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
|
||||
}
|
||||
|
||||
@@ -904,6 +1087,7 @@ 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) {
|
||||
@@ -914,10 +1098,18 @@ 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]
|
||||
@@ -980,6 +1172,58 @@ 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
|
||||
}
|
||||
|
||||
@@ -1537,460 +1781,6 @@ 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.
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/go-faster/errors"
|
||||
"github.com/go-faster/jx"
|
||||
"go.uber.org/multierr"
|
||||
|
||||
"github.com/ogen-go/ogen/ogenerrors"
|
||||
"github.com/ogen-go/ogen/validate"
|
||||
@@ -25,13 +26,13 @@ func (s *Server) decodeCreateMapfixRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -85,77 +86,6 @@ 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,
|
||||
@@ -167,13 +97,13 @@ func (s *Server) decodeCreateScriptRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -238,13 +168,13 @@ func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -309,13 +239,13 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
|
||||
// Close in reverse order, to match defer behavior.
|
||||
for i := len(closers) - 1; i >= 0; i-- {
|
||||
c := closers[i]
|
||||
merr = errors.Join(merr, c())
|
||||
merr = multierr.Append(merr, c())
|
||||
}
|
||||
return merr
|
||||
}
|
||||
defer func() {
|
||||
if rerr != nil {
|
||||
rerr = errors.Join(rerr, close())
|
||||
rerr = multierr.Append(rerr, close())
|
||||
}
|
||||
}()
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -368,74 +298,3 @@ 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,20 +25,6 @@ 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,
|
||||
@@ -80,17 +66,3 @@ 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,126 +776,6 @@ 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:
|
||||
@@ -1199,126 +1079,6 @@ 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,20 +104,6 @@ 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)
|
||||
@@ -160,20 +146,6 @@ 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,50 +113,6 @@ 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-" {
|
||||
@@ -508,50 +464,6 @@ 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-" {
|
||||
@@ -856,54 +768,6 @@ 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-" {
|
||||
@@ -1295,54 +1159,6 @@ 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,57 +43,6 @@ 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,18 +80,6 @@ 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.
|
||||
@@ -110,18 +98,6 @@ 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,24 +121,6 @@ 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.
|
||||
@@ -166,24 +148,6 @@ 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,88 +3,11 @@
|
||||
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,19 +48,6 @@ 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,13 +27,11 @@ type CreateMapfixRequest struct {
|
||||
type CheckSubmissionRequest struct{
|
||||
SubmissionID int64
|
||||
ModelID uint64
|
||||
SkipChecks bool
|
||||
}
|
||||
|
||||
type CheckMapfixRequest struct{
|
||||
MapfixID int64
|
||||
ModelID uint64
|
||||
SkipChecks bool
|
||||
MapfixID int64
|
||||
ModelID uint64
|
||||
}
|
||||
|
||||
type ValidateSubmissionRequest struct {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package model
|
||||
|
||||
type ResourceType int32
|
||||
const (
|
||||
ResourceUnknown ResourceType = 0
|
||||
ResourceMapfix ResourceType = 1
|
||||
ResourceSubmission ResourceType = 2
|
||||
)
|
||||
|
||||
type Resource struct{
|
||||
ID int64
|
||||
Type ResourceType
|
||||
}
|
||||
@@ -23,6 +23,13 @@ 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
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
package roblox
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type AssetMetadata struct {
|
||||
MetadataType uint32 `json:"metadataType"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// Struct equivalent to Rust's AssetLocationInfo
|
||||
type AssetLocationInfo struct {
|
||||
Location string `json:"location"`
|
||||
RequestId string `json:"requestId"`
|
||||
IsArchived bool `json:"isArchived"`
|
||||
AssetTypeId uint32 `json:"assetTypeId"`
|
||||
AssetMetadatas []AssetMetadata `json:"assetMetadatas"`
|
||||
IsRecordable bool `json:"isRecordable"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -10,13 +10,76 @@ import (
|
||||
"git.itzana.me/strafesnet/maps-service/pkg/model"
|
||||
)
|
||||
|
||||
func (svc *Service) ListAuditEvents(ctx context.Context, resource model.Resource, page model.Page) ([]api.AuditEvent, error){
|
||||
// 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
|
||||
}
|
||||
if !has_role {
|
||||
return ErrPermissionDeniedNeedRoleMapfixReview
|
||||
}
|
||||
|
||||
userId, err := userInfo.GetUserID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := []byte{}
|
||||
_, err = req.Read(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Comment := string(data)
|
||||
|
||||
event_data := model.AuditEventDataComment{
|
||||
Comment: Comment,
|
||||
}
|
||||
|
||||
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) {
|
||||
filter := datastore.Optional()
|
||||
|
||||
filter.Add("resource_type", resource.Type)
|
||||
filter.Add("resource_id", resource.ID)
|
||||
filter.Add("resource_type", model.ResourceMapfix)
|
||||
filter.Add("resource_id", params.MapfixID)
|
||||
|
||||
items, err := svc.DB.AuditEvents().List(ctx, filter, page)
|
||||
items, err := svc.DB.AuditEvents().List(ctx, filter, model.Page{
|
||||
Number: params.Page,
|
||||
Size: params.Limit,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -68,7 +131,41 @@ func (svc *Service) ListAuditEvents(ctx context.Context, resource model.Resource
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (svc *Service) CreateAuditEventAction(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataAction) error {
|
||||
// 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
|
||||
}
|
||||
if !has_role {
|
||||
return ErrPermissionDeniedNeedRoleSubmissionReview
|
||||
}
|
||||
|
||||
userId, err := userInfo.GetUserID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := []byte{}
|
||||
_, err = req.Read(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Comment := string(data)
|
||||
|
||||
event_data := model.AuditEventDataComment{
|
||||
Comment: Comment,
|
||||
}
|
||||
|
||||
EventData, err := json.Marshal(event_data)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -77,29 +174,8 @@ func (svc *Service) CreateAuditEventAction(ctx context.Context, userId uint64, r
|
||||
_, 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) 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,
|
||||
ResourceType: model.ResourceSubmission,
|
||||
ResourceID: params.SubmissionID,
|
||||
EventType: model.AuditEventTypeComment,
|
||||
EventData: EventData,
|
||||
})
|
||||
@@ -110,23 +186,68 @@ func (svc *Service) CreateAuditEventComment(ctx context.Context, userId uint64,
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
// 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()
|
||||
|
||||
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
|
||||
ID: 0,
|
||||
User: userId,
|
||||
ResourceType: resource.Type,
|
||||
ResourceID: resource.ID,
|
||||
EventType: model.AuditEventTypeChangeModel,
|
||||
EventData: EventData,
|
||||
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,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil
|
||||
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
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"git.itzana.me/strafesnet/go-grpc/maps"
|
||||
@@ -35,7 +34,7 @@ var(
|
||||
|
||||
var (
|
||||
ErrCreationPhaseMapfixesLimit = errors.New("Active mapfixes limited to 20")
|
||||
ErrActiveMapfixSameTargetAssetID = errors.New("There is an active mapfix for this map already")
|
||||
ErrActiveMapfixSameTargetAssetID = errors.New("There is an active mapfix with the same TargetAssetID")
|
||||
ErrAcceptOwnMapfix = fmt.Errorf("%w: You cannot accept your own mapfix as the submitter", ErrPermissionDenied)
|
||||
ErrCreateMapfixRateLimit = errors.New("You must not create more than 5 mapfixes every 10 minutes")
|
||||
)
|
||||
@@ -77,24 +76,6 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a mapfix targetting the same map exists in creation phase
|
||||
{
|
||||
filter := datastore.Optional()
|
||||
filter.Add("submitter", int64(userId))
|
||||
filter.Add("target_asset_id", request.TargetAssetID)
|
||||
filter.Add("status_id", CreationPhaseMapfixStatuses)
|
||||
active_mapfixes, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
|
||||
Number: 1,
|
||||
Size: 1,
|
||||
},datastore.ListSortDisabled)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(active_mapfixes) != 0{
|
||||
return nil, ErrActiveMapfixSameTargetAssetID
|
||||
}
|
||||
}
|
||||
|
||||
// Check if TargetAssetID actually exists
|
||||
{
|
||||
_, err := svc.Maps.Get(ctx, &maps.IdMessage{
|
||||
@@ -317,15 +298,24 @@ func (svc *Service) UpdateMapfixModel(ctx context.Context, params api.UpdateMapf
|
||||
NewModelVersion: NewModelVersion,
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventChangeModel(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_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.AuditEventTypeChangeModel,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionMapfixReject invokes actionMapfixReject operation.
|
||||
@@ -366,15 +356,24 @@ func (svc *Service) ActionMapfixReject(ctx context.Context, params api.ActionMap
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_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.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionMapfixRequestChanges invokes actionMapfixRequestChanges operation.
|
||||
@@ -397,33 +396,10 @@ func (svc *Service) ActionMapfixRequestChanges(ctx context.Context, params api.A
|
||||
return ErrPermissionDeniedNeedRoleMapfixReview
|
||||
}
|
||||
|
||||
userId, err := userInfo.GetUserID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// transaction
|
||||
target_status := model.MapfixStatusChangesRequested
|
||||
smap := datastore.Optional()
|
||||
smap.Add("status_id", target_status)
|
||||
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidated, model.MapfixStatusAcceptedUnvalidated, model.MapfixStatusSubmitted}, smap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
event_data := model.AuditEventDataAction{
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
smap.Add("status_id", model.MapfixStatusChangesRequested)
|
||||
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidated, model.MapfixStatusAcceptedUnvalidated, model.MapfixStatusSubmitted}, smap)
|
||||
}
|
||||
|
||||
// ActionMapfixRevoke invokes actionMapfixRevoke operation.
|
||||
@@ -467,15 +443,24 @@ func (svc *Service) ActionMapfixRevoke(ctx context.Context, params api.ActionMap
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_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.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionMapfixTriggerSubmit invokes actionMapfixTriggerSubmit operation.
|
||||
@@ -516,9 +501,8 @@ func (svc *Service) ActionMapfixTriggerSubmit(ctx context.Context, params api.Ac
|
||||
}
|
||||
|
||||
validate_request := model.CheckMapfixRequest{
|
||||
MapfixID: mapfix.ID,
|
||||
ModelID: mapfix.AssetID,
|
||||
SkipChecks: false,
|
||||
MapfixID: mapfix.ID,
|
||||
ModelID: mapfix.AssetID,
|
||||
}
|
||||
|
||||
j, err := json.Marshal(validate_request)
|
||||
@@ -535,92 +519,24 @@ func (svc *Service) ActionMapfixTriggerSubmit(ctx context.Context, params api.Ac
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
}
|
||||
|
||||
// ActionMapfixTriggerSubmitUnchecked invokes actionMapfixTriggerSubmitUnchecked operation.
|
||||
//
|
||||
// Role Reviewer changes status from ChangesRequested -> Submitting.
|
||||
//
|
||||
// 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
|
||||
}
|
||||
|
||||
// read mapfix (this could be done with a transaction WHERE clause)
|
||||
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
|
||||
EventData, err := json.Marshal(event_data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userId, err := userInfo.GetUserID()
|
||||
_, 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
|
||||
}
|
||||
|
||||
// check if caller is the submitter
|
||||
is_submitter := userId == mapfix.Submitter
|
||||
if is_submitter {
|
||||
return ErrAcceptOwnMapfix
|
||||
}
|
||||
|
||||
has_mapfix_review, err := userInfo.HasRoleMapfixReview()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !has_mapfix_review {
|
||||
return ErrPermissionDeniedNeedRoleMapfixReview
|
||||
}
|
||||
|
||||
// transaction
|
||||
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)
|
||||
if err != nil {
|
||||
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),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionMapfixResetSubmitting implements actionMapfixResetSubmitting operation.
|
||||
@@ -668,15 +584,24 @@ func (svc *Service) ActionMapfixResetSubmitting(ctx context.Context, params api.
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_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.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionMapfixTriggerUpload invokes actionMapfixTriggerUpload operation.
|
||||
@@ -735,15 +660,24 @@ func (svc *Service) ActionMapfixTriggerUpload(ctx context.Context, params api.Ac
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_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.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionMapfixValidate invokes actionMapfixValidate operation.
|
||||
@@ -794,15 +728,24 @@ func (svc *Service) ActionMapfixValidated(ctx context.Context, params api.Action
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_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.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionMapfixTriggerValidate invokes actionMapfixTriggerValidate operation.
|
||||
@@ -894,15 +837,24 @@ func (svc *Service) ActionMapfixTriggerValidate(ctx context.Context, params api.
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_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.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionMapfixRetryValidate invokes actionMapfixRetryValidate operation.
|
||||
@@ -965,15 +917,24 @@ func (svc *Service) ActionMapfixRetryValidate(ctx context.Context, params api.Ac
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_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.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionMapfixAccepted implements actionMapfixAccepted operation.
|
||||
@@ -1024,85 +985,22 @@ func (svc *Service) ActionMapfixAccepted(ctx context.Context, params api.ActionM
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
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()
|
||||
EventData, err := json.Marshal(event_data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userId, err := userInfo.GetUserID()
|
||||
_, 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
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"git.itzana.me/strafesnet/go-grpc/maps"
|
||||
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
||||
"git.itzana.me/strafesnet/maps-service/pkg/roblox"
|
||||
)
|
||||
|
||||
// ListMaps implements listMaps operation.
|
||||
@@ -73,43 +71,3 @@ func (svc *Service) GetMap(ctx context.Context, params api.GetMapParams) (*api.M
|
||||
Date: mapResponse.Date,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetMapAssetLocation invokes getMapAssetLocation operation.
|
||||
//
|
||||
// Get location of map asset.
|
||||
//
|
||||
// GET /maps/{MapID}/location
|
||||
func (svc *Service) GetMapAssetLocation(ctx context.Context, params api.GetMapAssetLocationParams) (ok api.GetMapAssetLocationOK, err error) {
|
||||
userInfo, success := ctx.Value("UserInfo").(UserInfoHandle)
|
||||
if !success {
|
||||
return ok, ErrUserInfo
|
||||
}
|
||||
|
||||
has_role, err := userInfo.HasRoleMapDownload()
|
||||
if err != nil {
|
||||
return ok, err
|
||||
}
|
||||
|
||||
if !has_role {
|
||||
return ok, ErrPermissionDeniedNeedRoleMapDownload
|
||||
}
|
||||
|
||||
// Ensure map exists in the db!
|
||||
// This could otherwise be used to access any asset
|
||||
_, err = svc.Maps.Get(ctx, &maps.IdMessage{
|
||||
ID: params.MapID,
|
||||
})
|
||||
if err != nil {
|
||||
return ok, err
|
||||
}
|
||||
|
||||
info, err := svc.Roblox.GetAssetLocation(roblox.GetAssetLatestRequest{
|
||||
AssetID: uint64(params.MapID),
|
||||
})
|
||||
if err != nil{
|
||||
return ok, err
|
||||
}
|
||||
|
||||
ok.Data = strings.NewReader(info.Location)
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
@@ -84,7 +84,6 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
|
||||
for _, item := range items {
|
||||
resp = append(resp, api.Script{
|
||||
ID: item.ID,
|
||||
Name: item.Name,
|
||||
Hash: model.HashFormat(uint64(item.Hash)),
|
||||
Source: item.Source,
|
||||
ResourceType: int32(item.ResourceType),
|
||||
|
||||
@@ -9,7 +9,6 @@ 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"
|
||||
)
|
||||
|
||||
@@ -36,7 +35,6 @@ type Service struct {
|
||||
Nats nats.JetStreamContext
|
||||
Maps maps.MapsServiceClient
|
||||
Users users.UsersServiceClient
|
||||
Roblox roblox.Client
|
||||
}
|
||||
|
||||
// NewError creates *ErrorStatusCode from error returned by handler.
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"git.itzana.me/strafesnet/go-grpc/maps"
|
||||
@@ -21,7 +20,7 @@ var(
|
||||
model.SubmissionStatusSubmitted,
|
||||
model.SubmissionStatusUnderConstruction,
|
||||
}
|
||||
// limit submissions in the pipeline to one per target map
|
||||
// limit mapfixes in the pipeline to one per target map
|
||||
ActiveAcceptedSubmissionStatuses = []model.SubmissionStatus{
|
||||
model.SubmissionStatusUploading,
|
||||
model.SubmissionStatusValidated,
|
||||
@@ -366,15 +365,24 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
|
||||
NewModelVersion: NewModelVersion,
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventChangeModel(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_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.ResourceSubmission,
|
||||
ResourceID: submission.ID,
|
||||
EventType: model.AuditEventTypeChangeModel,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionSubmissionReject invokes actionSubmissionReject operation.
|
||||
@@ -415,15 +423,24 @@ func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.Actio
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_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.ResourceSubmission,
|
||||
ResourceID: params.SubmissionID,
|
||||
EventType: model.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
|
||||
@@ -464,15 +481,24 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params a
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_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.ResourceSubmission,
|
||||
ResourceID: params.SubmissionID,
|
||||
EventType: model.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionSubmissionRevoke invokes actionSubmissionRevoke operation.
|
||||
@@ -516,15 +542,24 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_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.ResourceSubmission,
|
||||
ResourceID: params.SubmissionID,
|
||||
EventType: model.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionSubmissionTriggerSubmit invokes actionSubmissionTriggerSubmit operation.
|
||||
@@ -549,18 +584,16 @@ func (svc *Service) ActionSubmissionTriggerSubmit(ctx context.Context, params ap
|
||||
return err
|
||||
}
|
||||
|
||||
has_submission_review, err := userInfo.HasRoleSubmissionReview()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check if caller is the submitter
|
||||
is_submitter := userId == submission.Submitter
|
||||
// neither = deny
|
||||
if !is_submitter {
|
||||
has_submission_review, err := userInfo.HasRoleSubmissionReview()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !has_submission_review {
|
||||
return ErrPermissionDeniedNotSubmitter
|
||||
}
|
||||
if !is_submitter && !has_submission_review {
|
||||
return ErrPermissionDeniedNotSubmitter
|
||||
}
|
||||
|
||||
// transaction
|
||||
@@ -575,7 +608,6 @@ 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)
|
||||
@@ -592,92 +624,24 @@ func (svc *Service) ActionSubmissionTriggerSubmit(ctx context.Context, params ap
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
}
|
||||
|
||||
// ActionSubmissionTriggerSubmitUnchecked invokes actionSubmissionTriggerSubmitUnchecked operation.
|
||||
//
|
||||
// Role Reviewer changes status from ChangesRequested -> Submitting.
|
||||
//
|
||||
// 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
|
||||
}
|
||||
|
||||
// read submission (this could be done with a transaction WHERE clause)
|
||||
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
|
||||
EventData, err := json.Marshal(event_data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userId, err := userInfo.GetUserID()
|
||||
_, 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
|
||||
}
|
||||
|
||||
// check if caller is the submitter
|
||||
is_submitter := userId == submission.Submitter
|
||||
if is_submitter {
|
||||
return ErrAcceptOwnSubmission
|
||||
}
|
||||
|
||||
has_submission_review, err := userInfo.HasRoleSubmissionReview()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !has_submission_review {
|
||||
return ErrPermissionDeniedNeedRoleSubmissionReview
|
||||
}
|
||||
|
||||
// transaction
|
||||
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)
|
||||
if err != nil {
|
||||
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),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionSubmissionResetSubmitting implements actionSubmissionResetSubmitting operation.
|
||||
@@ -725,15 +689,24 @@ func (svc *Service) ActionSubmissionResetSubmitting(ctx context.Context, params
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_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.ResourceSubmission,
|
||||
ResourceID: params.SubmissionID,
|
||||
EventType: model.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation.
|
||||
@@ -799,15 +772,24 @@ func (svc *Service) ActionSubmissionTriggerUpload(ctx context.Context, params ap
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_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.ResourceSubmission,
|
||||
ResourceID: params.SubmissionID,
|
||||
EventType: model.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
|
||||
@@ -858,15 +840,24 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params api.Ac
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_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.ResourceSubmission,
|
||||
ResourceID: params.SubmissionID,
|
||||
EventType: model.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation.
|
||||
@@ -941,15 +932,24 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_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.ResourceSubmission,
|
||||
ResourceID: params.SubmissionID,
|
||||
EventType: model.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionSubmissionRetryValidate invokes actionSubmissionRetryValidate operation.
|
||||
@@ -1012,15 +1012,24 @@ func (svc *Service) ActionSubmissionRetryValidate(ctx context.Context, params ap
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_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.ResourceSubmission,
|
||||
ResourceID: params.SubmissionID,
|
||||
EventType: model.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
|
||||
@@ -1071,15 +1080,24 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params api.Act
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
userId,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_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.ResourceSubmission,
|
||||
ResourceID: params.SubmissionID,
|
||||
EventType: model.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReleaseSubmissions invokes releaseSubmissions operation.
|
||||
@@ -1149,75 +1167,3 @@ 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,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
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,6 +2,7 @@ package service_internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
@@ -53,15 +54,24 @@ func (svc *Service) UpdateMapfixValidatedModel(ctx context.Context, params inter
|
||||
ValidatedModelVersion: ValidatedModelVersion,
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventChangeValidatedModel(
|
||||
ctx,
|
||||
ValidtorUserID,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
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
|
||||
}
|
||||
|
||||
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
|
||||
@@ -87,15 +97,24 @@ func (svc *Service) ActionMapfixSubmitted(ctx context.Context, params internal.A
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
ValidtorUserID,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
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
|
||||
}
|
||||
|
||||
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
|
||||
@@ -113,19 +132,51 @@ 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),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
ValidtorUserID,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
EventData, err := json.Marshal(event_data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
|
||||
ID: 0,
|
||||
User: ValidtorUserID,
|
||||
ResourceType: model.ResourceMapfix,
|
||||
ResourceID: params.MapfixID,
|
||||
EventType: model.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionMapfixValidate invokes actionMapfixValidate operation.
|
||||
@@ -155,20 +206,53 @@ 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),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
ValidtorUserID,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
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
|
||||
}
|
||||
|
||||
// ActionMapfixUploaded implements actionMapfixUploaded operation.
|
||||
@@ -190,66 +274,24 @@ func (svc *Service) ActionMapfixUploaded(ctx context.Context, params internal.Ac
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
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,
|
||||
EventData, err := json.Marshal(event_data)
|
||||
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,
|
||||
}
|
||||
_, 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
|
||||
}
|
||||
|
||||
event_data := model.AuditEventDataCheckList{
|
||||
CheckList: check_list2,
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventCheckList(
|
||||
ctx,
|
||||
ValidtorUserID,
|
||||
model.Resource{
|
||||
ID: params.MapfixID,
|
||||
Type: model.ResourceMapfix,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// POST /mapfixes
|
||||
|
||||
@@ -71,7 +71,6 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
|
||||
for _, item := range items {
|
||||
resp = append(resp, api.Script{
|
||||
ID: item.ID,
|
||||
Name: item.Name,
|
||||
Hash: model.HashFormat(uint64(item.Hash)),
|
||||
Source: item.Source,
|
||||
ResourceType: int32(item.ResourceType),
|
||||
|
||||
@@ -2,6 +2,7 @@ package service_internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
@@ -53,15 +54,24 @@ func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params i
|
||||
ValidatedModelVersion: ValidatedModelVersion,
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventChangeValidatedModel(
|
||||
ctx,
|
||||
ValidtorUserID,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
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
|
||||
}
|
||||
|
||||
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
|
||||
@@ -87,15 +97,24 @@ func (svc *Service) ActionSubmissionSubmitted(ctx context.Context, params intern
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
ValidtorUserID,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
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
|
||||
}
|
||||
|
||||
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
|
||||
@@ -113,20 +132,53 @@ 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),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
ValidtorUserID,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
EventData, err := json.Marshal(event_data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
|
||||
ID: 0,
|
||||
User: ValidtorUserID,
|
||||
ResourceType: model.ResourceSubmission,
|
||||
ResourceID: params.SubmissionID,
|
||||
EventType: model.AuditEventTypeAction,
|
||||
EventData: EventData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
|
||||
@@ -148,15 +200,24 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params intern
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
ValidtorUserID,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
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
|
||||
}
|
||||
|
||||
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
|
||||
@@ -174,20 +235,54 @@ 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),
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventAction(
|
||||
ctx,
|
||||
ValidtorUserID,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
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
|
||||
}
|
||||
|
||||
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
|
||||
@@ -210,66 +305,24 @@ func (svc *Service) ActionSubmissionUploaded(ctx context.Context, params interna
|
||||
TargetStatus: uint32(target_status),
|
||||
}
|
||||
|
||||
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,
|
||||
EventData, err := json.Marshal(event_data)
|
||||
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,
|
||||
}
|
||||
_, 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
|
||||
}
|
||||
|
||||
event_data := model.AuditEventDataCheckList{
|
||||
CheckList: check_list2,
|
||||
}
|
||||
|
||||
return svc.CreateAuditEventCheckList(
|
||||
ctx,
|
||||
ValidtorUserID,
|
||||
model.Resource{
|
||||
ID: params.SubmissionID,
|
||||
Type: model.ResourceSubmission,
|
||||
},
|
||||
event_data,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// POST /submissions
|
||||
|
||||
@@ -5,13 +5,13 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
submissions-api = { path = "api", features = ["internal"], default-features = false, registry = "strafesnet" }
|
||||
async-nats = "0.42.0"
|
||||
async-nats = "0.40.0"
|
||||
futures = "0.3.31"
|
||||
rbx_asset = { version = "0.4.7", registry = "strafesnet" }
|
||||
rbx_binary = "1.0.0"
|
||||
rbx_dom_weak = "3.0.0"
|
||||
rbx_reflection_database = "1.0.3"
|
||||
rbx_xml = "1.0.0"
|
||||
rbx_asset = { version = "0.4.4-pre2", registry = "strafesnet" }
|
||||
rbx_binary = { version = "0.7.4", registry = "strafesnet"}
|
||||
rbx_dom_weak = { version = "2.9.0", registry = "strafesnet"}
|
||||
rbx_reflection_database = { version = "0.2.12", registry = "strafesnet"}
|
||||
rbx_xml = { version = "0.13.3", registry = "strafesnet"}
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
serde_json = "1.0.133"
|
||||
siphasher = "1.0.1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "submissions-api"
|
||||
version = "0.8.1"
|
||||
version = "0.7.0"
|
||||
edition = "2021"
|
||||
publish = ["strafesnet"]
|
||||
repository = "https://git.itzana.me/StrafesNET/maps-service"
|
||||
@@ -11,7 +11,6 @@ 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"
|
||||
|
||||
@@ -44,8 +44,4 @@ impl Context{
|
||||
.body(body)
|
||||
.send().await
|
||||
}
|
||||
pub async fn delete(&self,url:impl reqwest::IntoUrl)->Result<reqwest::Response,reqwest::Error>{
|
||||
self.client.delete(url)
|
||||
.send().await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ impl Context{
|
||||
).await.map_err(Error::Response)?
|
||||
.json().await.map_err(Error::ReqwestJson)
|
||||
}
|
||||
pub async fn get_scripts(&self,config:GetScriptsRequest<'_>)->Result<Vec<ScriptResponse>,Error>{
|
||||
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
|
||||
let url_raw=format!("{}/scripts",self.0.base_url);
|
||||
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||
|
||||
@@ -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(ResourceID(resource_id))=config.ResourceID{
|
||||
if let Some(resource_id)=config.ResourceID{
|
||||
query_pairs.append_pair("ResourceID",resource_id.to_string().as_str());
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ impl Context{
|
||||
).await.map_err(Error::Response)?
|
||||
.json().await.map_err(Error::ReqwestJson)
|
||||
}
|
||||
pub async fn get_script_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptResponse>,ScriptSingleItemError>{
|
||||
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
|
||||
let scripts=self.get_scripts(GetScriptsRequest{
|
||||
Page:1,
|
||||
Limit:2,
|
||||
@@ -57,11 +57,11 @@ impl Context{
|
||||
ResourceID:None,
|
||||
}).await.map_err(SingleItemError::Other)?;
|
||||
if 1<scripts.len(){
|
||||
return Err(SingleItemError::DuplicateItems(scripts.into_iter().map(|item|item.ID).collect()));
|
||||
return Err(SingleItemError::DuplicateItems);
|
||||
}
|
||||
Ok(scripts.into_iter().next())
|
||||
}
|
||||
pub async fn create_script(&self,config:CreateScriptRequest<'_>)->Result<ScriptIDResponse,Error>{
|
||||
pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{
|
||||
let url_raw=format!("{}/scripts",self.0.base_url);
|
||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||
|
||||
@@ -72,17 +72,7 @@ impl Context{
|
||||
).await.map_err(Error::Response)?
|
||||
.json().await.map_err(Error::ReqwestJson)
|
||||
}
|
||||
pub async fn delete_script(&self,config:GetScriptRequest)->Result<(),Error>{
|
||||
let url_raw=format!("{}/scripts/{}",self.0.base_url,config.ScriptID.0);
|
||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||
|
||||
response_ok(
|
||||
self.0.delete(url).await.map_err(Error::Reqwest)?
|
||||
).await.map_err(Error::Response)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub async fn get_script_policies(&self,config:GetScriptPoliciesRequest<'_>)->Result<Vec<ScriptPolicyResponse>,Error>{
|
||||
pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{
|
||||
let url_raw=format!("{}/script-policy",self.0.base_url);
|
||||
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||
|
||||
@@ -106,7 +96,7 @@ impl Context{
|
||||
).await.map_err(Error::Response)?
|
||||
.json().await.map_err(Error::ReqwestJson)
|
||||
}
|
||||
pub async fn get_script_policy_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptPolicyResponse>,ScriptPolicySingleItemError>{
|
||||
pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
|
||||
let policies=self.get_script_policies(GetScriptPoliciesRequest{
|
||||
Page:1,
|
||||
Limit:2,
|
||||
@@ -115,7 +105,7 @@ impl Context{
|
||||
Policy:None,
|
||||
}).await.map_err(SingleItemError::Other)?;
|
||||
if 1<policies.len(){
|
||||
return Err(SingleItemError::DuplicateItems(policies.into_iter().map(|item|item.ID).collect()));
|
||||
return Err(SingleItemError::DuplicateItems);
|
||||
}
|
||||
Ok(policies.into_iter().next())
|
||||
}
|
||||
@@ -140,94 +130,6 @@ impl Context{
|
||||
self.0.post(url,body).await.map_err(Error::Reqwest)?
|
||||
).await.map_err(Error::Response)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub async fn delete_script_policy(&self,config:GetScriptPolicyRequest)->Result<(),Error>{
|
||||
let url_raw=format!("{}/script-policy/{}",self.0.base_url,config.ScriptPolicyID.0);
|
||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||
|
||||
response_ok(
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ impl Context{
|
||||
).await.map_err(Error::Response)?
|
||||
.json().await.map_err(Error::ReqwestJson)
|
||||
}
|
||||
pub async fn get_scripts(&self,config:GetScriptsRequest<'_>)->Result<Vec<ScriptResponse>,Error>{
|
||||
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
|
||||
let url_raw=format!("{}/scripts",self.0.base_url);
|
||||
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||
|
||||
@@ -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(ResourceID(resource_id))=config.ResourceID{
|
||||
if let Some(resource_id)=config.ResourceID{
|
||||
query_pairs.append_pair("ResourceID",resource_id.to_string().as_str());
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ impl Context{
|
||||
).await.map_err(Error::Response)?
|
||||
.json().await.map_err(Error::ReqwestJson)
|
||||
}
|
||||
pub async fn get_script_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptResponse>,ScriptSingleItemError>{
|
||||
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
|
||||
let scripts=self.get_scripts(GetScriptsRequest{
|
||||
Page:1,
|
||||
Limit:2,
|
||||
@@ -87,11 +87,11 @@ impl Context{
|
||||
ResourceID:None,
|
||||
}).await.map_err(SingleItemError::Other)?;
|
||||
if 1<scripts.len(){
|
||||
return Err(SingleItemError::DuplicateItems(scripts.into_iter().map(|item|item.ID).collect()));
|
||||
return Err(SingleItemError::DuplicateItems);
|
||||
}
|
||||
Ok(scripts.into_iter().next())
|
||||
}
|
||||
pub async fn create_script(&self,config:CreateScriptRequest<'_>)->Result<ScriptIDResponse,Error>{
|
||||
pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{
|
||||
let url_raw=format!("{}/scripts",self.0.base_url);
|
||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||
|
||||
@@ -102,7 +102,7 @@ impl Context{
|
||||
).await.map_err(Error::Response)?
|
||||
.json().await.map_err(Error::ReqwestJson)
|
||||
}
|
||||
pub async fn get_script_policies(&self,config:GetScriptPoliciesRequest<'_>)->Result<Vec<ScriptPolicyResponse>,Error>{
|
||||
pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{
|
||||
let url_raw=format!("{}/script-policy",self.0.base_url);
|
||||
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||
|
||||
@@ -126,7 +126,7 @@ impl Context{
|
||||
).await.map_err(Error::Response)?
|
||||
.json().await.map_err(Error::ReqwestJson)
|
||||
}
|
||||
pub async fn get_script_policy_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptPolicyResponse>,ScriptPolicySingleItemError>{
|
||||
pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
|
||||
let policies=self.get_script_policies(GetScriptPoliciesRequest{
|
||||
Page:1,
|
||||
Limit:2,
|
||||
@@ -135,7 +135,7 @@ impl Context{
|
||||
Policy:None,
|
||||
}).await.map_err(SingleItemError::Other)?;
|
||||
if 1<policies.len(){
|
||||
return Err(SingleItemError::DuplicateItems(policies.into_iter().map(|item|item.ID).collect()));
|
||||
return Err(SingleItemError::DuplicateItems);
|
||||
}
|
||||
Ok(policies.into_iter().next())
|
||||
}
|
||||
@@ -150,7 +150,7 @@ impl Context{
|
||||
).await.map_err(Error::Response)?
|
||||
.json().await.map_err(Error::ReqwestJson)
|
||||
}
|
||||
pub async fn create_submission(&self,config:CreateSubmissionRequest<'_>)->Result<SubmissionIDResponse,Error>{
|
||||
pub async fn create_submission<'a>(&self,config:CreateSubmissionRequest<'a>)->Result<SubmissionIDResponse,Error>{
|
||||
let url_raw=format!("{}/submissions",self.0.base_url);
|
||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||
|
||||
@@ -161,39 +161,28 @@ 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.0,);
|
||||
action!("submissions",action_submission_submitted,config,ActionSubmissionSubmittedRequest,"status/validator-submitted",config.SubmissionID.0,
|
||||
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,
|
||||
("ModelVersion",config.ModelVersion.to_string().as_str())
|
||||
("DisplayName",config.DisplayName.as_str())
|
||||
("Creator",config.Creator.as_str())
|
||||
("GameID",(config.GameID as u8).to_string().as_str())
|
||||
("GameID",config.GameID.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.0,
|
||||
action!("submissions",update_submission_validated_model,config,UpdateSubmissionModelRequest,"validated-model",config.SubmissionID,
|
||||
("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.0,
|
||||
action!("submissions",action_submission_uploaded,config,ActionSubmissionUploadedRequest,"status/validator-uploaded",config.SubmissionID,
|
||||
("UploadedAssetID",config.UploadedAssetID.to_string().as_str())
|
||||
);
|
||||
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,
|
||||
action!("submissions",action_submission_accepted,config,ActionSubmissionAcceptedRequest,"status/validator-failed",config.SubmissionID,
|
||||
("ErrorMessage",config.ErrorMessage.as_str())
|
||||
);
|
||||
pub async fn create_mapfix(&self,config:CreateMapfixRequest<'_>)->Result<MapfixIDResponse,Error>{
|
||||
pub async fn create_mapfix<'a>(&self,config:CreateMapfixRequest<'a>)->Result<MapfixIDResponse,Error>{
|
||||
let url_raw=format!("{}/mapfixes",self.0.base_url);
|
||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||
|
||||
@@ -204,38 +193,27 @@ 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.0,);
|
||||
action!("mapfixes",action_mapfix_submitted,config,ActionMapfixSubmittedRequest,"status/validator-submitted",config.MapfixID.0,
|
||||
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,
|
||||
("ModelVersion",config.ModelVersion.to_string().as_str())
|
||||
("DisplayName",config.DisplayName.as_str())
|
||||
("Creator",config.Creator.as_str())
|
||||
("GameID",(config.GameID as u8).to_string().as_str())
|
||||
("GameID",config.GameID.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.0,
|
||||
action!("mapfixes",update_mapfix_validated_model,config,UpdateMapfixModelRequest,"validated-model",config.MapfixID,
|
||||
("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.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.0,
|
||||
("StatusMessage",config.StatusMessage.as_str())
|
||||
);
|
||||
action!("mapfixes",create_mapfix_audit_error,config,CreateMapfixAuditErrorRequest,"error",config.MapfixID.0,
|
||||
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())
|
||||
);
|
||||
// simple operation endpoint
|
||||
action!("operations",action_operation_failed,config,ActionOperationFailedRequest,"status/operation-failed",config.OperationID,
|
||||
("StatusMessage",config.StatusMessage.as_str())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,25 +14,21 @@ impl std::fmt::Display for Error{
|
||||
impl std::error::Error for Error{}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SingleItemError<Items>{
|
||||
DuplicateItems(Items),
|
||||
pub enum SingleItemError{
|
||||
DuplicateItems,
|
||||
Other(Error),
|
||||
}
|
||||
impl<Items> std::fmt::Display for SingleItemError<Items>
|
||||
where
|
||||
Items:std::fmt::Debug
|
||||
{
|
||||
impl std::fmt::Display for SingleItemError{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
write!(f,"{self:?}")
|
||||
}
|
||||
}
|
||||
impl<Items> std::error::Error for SingleItemError<Items> where Items:std::fmt::Debug{}
|
||||
pub type ScriptSingleItemError=SingleItemError<Vec<ScriptID>>;
|
||||
pub type ScriptPolicySingleItemError=SingleItemError<Vec<ScriptPolicyID>>;
|
||||
impl std::error::Error for SingleItemError{}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub struct UrlAndBody{
|
||||
pub struct StatusCodeWithUrlAndBody{
|
||||
pub status_code:reqwest::StatusCode,
|
||||
pub url:url::Url,
|
||||
pub body:String,
|
||||
}
|
||||
@@ -40,10 +36,7 @@ pub struct UrlAndBody{
|
||||
#[derive(Debug)]
|
||||
pub enum ResponseError{
|
||||
Reqwest(reqwest::Error),
|
||||
Details{
|
||||
status_code:reqwest::StatusCode,
|
||||
url_and_body:Box<UrlAndBody>,
|
||||
},
|
||||
StatusCodeWithUrlAndBody(StatusCodeWithUrlAndBody),
|
||||
}
|
||||
impl std::fmt::Display for ResponseError{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
@@ -60,30 +53,23 @@ pub async fn response_ok(response:reqwest::Response)->Result<reqwest::Response,R
|
||||
let url=response.url().to_owned();
|
||||
let bytes=response.bytes().await.map_err(ResponseError::Reqwest)?;
|
||||
let body=String::from_utf8_lossy(&bytes).to_string();
|
||||
Err(ResponseError::Details{
|
||||
Err(ResponseError::StatusCodeWithUrlAndBody(StatusCodeWithUrlAndBody{
|
||||
status_code,
|
||||
url_and_body:Box::new(UrlAndBody{url,body})
|
||||
})
|
||||
url,
|
||||
body,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[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:OperationID,
|
||||
pub OperationID:i32,
|
||||
pub AssetOwner:i64,
|
||||
pub DisplayName:&'a str,
|
||||
pub Creator:&'a str,
|
||||
pub GameID:GameID,
|
||||
pub GameID:i32,
|
||||
pub AssetID:u64,
|
||||
pub AssetVersion:u64,
|
||||
pub TargetAssetID:u64,
|
||||
@@ -98,11 +84,11 @@ pub struct MapfixIDResponse{
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug,serde::Serialize)]
|
||||
pub struct CreateSubmissionRequest<'a>{
|
||||
pub OperationID:OperationID,
|
||||
pub OperationID:i32,
|
||||
pub AssetOwner:i64,
|
||||
pub DisplayName:&'a str,
|
||||
pub Creator:&'a str,
|
||||
pub GameID:GameID,
|
||||
pub GameID:i32,
|
||||
pub AssetID:u64,
|
||||
pub AssetVersion:u64,
|
||||
pub Status:u32,
|
||||
@@ -145,7 +131,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<ResourceID>,
|
||||
pub ResourceID:Option<i64>,
|
||||
}
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct HashRequest<'a>{
|
||||
@@ -159,7 +145,7 @@ pub struct ScriptResponse{
|
||||
pub Hash:String,
|
||||
pub Source:String,
|
||||
pub ResourceType:ResourceType,
|
||||
pub ResourceID:ResourceID,
|
||||
pub ResourceID:i64,
|
||||
}
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug,serde::Serialize)]
|
||||
@@ -168,7 +154,7 @@ pub struct CreateScriptRequest<'a>{
|
||||
pub Source:&'a str,
|
||||
pub ResourceType:ResourceType,
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub ResourceID:Option<ResourceID>,
|
||||
pub ResourceID:Option<i64>,
|
||||
}
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug,serde::Deserialize)]
|
||||
@@ -186,10 +172,6 @@ pub enum Policy{
|
||||
Replace=4,
|
||||
}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
pub struct GetScriptPolicyRequest{
|
||||
pub ScriptPolicyID:ScriptPolicyID,
|
||||
}
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug,serde::Serialize)]
|
||||
pub struct GetScriptPoliciesRequest<'a>{
|
||||
@@ -238,160 +220,49 @@ pub struct UpdateScriptPolicyRequest{
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct UpdateSubmissionModelRequest{
|
||||
pub SubmissionID:SubmissionID,
|
||||
pub SubmissionID:i64,
|
||||
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:SubmissionID,
|
||||
pub SubmissionID:i64,
|
||||
pub ModelVersion:u64,
|
||||
pub DisplayName:String,
|
||||
pub Creator:String,
|
||||
pub GameID:GameID,
|
||||
pub GameID:u32,
|
||||
}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ActionSubmissionRequestChangesRequest{
|
||||
pub SubmissionID:SubmissionID,
|
||||
pub SubmissionID:i64,
|
||||
pub ErrorMessage:String,
|
||||
}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ActionSubmissionUploadedRequest{
|
||||
pub SubmissionID:SubmissionID,
|
||||
pub SubmissionID:i64,
|
||||
pub UploadedAssetID:u64,
|
||||
}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ActionSubmissionAcceptedRequest{
|
||||
pub SubmissionID:SubmissionID,
|
||||
}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct CreateSubmissionAuditErrorRequest{
|
||||
pub SubmissionID:SubmissionID,
|
||||
pub SubmissionID:i64,
|
||||
pub ErrorMessage:String,
|
||||
}
|
||||
|
||||
#[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);
|
||||
#[derive(Clone,Copy,Debug,serde::Deserialize)]
|
||||
pub struct SubmissionID(pub i64);
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct UpdateMapfixModelRequest{
|
||||
pub MapfixID:MapfixID,
|
||||
pub MapfixID:i64,
|
||||
pub ModelID:u64,
|
||||
pub ModelVersion:u64,
|
||||
}
|
||||
@@ -399,82 +270,39 @@ pub struct UpdateMapfixModelRequest{
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ActionMapfixSubmittedRequest{
|
||||
pub MapfixID:MapfixID,
|
||||
pub MapfixID:i64,
|
||||
pub ModelVersion:u64,
|
||||
pub DisplayName:String,
|
||||
pub Creator:String,
|
||||
pub GameID:GameID,
|
||||
pub GameID:u32,
|
||||
}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ActionMapfixRequestChangesRequest{
|
||||
pub MapfixID:MapfixID,
|
||||
}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ActionMapfixUploadedRequest{
|
||||
pub MapfixID:MapfixID,
|
||||
}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ActionMapfixAcceptedRequest{
|
||||
pub MapfixID:MapfixID,
|
||||
}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct CreateMapfixAuditErrorRequest{
|
||||
pub MapfixID:MapfixID,
|
||||
pub MapfixID:i64,
|
||||
pub ErrorMessage:String,
|
||||
}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct CreateMapfixAuditCheckListRequest<'a>{
|
||||
pub MapfixID:MapfixID,
|
||||
pub CheckList:&'a [Check],
|
||||
pub struct ActionMapfixUploadedRequest{
|
||||
pub MapfixID:i64,
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
|
||||
pub struct MapfixID(pub(crate)i64);
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ActionMapfixAcceptedRequest{
|
||||
pub MapfixID:i64,
|
||||
pub ErrorMessage:String,
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug,serde::Deserialize)]
|
||||
pub struct MapfixID(pub i64);
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ActionOperationFailedRequest{
|
||||
pub OperationID:OperationID,
|
||||
pub OperationID:i32,
|
||||
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],
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use std::collections::{HashSet,HashMap};
|
||||
use crate::download::download_asset_version;
|
||||
use crate::rbx_util::{get_mapinfo,get_root_instance,read_dom,ReadDomError,GameID,ParseGameIDError,MapInfo,GetRootInstanceError,StringValueError};
|
||||
use crate::rbx_util::{class_is_a,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)]
|
||||
@@ -13,8 +12,6 @@ pub enum Error{
|
||||
Download(crate::download::Error),
|
||||
ModelFileDecode(ReadDomError),
|
||||
GetRootInstance(GetRootInstanceError),
|
||||
IntoMapInfoOwned(IntoMapInfoOwnedError),
|
||||
ToJsonValue(serde_json::Error),
|
||||
}
|
||||
impl std::fmt::Display for Error{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
@@ -25,15 +22,13 @@ impl std::error::Error for Error{}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
pub struct CheckRequest{
|
||||
ModelID:u64,
|
||||
SkipChecks:bool,
|
||||
pub ModelID:u64,
|
||||
}
|
||||
|
||||
impl From<crate::nats_types::CheckMapfixRequest> for CheckRequest{
|
||||
fn from(value:crate::nats_types::CheckMapfixRequest)->Self{
|
||||
Self{
|
||||
ModelID:value.ModelID,
|
||||
SkipChecks:value.SkipChecks,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +36,6 @@ impl From<crate::nats_types::CheckSubmissionRequest> for CheckRequest{
|
||||
fn from(value:crate::nats_types::CheckSubmissionRequest)->Self{
|
||||
Self{
|
||||
ModelID:value.ModelID,
|
||||
SkipChecks:value.SkipChecks,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,166 +47,98 @@ impl ModeID{
|
||||
const BONUS:Self=Self(1);
|
||||
}
|
||||
enum Zone{
|
||||
Start,
|
||||
Finish,
|
||||
Anticheat,
|
||||
}
|
||||
struct ModeElement{
|
||||
zone:Zone,
|
||||
mode_id:ModeID,
|
||||
Start(ModeID),
|
||||
Finish(ModeID),
|
||||
Anticheat(ModeID),
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub enum IDParseError{
|
||||
NoCaptures,
|
||||
ParseInt(core::num::ParseIntError),
|
||||
ParseInt(core::num::ParseIntError)
|
||||
}
|
||||
// Parse a Zone from a part name
|
||||
impl std::str::FromStr for ModeElement{
|
||||
impl std::str::FromStr for Zone{
|
||||
type Err=IDParseError;
|
||||
fn from_str(s:&str)->Result<Self,Self::Err>{
|
||||
match s{
|
||||
"MapStart"=>Ok(Self{zone:Zone::Start,mode_id:ModeID::MAIN}),
|
||||
"MapFinish"=>Ok(Self{zone:Zone::Finish,mode_id:ModeID::MAIN}),
|
||||
"MapAnticheat"=>Ok(Self{zone:Zone::Anticheat,mode_id:ModeID::MAIN}),
|
||||
"BonusStart"=>Ok(Self{zone:Zone::Start,mode_id:ModeID::BONUS}),
|
||||
"BonusFinish"=>Ok(Self{zone:Zone::Finish,mode_id:ModeID::BONUS}),
|
||||
"BonusAnticheat"=>Ok(Self{zone:Zone::Anticheat,mode_id:ModeID::BONUS}),
|
||||
"MapStart"=>Ok(Self::Start(ModeID::MAIN)),
|
||||
"MapFinish"=>Ok(Self::Finish(ModeID::MAIN)),
|
||||
"MapAnticheat"=>Ok(Self::Anticheat(ModeID::MAIN)),
|
||||
"BonusStart"=>Ok(Self::Start(ModeID::BONUS)),
|
||||
"BonusFinish"=>Ok(Self::Finish(ModeID::BONUS)),
|
||||
"BonusAnticheat"=>Ok(Self::Anticheat(ModeID::BONUS)),
|
||||
other=>{
|
||||
let everything_pattern=lazy_regex::lazy_regex!(r"^Bonus(\d+)Start$|^BonusStart(\d+)$|^Bonus(\d+)Finish$|^BonusFinish(\d+)$|^Bonus(\d+)Anticheat$|^BonusAnticheat(\d+)$");
|
||||
if let Some(captures)=everything_pattern.captures(other){
|
||||
if let Some(mode_id)=captures.get(1).or(captures.get(2)){
|
||||
return Ok(Self{
|
||||
zone:Zone::Start,
|
||||
mode_id:ModeID(mode_id.as_str().parse().map_err(IDParseError::ParseInt)?),
|
||||
});
|
||||
}
|
||||
if let Some(mode_id)=captures.get(3).or(captures.get(4)){
|
||||
return Ok(Self{
|
||||
zone:Zone::Finish,
|
||||
mode_id:ModeID(mode_id.as_str().parse().map_err(IDParseError::ParseInt)?),
|
||||
});
|
||||
}
|
||||
if let Some(mode_id)=captures.get(5).or(captures.get(6)){
|
||||
return Ok(Self{
|
||||
zone:Zone::Anticheat,
|
||||
mode_id:ModeID(mode_id.as_str().parse().map_err(IDParseError::ParseInt)?),
|
||||
});
|
||||
}
|
||||
let bonus_start_pattern=lazy_regex::lazy_regex!(r"^Bonus(\d+)Start$|^BonusStart(\d+)$");
|
||||
if let Some(captures)=bonus_start_pattern.captures(other){
|
||||
return Ok(Self::Start(ModeID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
|
||||
}
|
||||
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^Bonus(\d+)Finish$|^BonusFinish(\d+)$");
|
||||
if let Some(captures)=bonus_finish_pattern.captures(other){
|
||||
return Ok(Self::Finish(ModeID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
|
||||
}
|
||||
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^Bonus(\d+)Anticheat$|^BonusAnticheat(\d+)$");
|
||||
if let Some(captures)=bonus_finish_pattern.captures(other){
|
||||
return Ok(Self::Anticheat(ModeID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
|
||||
}
|
||||
Err(IDParseError::NoCaptures)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for ModeElement{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
match self{
|
||||
ModeElement{zone:Zone::Start,mode_id:ModeID::MAIN}=>write!(f,"MapStart"),
|
||||
ModeElement{zone:Zone::Start,mode_id:ModeID::BONUS}=>write!(f,"BonusStart"),
|
||||
ModeElement{zone:Zone::Start,mode_id:ModeID(mode_id)}=>write!(f,"Bonus{mode_id}Start"),
|
||||
ModeElement{zone:Zone::Finish,mode_id:ModeID::MAIN}=>write!(f,"MapFinish"),
|
||||
ModeElement{zone:Zone::Finish,mode_id:ModeID::BONUS}=>write!(f,"BonusFinish"),
|
||||
ModeElement{zone:Zone::Finish,mode_id:ModeID(mode_id)}=>write!(f,"Bonus{mode_id}Finish"),
|
||||
ModeElement{zone:Zone::Anticheat,mode_id:ModeID::MAIN}=>write!(f,"MapAnticheat"),
|
||||
ModeElement{zone:Zone::Anticheat,mode_id:ModeID::BONUS}=>write!(f,"BonusAnticheat"),
|
||||
ModeElement{zone:Zone::Anticheat,mode_id:ModeID(mode_id)}=>write!(f,"Bonus{mode_id}Anticheat"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
||||
struct StageID(u64);
|
||||
impl StageID{
|
||||
struct SpawnID(u64);
|
||||
impl SpawnID{
|
||||
const FIRST:Self=Self(1);
|
||||
}
|
||||
enum StageElementBehaviour{
|
||||
Teleport,
|
||||
Spawn,
|
||||
enum SpawnTeleport{
|
||||
Teleport(SpawnID),
|
||||
Spawn(SpawnID),
|
||||
}
|
||||
struct StageElement{
|
||||
stage_id:StageID,
|
||||
behaviour:StageElementBehaviour,
|
||||
}
|
||||
// Parse a SpawnTeleport from a part name
|
||||
impl std::str::FromStr for StageElement{
|
||||
impl std::str::FromStr for SpawnTeleport{
|
||||
type Err=IDParseError;
|
||||
fn from_str(s:&str)->Result<Self,Self::Err>{
|
||||
// Trigger ForceTrigger Teleport ForceTeleport SpawnAt ForceSpawnAt
|
||||
let bonus_start_pattern=lazy_regex::lazy_regex!(r"^(?:Force)?(Teleport|SpawnAt|Trigger)(\d+)$");
|
||||
if let Some(captures)=bonus_start_pattern.captures(s){
|
||||
return Ok(StageElement{
|
||||
behaviour:StageElementBehaviour::Teleport,
|
||||
stage_id:StageID(captures[1].parse().map_err(IDParseError::ParseInt)?),
|
||||
});
|
||||
return Ok(Self::Teleport(SpawnID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
|
||||
}
|
||||
// Spawn
|
||||
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^Spawn(\d+)$");
|
||||
if let Some(captures)=bonus_finish_pattern.captures(s){
|
||||
return Ok(StageElement{
|
||||
behaviour:StageElementBehaviour::Spawn,
|
||||
stage_id:StageID(captures[1].parse().map_err(IDParseError::ParseInt)?),
|
||||
});
|
||||
return Ok(Self::Spawn(SpawnID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
|
||||
}
|
||||
Err(IDParseError::NoCaptures)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for StageElement{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
match self{
|
||||
StageElement{behaviour:StageElementBehaviour::Spawn,stage_id:StageID(stage_id)}=>write!(f,"Spawn{stage_id}"),
|
||||
StageElement{behaviour:StageElementBehaviour::Teleport,stage_id:StageID(stage_id)}=>write!(f,"Teleport{stage_id}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
||||
struct WormholeID(u64);
|
||||
enum WormholeBehaviour{
|
||||
In,
|
||||
Out,
|
||||
enum Wormhole{
|
||||
In(WormholeID),
|
||||
Out(WormholeID),
|
||||
}
|
||||
struct WormholeElement{
|
||||
behaviour:WormholeBehaviour,
|
||||
wormhole_id:WormholeID,
|
||||
}
|
||||
// Parse a Wormhole from a part name
|
||||
impl std::str::FromStr for WormholeElement{
|
||||
impl std::str::FromStr for Wormhole{
|
||||
type Err=IDParseError;
|
||||
fn from_str(s:&str)->Result<Self,Self::Err>{
|
||||
let bonus_start_pattern=lazy_regex::lazy_regex!(r"^WormholeIn(\d+)$");
|
||||
if let Some(captures)=bonus_start_pattern.captures(s){
|
||||
return Ok(Self{
|
||||
behaviour:WormholeBehaviour::In,
|
||||
wormhole_id:WormholeID(captures[1].parse().map_err(IDParseError::ParseInt)?),
|
||||
});
|
||||
return Ok(Self::In(WormholeID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
|
||||
}
|
||||
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^WormholeOut(\d+)$");
|
||||
if let Some(captures)=bonus_finish_pattern.captures(s){
|
||||
return Ok(Self{
|
||||
behaviour:WormholeBehaviour::Out,
|
||||
wormhole_id:WormholeID(captures[1].parse().map_err(IDParseError::ParseInt)?),
|
||||
});
|
||||
return Ok(Self::Out(WormholeID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
|
||||
}
|
||||
Err(IDParseError::NoCaptures)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for WormholeElement{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
match self{
|
||||
WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id:WormholeID(wormhole_id)}=>write!(f,"WormholeIn{wormhole_id}"),
|
||||
WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id:WormholeID(wormhole_id)}=>write!(f,"WormholeOut{wormhole_id}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Count various map elements
|
||||
#[derive(Default)]
|
||||
struct Counts<'a>{
|
||||
mode_start_counts:HashMap<ModeID,Vec<&'a str>>,
|
||||
mode_finish_counts:HashMap<ModeID,Vec<&'a str>>,
|
||||
mode_anticheat_counts:HashMap<ModeID,Vec<&'a str>>,
|
||||
teleport_counts:HashMap<StageID,Vec<&'a str>>,
|
||||
spawn_counts:HashMap<StageID,u64>,
|
||||
teleport_counts:HashMap<SpawnID,Vec<&'a str>>,
|
||||
spawn_counts:HashMap<SpawnID,u64>,
|
||||
wormhole_in_counts:HashMap<WormholeID,u64>,
|
||||
wormhole_out_counts:HashMap<WormholeID,u64>,
|
||||
}
|
||||
@@ -226,37 +152,31 @@ pub struct ModelInfo<'a>{
|
||||
|
||||
pub fn get_model_info<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&'a rbx_dom_weak::Instance)->ModelInfo<'a>{
|
||||
// extract model info
|
||||
let map_info=get_mapinfo(dom,model_instance);
|
||||
let map_info=get_mapinfo(&dom,model_instance);
|
||||
|
||||
// count objects (default count is 0)
|
||||
let mut counts=Counts::default();
|
||||
|
||||
let db=rbx_reflection_database::get();
|
||||
let base_part=&db.classes["BasePart"];
|
||||
let base_parts=dom.descendants_of(model_instance.referent()).filter(|&instance|
|
||||
db.classes.get(instance.class.as_str()).is_some_and(|class|
|
||||
db.has_superclass(class,base_part)
|
||||
)
|
||||
);
|
||||
for instance in base_parts{
|
||||
// Zones
|
||||
match instance.name.parse(){
|
||||
Ok(ModeElement{zone:Zone::Start,mode_id})=>counts.mode_start_counts.entry(mode_id).or_default().push(instance.name.as_str()),
|
||||
Ok(ModeElement{zone:Zone::Finish,mode_id})=>counts.mode_finish_counts.entry(mode_id).or_default().push(instance.name.as_str()),
|
||||
Ok(ModeElement{zone:Zone::Anticheat,mode_id})=>counts.mode_anticheat_counts.entry(mode_id).or_default().push(instance.name.as_str()),
|
||||
Err(_)=>(),
|
||||
}
|
||||
// Spawns & Teleports
|
||||
match instance.name.parse(){
|
||||
Ok(StageElement{behaviour:StageElementBehaviour::Teleport,stage_id})=>counts.teleport_counts.entry(stage_id).or_default().push(instance.name.as_str()),
|
||||
Ok(StageElement{behaviour:StageElementBehaviour::Spawn,stage_id})=>*counts.spawn_counts.entry(stage_id).or_insert(0)+=1,
|
||||
Err(_)=>(),
|
||||
}
|
||||
// Wormholes
|
||||
match instance.name.parse(){
|
||||
Ok(WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id})=>*counts.wormhole_in_counts.entry(wormhole_id).or_insert(0)+=1,
|
||||
Ok(WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id})=>*counts.wormhole_out_counts.entry(wormhole_id).or_insert(0)+=1,
|
||||
Err(_)=>(),
|
||||
for instance in dom.descendants_of(model_instance.referent()){
|
||||
if class_is_a(instance.class.as_str(),"BasePart"){
|
||||
// Zones
|
||||
match instance.name.parse(){
|
||||
Ok(Zone::Start(mode_id))=>counts.mode_start_counts.entry(mode_id).or_default().push(instance.name.as_str()),
|
||||
Ok(Zone::Finish(mode_id))=>counts.mode_finish_counts.entry(mode_id).or_default().push(instance.name.as_str()),
|
||||
Ok(Zone::Anticheat(mode_id))=>counts.mode_anticheat_counts.entry(mode_id).or_default().push(instance.name.as_str()),
|
||||
Err(_)=>(),
|
||||
}
|
||||
// Spawns & Teleports
|
||||
match instance.name.parse(){
|
||||
Ok(SpawnTeleport::Teleport(spawn_id))=>counts.teleport_counts.entry(spawn_id).or_default().push(instance.name.as_str()),
|
||||
Ok(SpawnTeleport::Spawn(spawn_id))=>*counts.spawn_counts.entry(spawn_id).or_insert(0)+=1,
|
||||
Err(_)=>(),
|
||||
}
|
||||
// Wormholes
|
||||
match instance.name.parse(){
|
||||
Ok(Wormhole::In(wormhole_id))=>*counts.wormhole_in_counts.entry(wormhole_id).or_insert(0)+=1,
|
||||
Ok(Wormhole::Out(wormhole_id))=>*counts.wormhole_out_counts.entry(wormhole_id).or_insert(0)+=1,
|
||||
Err(_)=>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +198,6 @@ impl<'a,Str> StringCheckContext<'a,Str>
|
||||
where
|
||||
&'a str:PartialEq<Str>,
|
||||
{
|
||||
/// Compute the StringCheck, passing through the provided value on success.
|
||||
fn check<T>(self,value:T)->StringCheck<'a,T,Str>{
|
||||
if self.observed==self.expected{
|
||||
StringCheck(Ok(value))
|
||||
@@ -287,7 +206,7 @@ impl<'a,Str> StringCheckContext<'a,Str>
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<Str:std::fmt::Display> std::fmt::Display for StringCheckContext<'_,Str>{
|
||||
impl<'a,Str:std::fmt::Display> std::fmt::Display for StringCheckContext<'a,Str>{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
write!(f,"expected: {}, observed: {}",self.expected,self.observed)
|
||||
}
|
||||
@@ -308,7 +227,6 @@ fn check_empty(value:&str)->Result<&str,StringEmpty>{
|
||||
pub struct DuplicateCheckContext<ID,T>(HashMap<ID,T>);
|
||||
pub struct DuplicateCheck<ID,T>(Result<(),DuplicateCheckContext<ID,T>>);
|
||||
impl<ID,T> DuplicateCheckContext<ID,T>{
|
||||
/// Compute the DuplicateCheck using the contents predicate.
|
||||
fn check(self,f:impl Fn(&T)->bool)->DuplicateCheck<ID,T>{
|
||||
let Self(mut set)=self;
|
||||
// remove correct entries
|
||||
@@ -322,11 +240,10 @@ impl<ID,T> DuplicateCheckContext<ID,T>{
|
||||
}
|
||||
}
|
||||
|
||||
// Check that there are no items which do not have a matching item in a reference set
|
||||
// check that there is at least one matching item for each item in a reference set, and no extra items
|
||||
pub struct SetDifferenceCheckContextAllowNone<ID,T>{
|
||||
extra:HashMap<ID,T>,
|
||||
}
|
||||
// Check that there is at least one matching item for each item in a reference set, and no extra items
|
||||
pub struct SetDifferenceCheckContextAtLeastOne<ID,T>{
|
||||
extra:HashMap<ID,T>,
|
||||
missing:HashSet<ID>,
|
||||
@@ -340,10 +257,9 @@ impl<ID,T> SetDifferenceCheckContextAllowNone<ID,T>{
|
||||
}
|
||||
}
|
||||
impl<ID:Eq+std::hash::Hash,T> SetDifferenceCheckContextAllowNone<ID,T>{
|
||||
/// Compute the SetDifferenceCheck result for the specified reference set.
|
||||
fn check<U>(mut self,reference_set:&HashMap<ID,U>)->SetDifferenceCheck<Self>{
|
||||
// remove correct entries
|
||||
for id in reference_set.keys(){
|
||||
for (id,_) in reference_set{
|
||||
self.extra.remove(id);
|
||||
}
|
||||
// if any entries remain, they are incorrect
|
||||
@@ -363,10 +279,9 @@ impl<ID,T> SetDifferenceCheckContextAtLeastOne<ID,T>{
|
||||
}
|
||||
}
|
||||
impl<ID:Copy+Eq+std::hash::Hash,T> SetDifferenceCheckContextAtLeastOne<ID,T>{
|
||||
/// Compute the SetDifferenceCheck result for the specified reference set.
|
||||
fn check<U>(mut self,reference_set:&HashMap<ID,U>)->SetDifferenceCheck<Self>{
|
||||
// remove correct entries
|
||||
for id in reference_set.keys(){
|
||||
for (id,_) in reference_set{
|
||||
if self.extra.remove(id).is_none(){
|
||||
// the set did not contain a required item. This is a fail
|
||||
self.missing.insert(*id);
|
||||
@@ -381,36 +296,14 @@ impl<ID:Copy+Eq+std::hash::Hash,T> SetDifferenceCheckContextAtLeastOne<ID,T>{
|
||||
}
|
||||
}
|
||||
|
||||
/// Info lifted out of a fully compliant map
|
||||
pub struct MapInfoOwned{
|
||||
pub display_name:String,
|
||||
pub creator:String,
|
||||
pub game_id:GameID,
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
#[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;
|
||||
struct Absent;
|
||||
|
||||
/// The result of every map check.
|
||||
struct MapCheck<'a>{
|
||||
// crazy!
|
||||
pub struct MapCheck<'a>{
|
||||
// === METADATA CHECKS ===
|
||||
// The root must be of class Model
|
||||
model_class:StringCheck<'a,(),&'static str>,
|
||||
@@ -428,7 +321,7 @@ struct MapCheck<'a>{
|
||||
|
||||
// === MODE CHECKS ===
|
||||
// MapStart must exist
|
||||
mapstart:Result<Exists,Absent>,
|
||||
mapstart:Result<(),()>,
|
||||
// No duplicate map starts (including bonuses)
|
||||
mode_start_counts:DuplicateCheck<ModeID,Vec<&'a str>>,
|
||||
// At least one finish zone for each start zone, and no finishes with no start
|
||||
@@ -436,11 +329,11 @@ struct MapCheck<'a>{
|
||||
// Check for dangling MapAnticheat zones (no associated MapStart)
|
||||
mode_anticheat_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<ModeID,Vec<&'a str>>>,
|
||||
// Spawn1 must exist
|
||||
spawn1:Result<Exists,Absent>,
|
||||
spawn1:Result<(),()>,
|
||||
// Check for dangling Teleport# (no associated Spawn#)
|
||||
teleport_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<StageID,Vec<&'a str>>>,
|
||||
teleport_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<SpawnID,Vec<&'a str>>>,
|
||||
// No duplicate Spawn#
|
||||
spawn_counts:DuplicateCheck<StageID,u64>,
|
||||
spawn_counts:DuplicateCheck<SpawnID,u64>,
|
||||
// Check for dangling WormholeIn# (no associated WormholeOut#)
|
||||
wormhole_in_counts:SetDifferenceCheck<SetDifferenceCheckContextAtLeastOne<WormholeID,u64>>,
|
||||
// No duplicate WormholeOut# (duplicate WormholeIn# ok)
|
||||
@@ -477,17 +370,17 @@ impl<'a> ModelInfo<'a>{
|
||||
let game_id=self.map_info.game_id;
|
||||
|
||||
// MapStart must exist
|
||||
let mapstart=if self.counts.mode_start_counts.contains_key(&ModeID::MAIN){
|
||||
Ok(Exists)
|
||||
let mapstart=if self.counts.mode_start_counts.get(&ModeID::MAIN).is_some(){
|
||||
Ok(())
|
||||
}else{
|
||||
Err(Absent)
|
||||
Err(())
|
||||
};
|
||||
|
||||
// Spawn1 must exist
|
||||
let spawn1=if self.counts.spawn_counts.contains_key(&StageID::FIRST){
|
||||
Ok(Exists)
|
||||
let spawn1=if self.counts.spawn_counts.get(&SpawnID::FIRST).is_some(){
|
||||
Ok(())
|
||||
}else{
|
||||
Err(Absent)
|
||||
Err(())
|
||||
};
|
||||
|
||||
// Check that at least one finish zone exists for each start zone.
|
||||
@@ -538,8 +431,8 @@ impl<'a> ModelInfo<'a>{
|
||||
}
|
||||
}
|
||||
|
||||
impl MapCheck<'_>{
|
||||
fn result(self)->Result<MapInfoOwned,Result<MapCheckList,serde_json::Error>>{
|
||||
impl<'a> MapCheck<'a>{
|
||||
fn pass(self)->Result<MapInfoOwned,Self>{
|
||||
match self{
|
||||
MapCheck{
|
||||
model_class:StringCheck(Ok(())),
|
||||
@@ -547,11 +440,11 @@ impl MapCheck<'_>{
|
||||
display_name:Ok(Ok(StringCheck(Ok(display_name)))),
|
||||
creator:Ok(Ok(creator)),
|
||||
game_id:Ok(game_id),
|
||||
mapstart:Ok(Exists),
|
||||
mapstart:Ok(()),
|
||||
mode_start_counts:DuplicateCheck(Ok(())),
|
||||
mode_finish_counts:SetDifferenceCheck(Ok(())),
|
||||
mode_anticheat_counts:SetDifferenceCheck(Ok(())),
|
||||
spawn1:Ok(Exists),
|
||||
spawn1:Ok(()),
|
||||
teleport_counts:SetDifferenceCheck(Ok(())),
|
||||
spawn_counts:DuplicateCheck(Ok(())),
|
||||
wormhole_in_counts:SetDifferenceCheck(Ok(())),
|
||||
@@ -563,253 +456,157 @@ impl MapCheck<'_>{
|
||||
game_id,
|
||||
})
|
||||
},
|
||||
other=>Err(other.itemize()),
|
||||
other=>Err(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Separated<F>{
|
||||
f:F,
|
||||
separator:&'static str,
|
||||
}
|
||||
impl<F> Separated<F>{
|
||||
fn new(separator:&'static str,f:F)->Self{
|
||||
Self{separator,f}
|
||||
fn write_comma_separated<T>(
|
||||
f:&mut std::fmt::Formatter<'_>,
|
||||
mut it:impl Iterator<Item=T>,
|
||||
custom_write:impl Fn(&mut std::fmt::Formatter<'_>,T)->std::fmt::Result
|
||||
)->std::fmt::Result{
|
||||
if let Some(t)=it.next(){
|
||||
custom_write(f,t)?;
|
||||
for t in it{
|
||||
write!(f,", ")?;
|
||||
custom_write(f,t)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
impl<F,I,D> std::fmt::Display for Separated<F>
|
||||
where
|
||||
D:std::fmt::Display,
|
||||
I:IntoIterator<Item=D>,
|
||||
F:Fn()->I,
|
||||
{
|
||||
|
||||
macro_rules! write_zone{
|
||||
($f:expr,$mode:expr,$zone:expr)=>{
|
||||
match $mode{
|
||||
ModeID(0)=>write!($f,concat!("Map",$zone)),
|
||||
ModeID(1)=>write!($f,concat!("Bonus",$zone)),
|
||||
ModeID(other)=>write!($f,concat!("Bonus{}",$zone),other),
|
||||
}
|
||||
};
|
||||
}
|
||||
impl<'a> std::fmt::Display for MapCheck<'a>{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
let mut it=(self.f)().into_iter();
|
||||
if let Some(first)=it.next(){
|
||||
write!(f,"{first}")?;
|
||||
for item in it{
|
||||
write!(f,"{}{item}",self.separator)?;
|
||||
if let StringCheck(Err(context))=&self.model_class{
|
||||
writeln!(f,"Invalid model class: {context}")?;
|
||||
}
|
||||
if let StringCheck(Err(context))=&self.model_name{
|
||||
writeln!(f,"Model name must have snake_case: {context}")?;
|
||||
}
|
||||
match &self.display_name{
|
||||
Ok(Ok(StringCheck(Ok(_))))=>(),
|
||||
Ok(Ok(StringCheck(Err(context))))=>writeln!(f,"DisplayName must have Title Case: {context}")?,
|
||||
Ok(Err(context))=>writeln!(f,"Invalid DisplayName: {context}")?,
|
||||
Err(StringValueError::ObjectNotFound)=>writeln!(f,"Missing DisplayName StringValue")?,
|
||||
Err(StringValueError::ValueNotSet)=>writeln!(f,"DisplayName Value not set")?,
|
||||
Err(StringValueError::NonStringValue)=>writeln!(f,"DisplayName Value is not a String")?,
|
||||
}
|
||||
match &self.creator{
|
||||
Ok(Ok(_))=>(),
|
||||
Ok(Err(context))=>writeln!(f,"Invalid Creator: {context}")?,
|
||||
Err(StringValueError::ObjectNotFound)=>writeln!(f,"Missing Creator StringValue")?,
|
||||
Err(StringValueError::ValueNotSet)=>writeln!(f,"Creator Value not set")?,
|
||||
Err(StringValueError::NonStringValue)=>writeln!(f,"Creator Value is not a String")?,
|
||||
}
|
||||
if let Err(_parse_game_id_error)=&self.game_id{
|
||||
writeln!(f,"Model name must be prefixed with bhop_ surf_ or flytrials_")?;
|
||||
}
|
||||
if let Err(())=&self.mapstart{
|
||||
writeln!(f,"Model has no MapStart")?;
|
||||
}
|
||||
if let DuplicateCheck(Err(DuplicateCheckContext(context)))=&self.mode_start_counts{
|
||||
write!(f,"Duplicate start zones: ")?;
|
||||
write_comma_separated(f,context.iter(),|f,(mode_id,names)|{
|
||||
write_zone!(f,mode_id,"Start")?;
|
||||
write!(f," ({} duplicates)",names.len())?;
|
||||
Ok(())
|
||||
})?;
|
||||
writeln!(f,"")?;
|
||||
}
|
||||
if let SetDifferenceCheck(Err(context))=&self.mode_finish_counts{
|
||||
if !context.extra.is_empty(){
|
||||
let plural=if context.extra.len()==1{"zone"}else{"zones"};
|
||||
write!(f,"No matching start zone for finish {plural}: ")?;
|
||||
write_comma_separated(f,context.extra.iter(),|f,(mode_id,_names)|
|
||||
write_zone!(f,mode_id,"Finish")
|
||||
)?;
|
||||
writeln!(f,"")?;
|
||||
}
|
||||
if !context.missing.is_empty(){
|
||||
let plural=if context.missing.len()==1{"zone"}else{"zones"};
|
||||
write!(f,"Missing finish {plural}: ")?;
|
||||
write_comma_separated(f,context.missing.iter(),|f,mode_id|
|
||||
write_zone!(f,mode_id,"Finish")
|
||||
)?;
|
||||
writeln!(f,"")?;
|
||||
}
|
||||
}
|
||||
if let SetDifferenceCheck(Err(context))=&self.mode_anticheat_counts{
|
||||
if !context.extra.is_empty(){
|
||||
let plural=if context.extra.len()==1{"zone"}else{"zones"};
|
||||
write!(f,"No matching start zone for anticheat {plural}: ")?;
|
||||
write_comma_separated(f,context.extra.iter(),|f,(mode_id,_names)|
|
||||
write_zone!(f,mode_id,"Anticheat")
|
||||
)?;
|
||||
writeln!(f,"")?;
|
||||
}
|
||||
}
|
||||
if let Err(())=&self.spawn1{
|
||||
writeln!(f,"Model has no Spawn1")?;
|
||||
}
|
||||
if let SetDifferenceCheck(Err(context))=&self.teleport_counts{
|
||||
for (_,names) in &context.extra{
|
||||
let plural=if names.len()==1{"object"}else{"objects"};
|
||||
write!(f,"No matching Spawn for {plural}: ")?;
|
||||
write_comma_separated(f,names.iter(),|f,&name|{
|
||||
write!(f,"{name}")
|
||||
})?;
|
||||
writeln!(f,"")?;
|
||||
}
|
||||
}
|
||||
if let DuplicateCheck(Err(DuplicateCheckContext(context)))=&self.spawn_counts{
|
||||
write!(f,"Duplicate Spawn: ")?;
|
||||
write_comma_separated(f,context.iter(),|f,(SpawnID(spawn_id),count)|
|
||||
write!(f,"Spawn{spawn_id} ({count} duplicates)")
|
||||
)?;
|
||||
writeln!(f,"")?;
|
||||
}
|
||||
if let SetDifferenceCheck(Err(context))=&self.wormhole_in_counts{
|
||||
if !context.extra.is_empty(){
|
||||
write!(f,"WormholeIn with no matching WormholeOut: ")?;
|
||||
write_comma_separated(f,context.extra.iter(),|f,(WormholeID(wormhole_id),_count)|
|
||||
write!(f,"WormholeIn{wormhole_id}")
|
||||
)?;
|
||||
writeln!(f,"")?;
|
||||
}
|
||||
if !context.missing.is_empty(){
|
||||
// This counts WormholeIn objects, but
|
||||
// flipped logic is easier to understand
|
||||
write!(f,"WormholeOut with no matching WormholeIn: ")?;
|
||||
write_comma_separated(f,context.missing.iter(),|f,WormholeID(wormhole_id)|
|
||||
write!(f,"WormholeOut{wormhole_id}")
|
||||
)?;
|
||||
writeln!(f,"")?;
|
||||
}
|
||||
}
|
||||
if let DuplicateCheck(Err(DuplicateCheckContext(context)))=&self.wormhole_out_counts{
|
||||
write!(f,"Duplicate WormholeOut: ")?;
|
||||
write_comma_separated(f,context.iter(),|f,(WormholeID(wormhole_id),count)|
|
||||
write!(f,"WormholeOut{wormhole_id} ({count} duplicates)")
|
||||
)?;
|
||||
writeln!(f,"")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Duplicates<D>{
|
||||
display:D,
|
||||
duplicates:usize,
|
||||
}
|
||||
impl<D> Duplicates<D>{
|
||||
fn new(display:D,duplicates:usize)->Self{
|
||||
Self{
|
||||
display,
|
||||
duplicates,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<D:std::fmt::Display> std::fmt::Display for Duplicates<D>{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
write!(f,"{} ({} duplicates)",self.display,self.duplicates)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
macro_rules! passed{
|
||||
($name:literal)=>{
|
||||
Check{
|
||||
Name:$name,
|
||||
Summary:String::new(),
|
||||
Passed:true,
|
||||
}
|
||||
}
|
||||
}
|
||||
macro_rules! summary{
|
||||
($name:literal,$summary:expr)=>{
|
||||
Check{
|
||||
Name:$name,
|
||||
Summary:$summary,
|
||||
Passed:false,
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! summary_format{
|
||||
($name:literal,$fmt:literal)=>{
|
||||
Check{
|
||||
Name:$name,
|
||||
Summary:format!($fmt),
|
||||
Passed:false,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Generate an error message for each observed issue separated by newlines.
|
||||
// This defines MapCheck.to_string() which is used in MapCheck.result()
|
||||
impl MapCheck<'_>{
|
||||
fn itemize(&self)->Result<MapCheckList,serde_json::Error>{
|
||||
let model_class=match &self.model_class{
|
||||
StringCheck(Ok(()))=>passed!("ModelClass"),
|
||||
StringCheck(Err(context))=>summary_format!("ModelClass","Invalid model class: {context}"),
|
||||
};
|
||||
let model_name=match &self.model_name{
|
||||
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(_))))=>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(_))=>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(_)=>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)=>passed!("MapStart"),
|
||||
Err(Absent)=>summary_format!("MapStart","Model has no MapStart"),
|
||||
};
|
||||
let duplicate_start=match &self.mode_start_counts{
|
||||
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}")
|
||||
}
|
||||
};
|
||||
let (extra_finish,missing_finish)=match &self.mode_finish_counts{
|
||||
SetDifferenceCheck(Ok(()))=>(passed!("DanglingFinish"),passed!("MissingFinish")),
|
||||
SetDifferenceCheck(Err(context))=>(
|
||||
if context.extra.is_empty(){
|
||||
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!("DanglingFinish","No matching start zone for finish {plural}: {context}")
|
||||
},
|
||||
if context.missing.is_empty(){
|
||||
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}")
|
||||
}
|
||||
),
|
||||
};
|
||||
let dangling_anticheat=match &self.mode_anticheat_counts{
|
||||
SetDifferenceCheck(Ok(()))=>passed!("DanglingAnticheat"),
|
||||
SetDifferenceCheck(Err(context))=>{
|
||||
if context.extra.is_empty(){
|
||||
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}")
|
||||
}
|
||||
}
|
||||
};
|
||||
let spawn1=match &self.spawn1{
|
||||
Ok(Exists)=>passed!("Spawn1"),
|
||||
Err(Absent)=>summary_format!("Spawn1","Model has no Spawn1"),
|
||||
};
|
||||
let dangling_teleport=match &self.teleport_counts{
|
||||
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}")
|
||||
}
|
||||
};
|
||||
let duplicate_spawns=match &self.spawn_counts{
|
||||
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}")
|
||||
}
|
||||
};
|
||||
let (extra_wormhole_in,missing_wormhole_in)=match &self.wormhole_in_counts{
|
||||
SetDifferenceCheck(Ok(()))=>(passed!("ExtraWormholeIn"),passed!("MissingWormholeIn")),
|
||||
SetDifferenceCheck(Err(context))=>(
|
||||
if context.extra.is_empty(){
|
||||
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}")
|
||||
},
|
||||
if context.missing.is_empty(){
|
||||
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}")
|
||||
}
|
||||
)
|
||||
};
|
||||
let duplicate_wormhole_out=match &self.wormhole_out_counts{
|
||||
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}")
|
||||
}
|
||||
};
|
||||
Ok(MapCheckList{checks:Box::new([
|
||||
model_class,
|
||||
model_name,
|
||||
display_name,
|
||||
creator,
|
||||
game_id,
|
||||
mapstart,
|
||||
duplicate_start,
|
||||
extra_finish,
|
||||
missing_finish,
|
||||
dangling_anticheat,
|
||||
spawn1,
|
||||
dangling_teleport,
|
||||
duplicate_spawns,
|
||||
extra_wormhole_in,
|
||||
missing_wormhole_in,
|
||||
duplicate_wormhole_out,
|
||||
])})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct MapCheckList{
|
||||
pub checks:Box<[Check;16]>,
|
||||
}
|
||||
|
||||
pub struct CheckListAndVersion{
|
||||
pub status:Result<MapInfoOwned,MapCheckList>,
|
||||
pub struct CheckReportAndVersion{
|
||||
pub status:Result<MapInfoOwned,String>,
|
||||
pub version:u64,
|
||||
}
|
||||
|
||||
impl crate::message_handler::MessageHandler{
|
||||
pub async fn check_inner(&self,check_info:CheckRequest)->Result<CheckListAndVersion,Error>{
|
||||
pub async fn check_inner(&self,check_info:CheckRequest)->Result<CheckReportAndVersion,Error>{
|
||||
// discover asset creator and latest version
|
||||
let info=self.cloud_context.get_asset_info(
|
||||
rbx_asset::cloud::GetAssetLatestRequest{asset_id:check_info.ModelID}
|
||||
@@ -834,17 +631,6 @@ 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(CheckListAndVersion{status,version});
|
||||
}
|
||||
|
||||
// extract information from the model
|
||||
let model_info=get_model_info(&dom,model_instance);
|
||||
|
||||
@@ -852,12 +638,8 @@ impl crate::message_handler::MessageHandler{
|
||||
let map_check=model_info.check();
|
||||
|
||||
// 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(check_list))=>Err(check_list),
|
||||
Err(Err(e))=>return Err(Error::ToJsonValue(e)),
|
||||
};
|
||||
let status=map_check.pass().map_err(|e|e.to_string());
|
||||
|
||||
Ok(CheckListAndVersion{status,version})
|
||||
Ok(CheckReportAndVersion{status,version})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::check::CheckListAndVersion;
|
||||
use crate::check::CheckReportAndVersion;
|
||||
use crate::nats_types::CheckMapfixRequest;
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -21,47 +21,32 @@ impl crate::message_handler::MessageHandler{
|
||||
|
||||
// update the mapfix depending on the result
|
||||
match check_result{
|
||||
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(CheckListAndVersion{status:Err(check_list),..})=>self.api.create_mapfix_audit_check_list(
|
||||
submissions_api::types::CreateMapfixAuditCheckListRequest{
|
||||
Ok(CheckReportAndVersion{status:Ok(map_info),version})=>self.api.action_mapfix_submitted(
|
||||
submissions_api::types::ActionMapfixSubmittedRequest{
|
||||
MapfixID:mapfix_id,
|
||||
CheckList:check_list.checks.as_slice(),
|
||||
ModelVersion:version,
|
||||
DisplayName:map_info.display_name,
|
||||
Creator:map_info.creator,
|
||||
GameID:map_info.game_id as u32,
|
||||
}
|
||||
).await.map_err(Error::ApiActionMapfixCheck)?,
|
||||
// update the mapfix model status to request changes
|
||||
Err(e)=>{
|
||||
// log error
|
||||
println!("[check_mapfix] Error: {e}");
|
||||
|
||||
self.api.create_mapfix_audit_error(
|
||||
submissions_api::types::CreateMapfixAuditErrorRequest{
|
||||
MapfixID:mapfix_id,
|
||||
ErrorMessage:e.to_string(),
|
||||
}
|
||||
).await.map_err(Error::ApiActionMapfixCheck)?;
|
||||
},
|
||||
Ok(CheckReportAndVersion{status:Err(report),..})=>self.api.action_mapfix_request_changes(
|
||||
submissions_api::types::ActionMapfixRequestChangesRequest{
|
||||
MapfixID:mapfix_id,
|
||||
ErrorMessage:report,
|
||||
}
|
||||
).await.map_err(Error::ApiActionMapfixCheck)?,
|
||||
// TODO: report the error
|
||||
// update the mapfix model status to request changes
|
||||
Err(e)=>self.api.action_mapfix_request_changes(
|
||||
submissions_api::types::ActionMapfixRequestChangesRequest{
|
||||
MapfixID:mapfix_id,
|
||||
ErrorMessage:e.to_string(),
|
||||
}
|
||||
).await.map_err(Error::ApiActionMapfixCheck)?,
|
||||
}
|
||||
|
||||
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::CheckListAndVersion;
|
||||
use crate::check::CheckReportAndVersion;
|
||||
use crate::nats_types::CheckSubmissionRequest;
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -22,47 +22,32 @@ impl crate::message_handler::MessageHandler{
|
||||
// update the submission depending on the result
|
||||
match check_result{
|
||||
// update the submission model status to submitted
|
||||
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(CheckListAndVersion{status:Err(check_list),..})=>self.api.create_submission_audit_check_list(
|
||||
submissions_api::types::CreateSubmissionAuditCheckListRequest{
|
||||
Ok(CheckReportAndVersion{status:Ok(map_info),version})=>self.api.action_submission_submitted(
|
||||
submissions_api::types::ActionSubmissionSubmittedRequest{
|
||||
SubmissionID:submission_id,
|
||||
CheckList:check_list.checks.as_slice(),
|
||||
ModelVersion:version,
|
||||
DisplayName:map_info.display_name,
|
||||
Creator:map_info.creator,
|
||||
GameID:map_info.game_id as u32,
|
||||
}
|
||||
).await.map_err(Error::ApiActionSubmissionCheck)?,
|
||||
// update the submission model status to request changes
|
||||
Err(e)=>{
|
||||
// log error
|
||||
println!("[check_submission] Error: {e}");
|
||||
|
||||
self.api.create_submission_audit_error(
|
||||
submissions_api::types::CreateSubmissionAuditErrorRequest{
|
||||
SubmissionID:submission_id,
|
||||
ErrorMessage:e.to_string(),
|
||||
}
|
||||
).await.map_err(Error::ApiActionSubmissionCheck)?;
|
||||
},
|
||||
Ok(CheckReportAndVersion{status:Err(report),..})=>self.api.action_submission_request_changes(
|
||||
submissions_api::types::ActionSubmissionRequestChangesRequest{
|
||||
SubmissionID:submission_id,
|
||||
ErrorMessage:report,
|
||||
}
|
||||
).await.map_err(Error::ApiActionSubmissionCheck)?,
|
||||
// TODO: report the error
|
||||
// update the submission model status to request changes
|
||||
Err(e)=>self.api.action_submission_request_changes(
|
||||
submissions_api::types::ActionSubmissionRequestChangesRequest{
|
||||
SubmissionID:submission_id,
|
||||
ErrorMessage:e.to_string(),
|
||||
}
|
||||
).await.map_err(Error::ApiActionSubmissionCheck)?,
|
||||
}
|
||||
|
||||
self.api.action_submission_request_changes(
|
||||
submissions_api::types::ActionSubmissionRequestChangesRequest{
|
||||
SubmissionID:submission_id,
|
||||
}
|
||||
).await.map_err(Error::ApiActionSubmissionCheck)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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).into(),
|
||||
GameID:create_request.GameID.unwrap_or(crate::rbx_util::GameID::Bhop) as i32,
|
||||
AssetID:create_info.ModelID,
|
||||
AssetVersion:create_request.AssetVersion,
|
||||
TargetAssetID:create_info.TargetAssetID,
|
||||
@@ -43,9 +43,6 @@ impl crate::message_handler::MessageHandler{
|
||||
let create_result=self.create_mapfix_inner(create_info).await;
|
||||
|
||||
if let Err(e)=create_result{
|
||||
// log error
|
||||
println!("[create_mapfix] Error: {e}");
|
||||
|
||||
self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{
|
||||
OperationID:operation_id,
|
||||
StatusMessage:e.to_string(),
|
||||
|
||||
@@ -42,7 +42,7 @@ impl crate::message_handler::MessageHandler{
|
||||
AssetOwner:create_request.AssetOwner as i64,
|
||||
DisplayName:display_name,
|
||||
Creator:creator,
|
||||
GameID:game_id.into(),
|
||||
GameID:game_id as i32,
|
||||
AssetID:create_info.ModelID,
|
||||
AssetVersion:create_request.AssetVersion,
|
||||
Status:create_info.Status,
|
||||
@@ -57,9 +57,6 @@ impl crate::message_handler::MessageHandler{
|
||||
let create_result=self.create_submission_inner(create_info).await;
|
||||
|
||||
if let Err(e)=create_result{
|
||||
// log error
|
||||
println!("[create_submission] Error: {e}");
|
||||
|
||||
self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{
|
||||
OperationID:operation_id,
|
||||
StatusMessage:e.to_string(),
|
||||
|
||||
@@ -3,6 +3,7 @@ use futures::StreamExt;
|
||||
mod rbx_util;
|
||||
mod message_handler;
|
||||
mod nats_types;
|
||||
mod types;
|
||||
mod download;
|
||||
mod check;
|
||||
mod check_mapfix;
|
||||
@@ -42,7 +43,7 @@ async fn main()->Result<(),StartupError>{
|
||||
"None"=>None,
|
||||
_=>Some(s.parse().expect("ROBLOX_GROUP_ID int parse")),
|
||||
},
|
||||
Err(e)=>panic!("{e}: ROBLOX_GROUP_ID env required"),
|
||||
Err(e)=>Err(e).expect("ROBLOX_GROUP_ID env required"),
|
||||
};
|
||||
|
||||
// create / upload models through STRAFESNET_CI2 account
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
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
|
||||
|
||||
@@ -10,7 +8,7 @@ use submissions_api::types::{SubmissionID,MapfixID,OperationID};
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct CreateSubmissionRequest{
|
||||
// operation_id is passed back in the response message
|
||||
pub OperationID:OperationID,
|
||||
pub OperationID:i32,
|
||||
pub ModelID:u64,
|
||||
pub DisplayName:String,
|
||||
pub Creator:String,
|
||||
@@ -23,7 +21,7 @@ pub struct CreateSubmissionRequest{
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct CreateMapfixRequest{
|
||||
pub OperationID:OperationID,
|
||||
pub OperationID:i32,
|
||||
pub ModelID:u64,
|
||||
pub TargetAssetID:u64,
|
||||
pub Description:String,
|
||||
@@ -32,24 +30,22 @@ pub struct CreateMapfixRequest{
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct CheckSubmissionRequest{
|
||||
pub SubmissionID:SubmissionID,
|
||||
pub SubmissionID:i64,
|
||||
pub ModelID:u64,
|
||||
pub SkipChecks:bool,
|
||||
}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct CheckMapfixRequest{
|
||||
pub MapfixID:MapfixID,
|
||||
pub MapfixID:i64,
|
||||
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:SubmissionID,
|
||||
pub SubmissionID:i64,
|
||||
pub ModelID:u64,
|
||||
pub ModelVersion:u64,
|
||||
pub ValidatedModelID:Option<u64>,
|
||||
@@ -59,7 +55,7 @@ pub struct ValidateSubmissionRequest{
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct ValidateMapfixRequest{
|
||||
// submission_id is passed back in the response message
|
||||
pub MapfixID:MapfixID,
|
||||
pub MapfixID:i64,
|
||||
pub ModelID:u64,
|
||||
pub ModelVersion:u64,
|
||||
pub ValidatedModelID:Option<u64>,
|
||||
@@ -69,7 +65,7 @@ pub struct ValidateMapfixRequest{
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct UploadSubmissionRequest{
|
||||
pub SubmissionID:SubmissionID,
|
||||
pub SubmissionID:i64,
|
||||
pub ModelID:u64,
|
||||
pub ModelVersion:u64,
|
||||
pub ModelName:String,
|
||||
@@ -78,7 +74,7 @@ pub struct UploadSubmissionRequest{
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct UploadMapfixRequest{
|
||||
pub MapfixID:MapfixID,
|
||||
pub MapfixID:i64,
|
||||
pub ModelID:u64,
|
||||
pub ModelVersion:u64,
|
||||
pub TargetAssetID:u64,
|
||||
|
||||
@@ -24,12 +24,28 @@ pub fn read_dom<R:std::io::Read>(input:R)->Result<rbx_dom_weak::WeakDom,ReadDomE
|
||||
}
|
||||
}
|
||||
|
||||
pub fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
|
||||
rbx_dom_weak::ustr(s)
|
||||
pub fn class_is_a(class:&str,superclass:&str)->bool{
|
||||
if class==superclass{
|
||||
return true
|
||||
}
|
||||
let class_descriptor=rbx_reflection_database::get().classes.get(class);
|
||||
if let Some(descriptor)=&class_descriptor{
|
||||
if let Some(class_super)=&descriptor.superclass{
|
||||
return class_is_a(&class_super,superclass)
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn find_first_child_name_and_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str,class:&str)->Option<&'a rbx_dom_weak::Instance>{
|
||||
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.name==name&&inst.class==class)
|
||||
pub fn find_first_child_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str,class:&str)->Option<&'a rbx_dom_weak::Instance>{
|
||||
for &referent in instance.children(){
|
||||
if let Some(c)=dom.get_by_ref(referent){
|
||||
if c.name==name&&class_is_a(c.class.as_str(),class) {
|
||||
return Some(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub enum GameID{
|
||||
@@ -37,15 +53,6 @@ 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{
|
||||
@@ -60,7 +67,7 @@ impl std::str::FromStr for GameID{
|
||||
if s.starts_with("flytrials_"){
|
||||
return Ok(GameID::FlyTrials);
|
||||
}
|
||||
Err(ParseGameIDError)
|
||||
return Err(ParseGameIDError);
|
||||
}
|
||||
}
|
||||
pub struct GameIDError;
|
||||
@@ -91,7 +98,7 @@ pub enum StringValueError{
|
||||
|
||||
fn string_value(instance:Option<&rbx_dom_weak::Instance>)->Result<&str,StringValueError>{
|
||||
let instance=instance.ok_or(StringValueError::ObjectNotFound)?;
|
||||
let value=instance.properties.get(&static_ustr("Value")).ok_or(StringValueError::ValueNotSet)?;
|
||||
let value=instance.properties.get("Value").ok_or(StringValueError::ValueNotSet)?;
|
||||
match value{
|
||||
rbx_dom_weak::types::Variant::String(value)=>Ok(value),
|
||||
_=>Err(StringValueError::NonStringValue),
|
||||
@@ -115,8 +122,8 @@ pub fn get_root_instance(dom:&rbx_dom_weak::WeakDom)->Result<&rbx_dom_weak::Inst
|
||||
|
||||
pub fn get_mapinfo<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&rbx_dom_weak::Instance)->MapInfo<'a>{
|
||||
MapInfo{
|
||||
display_name:string_value(find_first_child_name_and_class(dom,model_instance,"DisplayName","StringValue")),
|
||||
creator:string_value(find_first_child_name_and_class(dom,model_instance,"Creator","StringValue")),
|
||||
display_name:string_value(find_first_child_class(dom,model_instance,"DisplayName","StringValue")),
|
||||
creator:string_value(find_first_child_class(dom,model_instance,"Creator","StringValue")),
|
||||
game_id:model_instance.name.parse(),
|
||||
}
|
||||
}
|
||||
|
||||
4
validation/src/types.rs
Normal file
4
validation/src/types.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub enum ResourceID{
|
||||
Mapfix(i64),
|
||||
Submission(i64),
|
||||
}
|
||||
@@ -22,23 +22,14 @@ impl crate::message_handler::MessageHandler{
|
||||
Ok(())=>{
|
||||
// update the mapfix model status to validated
|
||||
self.api.action_mapfix_validated(
|
||||
mapfix_id
|
||||
submissions_api::types::MapfixID(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,23 +22,14 @@ impl crate::message_handler::MessageHandler{
|
||||
Ok(())=>{
|
||||
// update the submission model status to validated
|
||||
self.api.action_submission_validated(
|
||||
submission_id
|
||||
submissions_api::types::SubmissionID(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,8 +1,9 @@
|
||||
use futures::TryStreamExt;
|
||||
use submissions_api::types::Resource;
|
||||
use submissions_api::types::ResourceType;
|
||||
|
||||
use crate::download::download_asset_version;
|
||||
use crate::rbx_util::{read_dom,static_ustr,ReadDomError};
|
||||
use crate::rbx_util::{class_is_a,read_dom,ReadDomError};
|
||||
use crate::types::ResourceID;
|
||||
|
||||
const SCRIPT_CONCURRENCY:usize=16;
|
||||
|
||||
@@ -20,7 +21,7 @@ struct NamePolicy{
|
||||
}
|
||||
|
||||
fn source_has_illegal_keywords(source:&str)->bool{
|
||||
source.contains("getfenv")||source.contains("require")
|
||||
source.find("getfenv").is_some()||source.find("require").is_some()
|
||||
}
|
||||
|
||||
fn hash_source(source:&str)->String{
|
||||
@@ -33,22 +34,16 @@ fn hash_source(source:&str)->String{
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub enum Error{
|
||||
ModelInfoDownload(rbx_asset::cloud::GetError),
|
||||
CreatorTypeMustBeUser,
|
||||
RevisionMismatch{
|
||||
current:u64,
|
||||
submitted:u64,
|
||||
},
|
||||
ScriptFlaggedIllegalKeyword(String),
|
||||
ScriptBlocked(Option<submissions_api::types::ScriptID>),
|
||||
ScriptNotYetReviewed(Option<submissions_api::types::ScriptID>),
|
||||
Download(crate::download::Error),
|
||||
ModelFileDecode(ReadDomError),
|
||||
ApiGetScriptPolicyFromHash(submissions_api::types::ScriptPolicySingleItemError),
|
||||
ApiGetScriptPolicyFromHash(submissions_api::types::SingleItemError),
|
||||
ApiGetScript(submissions_api::Error),
|
||||
ApiCreateScript(submissions_api::Error),
|
||||
ApiCreateScriptPolicy(submissions_api::Error),
|
||||
ApiGetScriptFromHash(submissions_api::types::ScriptSingleItemError),
|
||||
ApiGetScriptFromHash(submissions_api::types::SingleItemError),
|
||||
ApiUpdateMapfixModel(submissions_api::Error),
|
||||
ApiUpdateSubmissionModel(submissions_api::Error),
|
||||
ModelFileRootMustHaveOneChild,
|
||||
@@ -69,7 +64,7 @@ pub struct ValidateRequest{
|
||||
pub ModelID:u64,
|
||||
pub ModelVersion:u64,
|
||||
pub ValidatedModelID:Option<u64>,
|
||||
pub Resource:Resource,
|
||||
pub ResourceID:ResourceID,
|
||||
}
|
||||
|
||||
impl From<crate::nats_types::ValidateMapfixRequest> for ValidateRequest{
|
||||
@@ -78,7 +73,7 @@ impl From<crate::nats_types::ValidateMapfixRequest> for ValidateRequest{
|
||||
ModelID:value.ModelID,
|
||||
ModelVersion:value.ModelVersion,
|
||||
ValidatedModelID:value.ValidatedModelID,
|
||||
Resource:Resource::Mapfix(value.MapfixID),
|
||||
ResourceID:ResourceID::Mapfix(value.MapfixID),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,31 +83,13 @@ impl From<crate::nats_types::ValidateSubmissionRequest> for ValidateRequest{
|
||||
ModelID:value.ModelID,
|
||||
ModelVersion:value.ModelVersion,
|
||||
ValidatedModelID:value.ValidatedModelID,
|
||||
Resource:Resource::Submission(value.SubmissionID),
|
||||
ResourceID:ResourceID::Submission(value.SubmissionID),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::message_handler::MessageHandler{
|
||||
pub async fn validate_inner(&self,validate_info:ValidateRequest)->Result<(),Error>{
|
||||
// discover asset creator and latest version
|
||||
let info=self.cloud_context.get_asset_info(
|
||||
rbx_asset::cloud::GetAssetLatestRequest{asset_id:validate_info.ModelID}
|
||||
).await.map_err(Error::ModelInfoDownload)?;
|
||||
|
||||
// reject models created by a group
|
||||
let rbx_asset::cloud::Creator::userId(_user_id)=info.creationContext.creator else{
|
||||
return Err(Error::CreatorTypeMustBeUser);
|
||||
};
|
||||
|
||||
// Has the map been updated since it was submitted?
|
||||
if info.revisionId!=validate_info.ModelVersion{
|
||||
return Err(Error::RevisionMismatch{
|
||||
current:info.revisionId,
|
||||
submitted:validate_info.ModelVersion,
|
||||
});
|
||||
}
|
||||
|
||||
// download the map model
|
||||
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
|
||||
asset_id:validate_info.ModelID,
|
||||
@@ -124,15 +101,12 @@ impl crate::message_handler::MessageHandler{
|
||||
|
||||
/* VALIDATE MAP */
|
||||
|
||||
// stupid ustr thing
|
||||
let source_property=static_ustr("Source");
|
||||
|
||||
// collect unique scripts
|
||||
let script_refs=get_script_refs(&dom);
|
||||
let mut script_map=std::collections::HashMap::<String,NamePolicy>::new();
|
||||
for &script_ref in &script_refs{
|
||||
if let Some(script)=dom.get_by_ref(script_ref){
|
||||
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get(&source_property){
|
||||
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get("Source"){
|
||||
// check the source for illegal keywords
|
||||
if source_has_illegal_keywords(source){
|
||||
// immediately abort
|
||||
@@ -176,7 +150,10 @@ impl crate::message_handler::MessageHandler{
|
||||
},
|
||||
};
|
||||
}else{
|
||||
let (resource_type,resource_id)=validate_info.Resource.split();
|
||||
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),
|
||||
};
|
||||
|
||||
// upload the script
|
||||
let script=self.api.create_script(submissions_api::types::CreateScriptRequest{
|
||||
@@ -202,7 +179,7 @@ impl crate::message_handler::MessageHandler{
|
||||
let mut modified=false;
|
||||
for &script_ref in &script_refs{
|
||||
if let Some(script)=dom.get_by_ref_mut(script_ref){
|
||||
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get_mut(&source_property){
|
||||
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get_mut("Source"){
|
||||
match script_map.get(source.as_str()).map(|p|&p.policy){
|
||||
Some(Policy::Blocked)=>{
|
||||
let hash=hash_source(source.as_str());
|
||||
@@ -277,8 +254,8 @@ impl crate::message_handler::MessageHandler{
|
||||
(validate_info.ModelID,validate_info.ModelVersion)
|
||||
};
|
||||
|
||||
match validate_info.Resource{
|
||||
Resource::Mapfix(mapfix_id)=>{
|
||||
match validate_info.ResourceID{
|
||||
ResourceID::Mapfix(mapfix_id)=>{
|
||||
// update the mapfix to use the validated model
|
||||
self.api.update_mapfix_validated_model(submissions_api::types::UpdateMapfixModelRequest{
|
||||
MapfixID:mapfix_id,
|
||||
@@ -286,7 +263,7 @@ impl crate::message_handler::MessageHandler{
|
||||
ModelVersion:validated_model_version,
|
||||
}).await.map_err(Error::ApiUpdateMapfixModel)?;
|
||||
},
|
||||
Resource::Submission(submission_id)=>{
|
||||
ResourceID::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 +277,17 @@ impl crate::message_handler::MessageHandler{
|
||||
}
|
||||
}
|
||||
|
||||
fn recursive_collect_superclass(objects:&mut std::vec::Vec<rbx_dom_weak::types::Ref>,dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,superclass:&str){
|
||||
for &referent in instance.children(){
|
||||
if let Some(c)=dom.get_by_ref(referent){
|
||||
if class_is_a(c.class.as_str(),superclass){
|
||||
objects.push(c.referent());//copy ref
|
||||
}
|
||||
recursive_collect_superclass(objects,dom,c,superclass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_partial_path(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->String{
|
||||
let mut names:Vec<_>=core::iter::successors(
|
||||
Some(instance),
|
||||
@@ -314,11 +302,7 @@ fn get_partial_path(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)
|
||||
}
|
||||
|
||||
fn get_script_refs(dom:&rbx_dom_weak::WeakDom)->Vec<rbx_dom_weak::types::Ref>{
|
||||
let db=rbx_reflection_database::get();
|
||||
let superclass=&db.classes["LuaSourceContainer"];
|
||||
dom.descendants().filter_map(|inst|{
|
||||
let class=db.classes.get(inst.class.as_str())?;
|
||||
db.has_superclass(class,superclass)
|
||||
.then_some(inst.referent())
|
||||
}).collect()
|
||||
let mut scripts=std::vec::Vec::new();
|
||||
recursive_collect_superclass(&mut scripts,dom,dom.root(),"LuaSourceContainer");
|
||||
scripts
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ const nextConfig: NextConfig = {
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "**.rbxcdn.com",
|
||||
hostname: "tr.rbxcdn.com",
|
||||
pathname: "/**",
|
||||
port: "",
|
||||
search: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -11,23 +11,20 @@
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@mui/icons-material": "^6.1.10",
|
||||
"@mui/material": "^6.1.10",
|
||||
"date-fns": "^4.1.0",
|
||||
"next": "^15.1.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"sass": "^1.82.0",
|
||||
"swr": "^2.3.3"
|
||||
"sass": "^1.82.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"typescript": "^5.7.2",
|
||||
"@types/node": "^20.17.9",
|
||||
"@types/react": "^19.0.1",
|
||||
"@types/react-dom": "^19.0.2",
|
||||
"eslint": "^9.16.0",
|
||||
"eslint-config-next": "15.1.0",
|
||||
"typescript": "^5.7.2"
|
||||
"@eslint/eslintrc": "^3.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB |
@@ -1,15 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@mui/material";
|
||||
import { SessionProvider } from "@/app/_components/SessionContext";
|
||||
import { theme } from "@/app/lib/theme";
|
||||
|
||||
export default function AppProviders({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<SessionProvider>
|
||||
<ThemeProvider theme={theme}>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</SessionProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import { Button, Container, Paper, Typography } from "@mui/material";
|
||||
import Webpage from "@/app/_components/webpage";
|
||||
|
||||
interface ErrorDisplayProps {
|
||||
title: string;
|
||||
message: string;
|
||||
buttonText?: string;
|
||||
onButtonClick?: () => void;
|
||||
}
|
||||
|
||||
export function ErrorDisplay({
|
||||
title,
|
||||
message,
|
||||
buttonText,
|
||||
onButtonClick
|
||||
}: ErrorDisplayProps) {
|
||||
return (
|
||||
<Webpage>
|
||||
<Container maxWidth="lg" sx={{ py: 6 }}>
|
||||
<Paper
|
||||
elevation={3}
|
||||
sx={{
|
||||
p: 4,
|
||||
textAlign: 'center',
|
||||
borderRadius: 2
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" gutterBottom>{title}</Typography>
|
||||
<Typography variant="body1">{message}</Typography>
|
||||
{buttonText && onButtonClick && (
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={onButtonClick}
|
||||
sx={{ mt: 3 }}
|
||||
>
|
||||
{buttonText}
|
||||
</Button>
|
||||
)}
|
||||
</Paper>
|
||||
</Container>
|
||||
</Webpage>
|
||||
);
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, useContext, ReactNode } from "react";
|
||||
import useSWR from "swr";
|
||||
import { RolesConstants } from "@/app/ts/Roles";
|
||||
|
||||
interface UserInfo {
|
||||
UserID: number;
|
||||
Username: string;
|
||||
AvatarURL: string;
|
||||
}
|
||||
|
||||
interface SessionContextType {
|
||||
roles: number;
|
||||
user: UserInfo | null;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
const SessionContext = createContext<SessionContextType>({ roles: RolesConstants.Empty, user: null, loading: true });
|
||||
|
||||
const fetcher = async (url: string) => {
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) return null;
|
||||
try {
|
||||
const data = await res.json();
|
||||
if (data && typeof data === 'object' && (data.code || data.error)) return null;
|
||||
return data;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const SessionProvider = ({ children }: { children: ReactNode }) => {
|
||||
const { data: rolesData, isLoading: rolesLoading } = useSWR("/api/session/roles", fetcher, { refreshInterval: 60000 });
|
||||
const { data: userData, isLoading: userLoading } = useSWR("/api/session/user", fetcher, { refreshInterval: 60000 });
|
||||
|
||||
const loading = rolesLoading || userLoading;
|
||||
const roles = rolesData?.Roles ?? RolesConstants.Empty;
|
||||
const user = userData ?? null;
|
||||
|
||||
return (
|
||||
<SessionContext.Provider value={{ roles, user, loading }}>
|
||||
{children}
|
||||
</SessionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useSession = () => useContext(SessionContext);
|
||||
@@ -1,151 +0,0 @@
|
||||
import {Box, IconButton, Typography} from "@mui/material";
|
||||
import {useEffect, useRef, useState} from "react";
|
||||
import Link from "next/link";
|
||||
import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew";
|
||||
import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
|
||||
import {SubmissionInfo} from "@/app/ts/Submission";
|
||||
import {MapfixInfo} from "@/app/ts/Mapfix";
|
||||
|
||||
// Type for the items in the carousel
|
||||
type CarouselItem = SubmissionInfo | MapfixInfo;
|
||||
|
||||
// Props for the Carousel component
|
||||
interface CarouselProps<T extends CarouselItem> {
|
||||
title: string;
|
||||
items: T[] | undefined;
|
||||
renderItem: (item: T) => React.ReactNode;
|
||||
viewAllLink: string;
|
||||
}
|
||||
|
||||
export function Carousel<T extends CarouselItem>({ title, items, renderItem, viewAllLink }: CarouselProps<T>) {
|
||||
const carouselRef = useRef<HTMLDivElement | null>(null);
|
||||
const [scrollPosition, setScrollPosition] = useState<number>(0);
|
||||
const [maxScroll, setMaxScroll] = useState<number>(0);
|
||||
|
||||
const SCROLL_AMOUNT = 300;
|
||||
|
||||
useEffect(() => {
|
||||
if (carouselRef.current) {
|
||||
const scrollWidth = carouselRef.current.scrollWidth;
|
||||
const clientWidth = carouselRef.current.clientWidth;
|
||||
setMaxScroll(scrollWidth - clientWidth);
|
||||
}
|
||||
}, [items]);
|
||||
|
||||
const scroll = (direction: 'left' | 'right'): void => {
|
||||
if (carouselRef.current) {
|
||||
const scrollAmount = direction === 'left' ? -SCROLL_AMOUNT : SCROLL_AMOUNT;
|
||||
|
||||
carouselRef.current.scrollBy({
|
||||
left: scrollAmount,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
if (carouselRef.current) {
|
||||
setScrollPosition(carouselRef.current.scrollLeft);
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
if (carouselRef.current) {
|
||||
setScrollPosition(carouselRef.current.scrollLeft);
|
||||
}
|
||||
};
|
||||
|
||||
const ref = carouselRef.current;
|
||||
if (ref) {
|
||||
ref.addEventListener('scroll', handleScroll);
|
||||
return () => ref.removeEventListener('scroll', handleScroll);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box mb={6}>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center" mb={2}>
|
||||
<Typography variant="h4" component="h2" fontWeight="bold">
|
||||
{title}
|
||||
</Typography>
|
||||
<Link href={viewAllLink} style={{textDecoration: 'none'}}>
|
||||
<Typography component="span" color="primary">
|
||||
View All →
|
||||
</Typography>
|
||||
</Link>
|
||||
</Box>
|
||||
|
||||
<Box position="relative">
|
||||
<IconButton
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
left: -20,
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
zIndex: 2,
|
||||
backgroundColor: 'background.paper',
|
||||
boxShadow: 2,
|
||||
'&:hover': {
|
||||
backgroundColor: 'action.hover',
|
||||
},
|
||||
visibility: scrollPosition <= 5 ? 'hidden' : 'visible',
|
||||
}}
|
||||
onClick={() => scroll('left')}
|
||||
>
|
||||
<ArrowBackIosNewIcon />
|
||||
</IconButton>
|
||||
|
||||
<Box
|
||||
ref={carouselRef}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
overflowX: 'auto',
|
||||
scrollbarWidth: 'none',
|
||||
msOverflowStyle: 'none',
|
||||
'&::-webkit-scrollbar': {
|
||||
display: 'none',
|
||||
},
|
||||
gap: '16px', // Fixed 16px gap - using string with px unit to ensure it's absolute
|
||||
padding: '8px 4px',
|
||||
}}
|
||||
>
|
||||
{items?.map((item, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
sx={{
|
||||
flex: '0 0 auto',
|
||||
width: {
|
||||
xs: '260px', // Fixed width at different breakpoints
|
||||
sm: '280px',
|
||||
md: '300px'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{renderItem(item)}
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
<IconButton
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
right: -20,
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
zIndex: 2,
|
||||
backgroundColor: 'background.paper',
|
||||
boxShadow: 2,
|
||||
'&:hover': {
|
||||
backgroundColor: 'action.hover',
|
||||
},
|
||||
visibility: scrollPosition >= maxScroll - 5 ? 'hidden' : 'visible',
|
||||
}}
|
||||
onClick={() => scroll('right')}
|
||||
>
|
||||
<ArrowForwardIosIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Avatar,
|
||||
Typography,
|
||||
Tooltip
|
||||
} from "@mui/material";
|
||||
import PersonIcon from '@mui/icons-material/Person';
|
||||
import { formatDistanceToNow, format } from "date-fns";
|
||||
import { AuditEvent, decodeAuditEvent as auditEventMessage } from "@/app/ts/AuditEvent";
|
||||
|
||||
interface AuditEventItemProps {
|
||||
event: AuditEvent;
|
||||
validatorUser: number;
|
||||
userAvatarUrl?: string;
|
||||
}
|
||||
|
||||
export default function AuditEventItem({ event, validatorUser, userAvatarUrl }: AuditEventItemProps) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', gap: 2 }}>
|
||||
<Avatar
|
||||
src={event.User === validatorUser ? undefined : userAvatarUrl}
|
||||
sx={{ border: '1px solid rgba(255, 255, 255, 0.1)', bgcolor: 'grey.900' }}
|
||||
>
|
||||
<PersonIcon />
|
||||
</Avatar>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Typography variant="subtitle2">
|
||||
{event.User === validatorUser ? "Validator" : event.Username || "Unknown"}
|
||||
</Typography>
|
||||
<DateDisplay date={event.Date} />
|
||||
</Box>
|
||||
<Typography variant="body2">{auditEventMessage(event)}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
interface DateDisplayProps {
|
||||
date: number;
|
||||
}
|
||||
|
||||
function DateDisplay({ date }: DateDisplayProps) {
|
||||
return (
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
<Tooltip title={format(new Date(date * 1000), 'PPpp')}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{formatDistanceToNow(new Date(date * 1000), { addSuffix: true })}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Stack,
|
||||
} from "@mui/material";
|
||||
import { AuditEvent, AuditEventType } from "@/app/ts/AuditEvent";
|
||||
import AuditEventItem from './AuditEventItem';
|
||||
|
||||
interface AuditEventsTabPanelProps {
|
||||
activeTab: number;
|
||||
auditEvents: AuditEvent[];
|
||||
validatorUser: number;
|
||||
auditEventUserAvatarUrls?: Record<number, string>;
|
||||
}
|
||||
|
||||
export default function AuditEventsTabPanel({
|
||||
activeTab,
|
||||
auditEvents,
|
||||
validatorUser,
|
||||
auditEventUserAvatarUrls
|
||||
}: AuditEventsTabPanelProps) {
|
||||
const filteredEvents = auditEvents.filter(
|
||||
event => event.EventType !== AuditEventType.Comment
|
||||
);
|
||||
|
||||
return (
|
||||
<Box role="tabpanel" hidden={activeTab !== 1}>
|
||||
{activeTab === 1 && (
|
||||
<Stack spacing={2}>
|
||||
{filteredEvents.map((event, index) => (
|
||||
<AuditEventItem
|
||||
key={index}
|
||||
event={event}
|
||||
validatorUser={validatorUser}
|
||||
userAvatarUrl={auditEventUserAvatarUrls?.[event.User]}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Avatar,
|
||||
Typography,
|
||||
Tooltip
|
||||
} from "@mui/material";
|
||||
import PersonIcon from '@mui/icons-material/Person';
|
||||
import { formatDistanceToNow, format } from "date-fns";
|
||||
import { AuditEvent, decodeAuditEvent } from "@/app/ts/AuditEvent";
|
||||
|
||||
interface CommentItemProps {
|
||||
event: AuditEvent;
|
||||
validatorUser: number;
|
||||
userAvatarUrl?: string;
|
||||
}
|
||||
|
||||
export default function CommentItem({ event, validatorUser, userAvatarUrl }: CommentItemProps) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', gap: 2 }}>
|
||||
<Avatar
|
||||
src={event.User === validatorUser ? undefined : userAvatarUrl}
|
||||
sx={{ border: '1px solid rgba(255, 255, 255, 0.1)', bgcolor: 'grey.900' }}
|
||||
>
|
||||
<PersonIcon />
|
||||
</Avatar>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Typography variant="subtitle2">
|
||||
{event.User === validatorUser ? "Validator" : event.Username || "Unknown"}
|
||||
</Typography>
|
||||
<DateDisplay date={event.Date} />
|
||||
</Box>
|
||||
<Typography variant="body2">{decodeAuditEvent(event)}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
interface DateDisplayProps {
|
||||
date: number;
|
||||
}
|
||||
|
||||
function DateDisplay({ date }: DateDisplayProps) {
|
||||
return (
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
<Tooltip title={format(new Date(date * 1000), 'PPpp')}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{formatDistanceToNow(new Date(date * 1000), { addSuffix: true })}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import React, {useState} from 'react';
|
||||
import {
|
||||
Paper,
|
||||
Box,
|
||||
Tabs,
|
||||
Tab,
|
||||
} from "@mui/material";
|
||||
import CommentsTabPanel from './CommentsTabPanel';
|
||||
import AuditEventsTabPanel from './AuditEventsTabPanel';
|
||||
import { AuditEvent } from "@/app/ts/AuditEvent";
|
||||
|
||||
interface CommentsAndAuditSectionProps {
|
||||
auditEvents: AuditEvent[];
|
||||
newComment: string;
|
||||
setNewComment: (comment: string) => void;
|
||||
handleCommentSubmit: () => void;
|
||||
validatorUser: number;
|
||||
userId: number | null;
|
||||
commentUserAvatarUrls: Record<number, string>;
|
||||
auditEventUserAvatarUrls?: Record<number, string>;
|
||||
}
|
||||
|
||||
export default function CommentsAndAuditSection({
|
||||
auditEvents,
|
||||
newComment,
|
||||
setNewComment,
|
||||
handleCommentSubmit,
|
||||
validatorUser,
|
||||
userId,
|
||||
commentUserAvatarUrls,
|
||||
auditEventUserAvatarUrls
|
||||
}: CommentsAndAuditSectionProps) {
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
|
||||
setActiveTab(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<Paper sx={{ p: 3, mt: 3 }}>
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 2 }}>
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onChange={handleTabChange}
|
||||
aria-label="comments and audit tabs"
|
||||
>
|
||||
<Tab label="Comments" />
|
||||
<Tab label="Audit Events" />
|
||||
</Tabs>
|
||||
</Box>
|
||||
|
||||
<CommentsTabPanel
|
||||
activeTab={activeTab}
|
||||
auditEvents={auditEvents}
|
||||
validatorUser={validatorUser}
|
||||
newComment={newComment}
|
||||
setNewComment={setNewComment}
|
||||
handleCommentSubmit={handleCommentSubmit}
|
||||
userId={userId}
|
||||
commentUserAvatarUrls={commentUserAvatarUrls}
|
||||
/>
|
||||
|
||||
<AuditEventsTabPanel
|
||||
activeTab={activeTab}
|
||||
auditEvents={auditEvents}
|
||||
validatorUser={validatorUser}
|
||||
auditEventUserAvatarUrls={auditEventUserAvatarUrls}
|
||||
/>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Stack,
|
||||
Avatar,
|
||||
TextField,
|
||||
IconButton
|
||||
} from "@mui/material";
|
||||
import SendIcon from '@mui/icons-material/Send';
|
||||
import { AuditEvent, AuditEventType } from "@/app/ts/AuditEvent";
|
||||
import CommentItem from './CommentItem';
|
||||
|
||||
interface CommentsTabPanelProps {
|
||||
activeTab: number;
|
||||
auditEvents: AuditEvent[];
|
||||
validatorUser: number;
|
||||
newComment: string;
|
||||
setNewComment: (comment: string) => void;
|
||||
handleCommentSubmit: () => void;
|
||||
userId: number | null;
|
||||
userAvatarUrl?: string;
|
||||
commentUserAvatarUrls?: Record<number, string>;
|
||||
}
|
||||
|
||||
export default function CommentsTabPanel({
|
||||
activeTab,
|
||||
auditEvents,
|
||||
validatorUser,
|
||||
newComment,
|
||||
setNewComment,
|
||||
handleCommentSubmit,
|
||||
userId,
|
||||
userAvatarUrl,
|
||||
commentUserAvatarUrls
|
||||
}: CommentsTabPanelProps) {
|
||||
const commentEvents = auditEvents.filter(
|
||||
event => event.EventType === AuditEventType.Comment
|
||||
);
|
||||
|
||||
return (
|
||||
<Box role="tabpanel" hidden={activeTab !== 0}>
|
||||
{activeTab === 0 && (
|
||||
<>
|
||||
<Stack spacing={2} sx={{ mb: 3 }}>
|
||||
{commentEvents.length > 0 ? (
|
||||
commentEvents.map((event, index) => (
|
||||
<CommentItem
|
||||
key={index}
|
||||
event={event}
|
||||
validatorUser={validatorUser}
|
||||
userAvatarUrl={commentUserAvatarUrls?.[event.User]}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Box sx={{ textAlign: 'center', py: 2, color: 'text.secondary' }}>
|
||||
No Comments
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{userId !== null && (
|
||||
<CommentInput
|
||||
newComment={newComment}
|
||||
setNewComment={setNewComment}
|
||||
handleCommentSubmit={handleCommentSubmit}
|
||||
userId={userId}
|
||||
userAvatarUrl={userAvatarUrl}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
interface CommentInputProps {
|
||||
newComment: string;
|
||||
setNewComment: (comment: string) => void;
|
||||
handleCommentSubmit: () => void;
|
||||
userId: number | null;
|
||||
userAvatarUrl?: string;
|
||||
}
|
||||
|
||||
function CommentInput({ newComment, setNewComment, handleCommentSubmit, userAvatarUrl }: CommentInputProps) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', gap: 2, alignItems: 'flex-start' }}>
|
||||
<Avatar
|
||||
src={userAvatarUrl}
|
||||
sx={{ border: '1px solid rgba(255, 255, 255, 0.1)', bgcolor: 'grey.900' }}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
rows={2}
|
||||
placeholder="Add a comment..."
|
||||
value={newComment}
|
||||
onChange={(e) => setNewComment(e.target.value)}
|
||||
/>
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={handleCommentSubmit}
|
||||
disabled={!newComment.trim()}
|
||||
>
|
||||
<SendIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -2,277 +2,63 @@
|
||||
|
||||
import Link from "next/link"
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
import { useSession } from "@/app/_components/SessionContext";
|
||||
|
||||
import AppBar from "@mui/material/AppBar";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import Button from "@mui/material/Button";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Box from "@mui/material/Box";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import Drawer from "@mui/material/Drawer";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
|
||||
import { RolesConstants, hasRole } from "@/app/ts/Roles";
|
||||
import "./styles/header.scss"
|
||||
import { UserInfo } from "@/app/ts/User";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
interface HeaderButton {
|
||||
name: string;
|
||||
href: string;
|
||||
name: string,
|
||||
href: string
|
||||
}
|
||||
|
||||
const navItems: HeaderButton[] = [
|
||||
{ name: "Home", href: "/" },
|
||||
{ name: "Submissions", href: "/submissions" },
|
||||
{ name: "Mapfixes", href: "/mapfixes" },
|
||||
{ name: "Maps", href: "/maps" },
|
||||
];
|
||||
|
||||
function HeaderButton(header: HeaderButton) {
|
||||
return (
|
||||
<Button color="inherit" component={Link} href={header.href}>
|
||||
{header.name}
|
||||
</Button>
|
||||
);
|
||||
return (
|
||||
<Link href={header.href}>
|
||||
<button>{header.name}</button>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Header() {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
const handleLoginClick = () => {
|
||||
window.location.href = "/auth/oauth2/login?redirect=" + window.location.href;
|
||||
};
|
||||
|
||||
const { user, roles } = useSession();
|
||||
const valid = !!user;
|
||||
const [valid, setValid] = useState<boolean>(false)
|
||||
const [user, setUser] = useState<UserInfo | null>(null)
|
||||
|
||||
const handleLoginClick = () => {
|
||||
window.location.href =
|
||||
"/auth/oauth2/login?redirect=" + window.location.href;
|
||||
};
|
||||
useEffect(() => {
|
||||
async function getLoginInfo() {
|
||||
const [validateData, userData] = await Promise.all([
|
||||
fetch("/api/session/validate").then(validateResponse => validateResponse.json()),
|
||||
fetch("/api/session/user").then(userResponse => userResponse.json())
|
||||
]);
|
||||
setValid(validateData)
|
||||
setUser(userData)
|
||||
}
|
||||
getLoginInfo()
|
||||
}, [])
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
const [quickLinksAnchor, setQuickLinksAnchor] = useState<null | HTMLElement>(null);
|
||||
|
||||
const handleMenuOpen = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleDrawerToggle = () => {
|
||||
setMobileOpen(!mobileOpen);
|
||||
};
|
||||
|
||||
const handleQuickLinksOpen = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setQuickLinksAnchor(event.currentTarget);
|
||||
};
|
||||
const handleQuickLinksClose = () => {
|
||||
setQuickLinksAnchor(null);
|
||||
};
|
||||
|
||||
// Mobile navigation drawer content
|
||||
const drawer = (
|
||||
<Box onClick={handleDrawerToggle} sx={{ textAlign: 'center' }}>
|
||||
<List>
|
||||
{navItems.map((item) => (
|
||||
<ListItem key={item.name} disablePadding>
|
||||
<ListItemButton component={Link} href={item.href} sx={{ textAlign: 'center' }}>
|
||||
<ListItemText primary={item.name} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
{valid && user && (
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton component={Link} href="/submit" sx={{ textAlign: 'center' }}>
|
||||
<ListItemText primary="Submit Map" sx={{ color: 'success.main' }} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
)}
|
||||
{!valid && (
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton onClick={handleLoginClick} sx={{ textAlign: 'center' }}>
|
||||
<ListItemText primary="Login" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
)}
|
||||
{valid && user && (
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton component={Link} href="/auth" sx={{ textAlign: 'center' }}>
|
||||
<ListItemText primary="Manage Account" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
)}
|
||||
</List>
|
||||
</Box>
|
||||
);
|
||||
|
||||
const quickLinks = [
|
||||
{ name: "Bhop", href: "https://www.roblox.com/games/5315046213" },
|
||||
{ name: "Bhop Maptest", href: "https://www.roblox.com/games/517201717" },
|
||||
{ name: "Surf", href: "https://www.roblox.com/games/5315066937" },
|
||||
{ name: "Surf Maptest", href: "https://www.roblox.com/games/517206177" },
|
||||
{ name: "Fly Trials", href: "https://www.roblox.com/games/12591611759" },
|
||||
{ name: "Fly Trials Maptest", href: "https://www.roblox.com/games/12724901535" },
|
||||
];
|
||||
|
||||
const showScriptReview = hasRole(roles, RolesConstants.ScriptWrite);
|
||||
|
||||
return (
|
||||
<AppBar position="static">
|
||||
<Toolbar>
|
||||
{isMobile && (
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-label="open drawer"
|
||||
edge="start"
|
||||
onClick={handleDrawerToggle}
|
||||
sx={{ mr: 2 }}
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
{/* Desktop navigation */}
|
||||
{!isMobile && (
|
||||
<Box display="flex" flexGrow={1} gap={2} alignItems="center">
|
||||
{navItems.map((item) => (
|
||||
<HeaderButton key={item.name} name={item.name} href={item.href} />
|
||||
))}
|
||||
{showScriptReview && (
|
||||
<HeaderButton name="Script Review" href="/script-review" />
|
||||
)}
|
||||
<Box sx={{ flexGrow: 1 }} /> {/* Push quick links to the right */}
|
||||
{/* Quick Links Dropdown */}
|
||||
<Box>
|
||||
<Button
|
||||
color="inherit"
|
||||
endIcon={<ArrowDropDownIcon />}
|
||||
onClick={handleQuickLinksOpen}
|
||||
sx={{ textTransform: 'none', fontSize: '0.95rem', px: 1 }}
|
||||
>
|
||||
QUICK LINKS
|
||||
</Button>
|
||||
<Menu
|
||||
anchorEl={quickLinksAnchor}
|
||||
open={Boolean(quickLinksAnchor)}
|
||||
onClose={handleQuickLinksClose}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||||
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
>
|
||||
{quickLinks.map(link => (
|
||||
<MenuItem
|
||||
key={link.name}
|
||||
onClick={handleQuickLinksClose}
|
||||
sx={{ minWidth: 180 }}
|
||||
component="a"
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{link.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Spacer for mobile view */}
|
||||
{isMobile && <Box sx={{ flexGrow: 1 }} />}
|
||||
|
||||
{/* Right side of nav */}
|
||||
<Box display="flex" gap={2}>
|
||||
{!isMobile && valid && user && (
|
||||
<Button variant="outlined" color="success" component={Link} href="/submit">
|
||||
Submit Map
|
||||
</Button>
|
||||
)}
|
||||
{!isMobile && valid && user ? (
|
||||
<Box display="flex" alignItems="center">
|
||||
<Button
|
||||
onClick={handleMenuOpen}
|
||||
color="inherit"
|
||||
size="small"
|
||||
style={{ textTransform: "none" }}
|
||||
>
|
||||
<Image
|
||||
className="avatar"
|
||||
width={28}
|
||||
height={28}
|
||||
priority={true}
|
||||
src={user.AvatarURL}
|
||||
alt={user.Username}
|
||||
style={{ marginRight: 8 }}
|
||||
/>
|
||||
<Typography variant="body1">{user.Username}</Typography>
|
||||
</Button>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
}}
|
||||
>
|
||||
<MenuItem component={Link} href="/auth">
|
||||
Manage
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Box>
|
||||
) : !isMobile && (
|
||||
<Button color="inherit" onClick={handleLoginClick}>
|
||||
Login
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* In mobile view, display just the avatar if logged in */}
|
||||
{isMobile && valid && user && (
|
||||
<IconButton
|
||||
onClick={handleMenuOpen}
|
||||
color="inherit"
|
||||
size="small"
|
||||
>
|
||||
<Image
|
||||
className="avatar"
|
||||
width={28}
|
||||
height={28}
|
||||
priority={true}
|
||||
src={user.AvatarURL}
|
||||
alt={user.Username}
|
||||
/>
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
</Toolbar>
|
||||
|
||||
{/* Mobile drawer */}
|
||||
<Drawer
|
||||
variant="temporary"
|
||||
open={mobileOpen}
|
||||
onClose={handleDrawerToggle}
|
||||
ModalProps={{
|
||||
keepMounted: true, // Better open performance on mobile
|
||||
}}
|
||||
sx={{
|
||||
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: 240 },
|
||||
}}
|
||||
>
|
||||
{drawer}
|
||||
</Drawer>
|
||||
</AppBar>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<header className="header-bar">
|
||||
<nav className="left">
|
||||
<HeaderButton name="Submissions" href="/submissions"/>
|
||||
<HeaderButton name="Mapfixes" href="/mapfixes"/>
|
||||
<HeaderButton name="Maps" href="/maps"/>
|
||||
</nav>
|
||||
<nav className="right">
|
||||
<HeaderButton name="Submit" href="/submit"/>
|
||||
{valid && user ? (
|
||||
<div className="author">
|
||||
<Link href="/auth">
|
||||
<Image className="avatar" width={28} height={28} priority={true} src={user.AvatarURL} alt={user.Username}/>
|
||||
<button>{user.Username}</button>
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<button onClick={handleLoginClick}>Login</button>
|
||||
)}
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,193 +1,71 @@
|
||||
import React from "react";
|
||||
import {Avatar, Box, Card, CardActionArea, CardContent, CardMedia, CircularProgress, Divider, Grid, Typography} from "@mui/material";
|
||||
import {Explore, Person2} from "@mui/icons-material";
|
||||
import {StatusChip} from "@/app/_components/statusChip";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Rating } from "@mui/material";
|
||||
|
||||
interface MapCardProps {
|
||||
interface SubmissionCardProps {
|
||||
displayName: string;
|
||||
assetId: number;
|
||||
submitterId: number;
|
||||
submitterUsername: string;
|
||||
authorId: number;
|
||||
author: string;
|
||||
rating: number;
|
||||
id: number;
|
||||
statusID: number;
|
||||
gameID: number;
|
||||
created: number;
|
||||
type: 'mapfix' | 'submission';
|
||||
thumbnailUrl?: string;
|
||||
authorAvatarUrl?: string;
|
||||
}
|
||||
|
||||
const CARD_WIDTH = 270;
|
||||
|
||||
export function MapCard(props: MapCardProps) {
|
||||
export function SubmissionCard(props: SubmissionCardProps) {
|
||||
return (
|
||||
<Grid item xs={12} sm={6} md={3} key={props.assetId}>
|
||||
<Box sx={{
|
||||
width: CARD_WIDTH,
|
||||
mx: 'auto', // Center the card in its grid cell
|
||||
}}>
|
||||
<Card sx={{
|
||||
width: CARD_WIDTH,
|
||||
height: 340, // Fixed height for all cards
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}>
|
||||
<CardActionArea
|
||||
sx={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'stretch'
|
||||
}}
|
||||
href={`/${props.type === 'submission' ? 'submissions' : 'mapfixes'}/${props.id}`}>
|
||||
<Box sx={{ position: 'relative' }}>
|
||||
{props.thumbnailUrl ? (
|
||||
<CardMedia
|
||||
component="img"
|
||||
image={props.thumbnailUrl}
|
||||
alt={props.displayName}
|
||||
sx={{
|
||||
height: 160, // Fixed height for all images
|
||||
objectFit: 'cover',
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Box sx={{ height: 160, display: 'flex', alignItems: 'center', justifyContent: 'center', bgcolor: 'grey.900' }}>
|
||||
<CircularProgress size={32} />
|
||||
</Box>
|
||||
)}
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 12,
|
||||
right: 12,
|
||||
}}
|
||||
>
|
||||
<StatusChip status={props.statusID}/>
|
||||
</Box>
|
||||
</Box>
|
||||
<CardContent sx={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
p: 2,
|
||||
width: '100%',
|
||||
}}>
|
||||
<Box>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
component="div"
|
||||
sx={{
|
||||
mb: 1,
|
||||
fontWeight: 600,
|
||||
color: '#fff',
|
||||
lineHeight: '1.3',
|
||||
// Allow text to wrap
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
}}
|
||||
>
|
||||
{props.displayName}
|
||||
</Typography>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
mb: 1.5,
|
||||
}}>
|
||||
<Explore sx={{
|
||||
mr: 0.75,
|
||||
mt: 0.25,
|
||||
color: 'text.secondary',
|
||||
fontSize: '0.9rem',
|
||||
flexShrink: 0,
|
||||
}} />
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
sx={{
|
||||
fontWeight: 500,
|
||||
// Allow text to wrap
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
lineHeight: '1.2',
|
||||
wordBreak: 'break-word',
|
||||
}}
|
||||
>
|
||||
{props.gameID === 1 ? 'Bhop' : props.gameID === 2 ? 'Surf' : props.gameID === 5 ? 'Fly Trials' : props.gameID === 4 ? 'Deathrun' : 'Unknown'}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
mb: 1.5,
|
||||
}}>
|
||||
<Person2 sx={{
|
||||
mr: 0.75,
|
||||
mt: 0.25,
|
||||
color: 'text.secondary',
|
||||
fontSize: '0.9rem',
|
||||
flexShrink: 0,
|
||||
}} />
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
sx={{
|
||||
fontWeight: 500,
|
||||
// Allow text to wrap
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
lineHeight: '1.2',
|
||||
wordBreak: 'break-word',
|
||||
}}
|
||||
>
|
||||
{props.author}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Divider sx={{ my: 1.5 }} />
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Avatar
|
||||
src={props.authorAvatarUrl}
|
||||
alt={props.submitterUsername}
|
||||
sx={{
|
||||
width: 24,
|
||||
height: 24,
|
||||
border: '1px solid rgba(255, 255, 255, 0.1)',
|
||||
bgcolor: 'grey.900'
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{
|
||||
ml: 1,
|
||||
color: 'text.secondary',
|
||||
fontWeight: 500,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{props.submitterUsername} - {new Date(props.created * 1000).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</Typography>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
</Box>
|
||||
</Grid>
|
||||
)
|
||||
<Link href={`/submissions/${props.id}`}>
|
||||
<div className="submissionCard">
|
||||
<div className="content">
|
||||
<div className="map-image">
|
||||
{/* TODO: Grab image of model */}
|
||||
<Image width={230} height={230} layout="fixed" priority={true} src={`/thumbnails/asset/${props.assetId}`} alt={props.displayName} />
|
||||
</div>
|
||||
<div className="details">
|
||||
<div className="header">
|
||||
<span className="displayName">{props.displayName}</span>
|
||||
<div className="rating">
|
||||
<Rating value={props.rating} readOnly size="small" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="footer">
|
||||
<div className="author">
|
||||
<Image className="avatar" width={28} height={28} priority={true} src={`/thumbnails/user/${props.authorId}`} alt={props.author}/>
|
||||
<span>{props.author}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export function MapfixCard(props: SubmissionCardProps) {
|
||||
return (
|
||||
<Link href={`/mapfixes/${props.id}`}>
|
||||
<div className="MapfixCard">
|
||||
<div className="content">
|
||||
<div className="map-image">
|
||||
{/* TODO: Grab image of model */}
|
||||
<Image width={230} height={230} layout="fixed" priority={true} src={`/thumbnails/asset/${props.assetId}`} alt={props.displayName} />
|
||||
</div>
|
||||
<div className="details">
|
||||
<div className="header">
|
||||
<span className="displayName">{props.displayName}</span>
|
||||
<div className="rating">
|
||||
<Rating value={props.rating} readOnly size="small" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="footer">
|
||||
<div className="author">
|
||||
<Image className="avatar" width={28} height={28} priority={true} src={`/thumbnails/user/${props.authorId}`} alt={props.author}/>
|
||||
<span>{props.author}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { Typography, Box, IconButton, Tooltip } from "@mui/material";
|
||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||
|
||||
interface CopyableFieldProps {
|
||||
label: string;
|
||||
value: string | number | null | undefined;
|
||||
onCopy: (value: string) => void;
|
||||
placeholderText?: string;
|
||||
link?: string; // Optional link prop
|
||||
}
|
||||
|
||||
export const CopyableField = ({
|
||||
label,
|
||||
value,
|
||||
onCopy,
|
||||
placeholderText = "Not assigned",
|
||||
link
|
||||
}: CopyableFieldProps) => {
|
||||
const displayValue = value?.toString() || placeholderText;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography variant="body2" color="text.secondary">{label}</Typography>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
{link ? (
|
||||
<a
|
||||
href={link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: 'inherit', textDecoration: 'none', cursor: 'pointer' }}
|
||||
>
|
||||
<Typography variant="body1" sx={{ '&:hover': { textDecoration: 'underline' } }}>{displayValue}</Typography>
|
||||
</a>
|
||||
) : (
|
||||
<Typography variant="body1">{displayValue}</Typography>
|
||||
)}
|
||||
{value && (
|
||||
<Tooltip title="Copy ID">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => onCopy(value.toString())}
|
||||
sx={{ ml: 1 }}
|
||||
>
|
||||
<ContentCopyIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,155 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Button, Stack } from '@mui/material';
|
||||
import {MapfixInfo } from "@/app/ts/Mapfix";
|
||||
import {hasRole, Roles, RolesConstants} from "@/app/ts/Roles";
|
||||
import {SubmissionInfo} from "@/app/ts/Submission";
|
||||
import {Status, StatusMatches} from "@/app/ts/Status";
|
||||
|
||||
interface ReviewAction {
|
||||
name: string,
|
||||
action: string,
|
||||
}
|
||||
|
||||
interface ReviewButtonsProps {
|
||||
onClick: (action: string, id: number) => void;
|
||||
item: (SubmissionInfo | MapfixInfo);
|
||||
userId: number | null;
|
||||
roles: Roles;
|
||||
type: "submission" | "mapfix";
|
||||
}
|
||||
|
||||
const ReviewActions = {
|
||||
Submit: {name:"Submit",action:"trigger-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,
|
||||
Reject: {name:"Reject",action:"reject"} as ReviewAction,
|
||||
Validate: {name:"Validate",action:"retry-validate"} as ReviewAction,
|
||||
ResetValidating: {name:"Reset Validating",action:"reset-validating"} as ReviewAction,
|
||||
RequestChanges: {name:"Request Changes",action:"request-changes"} as ReviewAction,
|
||||
Upload: {name:"Upload",action:"trigger-upload"} as ReviewAction,
|
||||
ResetUploading: {name:"Reset Uploading",action:"reset-uploading"} as ReviewAction,
|
||||
}
|
||||
|
||||
const ReviewButtons: React.FC<ReviewButtonsProps> = ({
|
||||
onClick,
|
||||
item,
|
||||
userId,
|
||||
roles,
|
||||
type,
|
||||
}) => {
|
||||
const getVisibleButtons = () => {
|
||||
if (!item || userId === null) return [];
|
||||
|
||||
// Define a type for the button
|
||||
type ReviewButton = {
|
||||
action: ReviewAction;
|
||||
color: "primary" | "error" | "success" | "info" | "warning";
|
||||
};
|
||||
|
||||
const buttons: ReviewButton[] = [];
|
||||
const is_submitter = userId === item.Submitter;
|
||||
const status = item.StatusID;
|
||||
|
||||
const reviewRole = type === "submission" ? RolesConstants.SubmissionReview : RolesConstants.MapfixReview;
|
||||
const uploadRole = type === "submission" ? RolesConstants.SubmissionUpload : RolesConstants.MapfixUpload;
|
||||
|
||||
if (is_submitter) {
|
||||
if (StatusMatches(status, [Status.UnderConstruction, Status.ChangesRequested])) {
|
||||
buttons.push({
|
||||
action: ReviewActions.Submit,
|
||||
color: "primary"
|
||||
});
|
||||
}
|
||||
|
||||
if (StatusMatches(status, [Status.Submitted, Status.ChangesRequested])) {
|
||||
buttons.push({
|
||||
action: ReviewActions.Revoke,
|
||||
color: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons for review role
|
||||
if (hasRole(roles, reviewRole)) {
|
||||
if (status === Status.Submitted && !is_submitter) {
|
||||
buttons.push(
|
||||
{
|
||||
action: ReviewActions.Accept,
|
||||
color: "success"
|
||||
},
|
||||
{
|
||||
action: ReviewActions.Reject,
|
||||
color: "error"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (status === Status.AcceptedUnvalidated) {
|
||||
buttons.push({
|
||||
action: ReviewActions.Validate,
|
||||
color: "info"
|
||||
});
|
||||
}
|
||||
|
||||
if (status === Status.Validating) {
|
||||
buttons.push({
|
||||
action: ReviewActions.ResetValidating,
|
||||
color: "warning"
|
||||
});
|
||||
}
|
||||
|
||||
if (StatusMatches(status, [Status.Validated, Status.AcceptedUnvalidated, Status.Submitted]) && !is_submitter) {
|
||||
buttons.push({
|
||||
action: ReviewActions.RequestChanges,
|
||||
color: "warning"
|
||||
});
|
||||
}
|
||||
|
||||
if (status === Status.ChangesRequested) {
|
||||
buttons.push({
|
||||
action: ReviewActions.SubmitUnchecked,
|
||||
color: "warning"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons for upload role
|
||||
if (hasRole(roles, uploadRole)) {
|
||||
if (status === Status.Validated) {
|
||||
buttons.push({
|
||||
action: ReviewActions.Upload,
|
||||
color: "success"
|
||||
});
|
||||
}
|
||||
|
||||
if (status === Status.Uploading) {
|
||||
buttons.push({
|
||||
action: ReviewActions.ResetUploading,
|
||||
color: "warning"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return buttons;
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack spacing={2} sx={{ mb: 3 }}>
|
||||
{getVisibleButtons().map((button, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
variant="contained"
|
||||
color={button.color}
|
||||
fullWidth
|
||||
onClick={() => onClick(button.action.action, item.ID)}
|
||||
>
|
||||
{button.action.name}
|
||||
</Button>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReviewButtons;
|
||||
@@ -1,97 +0,0 @@
|
||||
import { Paper, Grid, Typography } from "@mui/material";
|
||||
import { ReviewItemHeader } from "./ReviewItemHeader";
|
||||
import { CopyableField } from "@/app/_components/review/CopyableField";
|
||||
import { SubmissionInfo } from "@/app/ts/Submission";
|
||||
import { MapfixInfo } from "@/app/ts/Mapfix";
|
||||
|
||||
// Define a field configuration for specific types
|
||||
interface FieldConfig {
|
||||
key: string;
|
||||
label: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
type ReviewItemType = SubmissionInfo | MapfixInfo;
|
||||
|
||||
interface ReviewItemProps {
|
||||
item: ReviewItemType;
|
||||
handleCopyValue: (value: string) => void;
|
||||
submitterAvatarUrl?: string;
|
||||
submitterUsername?: string;
|
||||
}
|
||||
|
||||
export function ReviewItem({
|
||||
item,
|
||||
handleCopyValue,
|
||||
submitterAvatarUrl,
|
||||
submitterUsername
|
||||
}: ReviewItemProps) {
|
||||
if (!item) return null;
|
||||
|
||||
// Determine the type of item
|
||||
const isSubmission = 'UploadedAssetID' in item;
|
||||
const isMapfix = 'TargetAssetID' in item;
|
||||
|
||||
// Define static fields based on item type
|
||||
let fields: FieldConfig[] = [];
|
||||
if (isSubmission) {
|
||||
// Fields for Submission
|
||||
fields = [
|
||||
{ key: 'AssetID', label: 'Asset ID' },
|
||||
{ key: 'UploadedAssetID', label: 'Uploaded Asset ID' },
|
||||
];
|
||||
} else if (isMapfix) {
|
||||
// Fields for Mapfix
|
||||
fields = [
|
||||
{ key: 'AssetID', label: 'Asset ID' },
|
||||
{ key: 'TargetAssetID', label: 'Target Asset ID' },
|
||||
];
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper elevation={3} sx={{ p: 3, borderRadius: 2, mb: 4 }}>
|
||||
<ReviewItemHeader
|
||||
displayName={item.DisplayName}
|
||||
assetId={isMapfix ? item.TargetAssetID : undefined}
|
||||
statusId={item.StatusID}
|
||||
creator={item.Creator}
|
||||
submitterId={item.Submitter}
|
||||
submitterAvatarUrl={submitterAvatarUrl}
|
||||
submitterUsername={submitterUsername}
|
||||
/>
|
||||
|
||||
{/* Item Details */}
|
||||
<Grid container spacing={2} sx={{ mt: 2 }}>
|
||||
{fields.map((field) => {
|
||||
const fieldValue = (item as never)[field.key];
|
||||
const displayValue = fieldValue === 0 || fieldValue == null ? 'N/A' : fieldValue;
|
||||
const isAssetId = field.key.includes('AssetID') && fieldValue !== 0 && fieldValue != null;
|
||||
|
||||
return (
|
||||
<Grid item xs={12} sm={6} key={field.key}>
|
||||
<CopyableField
|
||||
label={field.label}
|
||||
value={String(displayValue)}
|
||||
onCopy={handleCopyValue}
|
||||
placeholderText={field.placeholder}
|
||||
link={isAssetId ? `https://create.roblox.com/store/asset/${fieldValue}` : undefined}
|
||||
/>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
|
||||
{/* Description Section */}
|
||||
{isMapfix && item.Description && (
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
||||
Description
|
||||
</Typography>
|
||||
<Typography variant="body1">
|
||||
{item.Description}
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
import {Typography, Box, Avatar, keyframes} from "@mui/material";
|
||||
import { StatusChip } from "@/app/_components/statusChip";
|
||||
import { SubmissionStatus } from "@/app/ts/Submission";
|
||||
import { MapfixStatus } from "@/app/ts/Mapfix";
|
||||
import {Status, StatusMatches} from "@/app/ts/Status";
|
||||
import Link from "next/link";
|
||||
import LaunchIcon from '@mui/icons-material/Launch';
|
||||
|
||||
interface ReviewItemHeaderProps {
|
||||
displayName: string;
|
||||
assetId: number | null | undefined,
|
||||
statusId: SubmissionStatus | MapfixStatus;
|
||||
creator: string | null | undefined;
|
||||
submitterId: number;
|
||||
submitterAvatarUrl?: string;
|
||||
submitterUsername?: string;
|
||||
}
|
||||
|
||||
export const ReviewItemHeader = ({ displayName, assetId, statusId, creator, submitterId, submitterAvatarUrl, submitterUsername }: ReviewItemHeaderProps) => {
|
||||
const isProcessing = StatusMatches(statusId, [Status.Validating, Status.Uploading, Status.Submitting]);
|
||||
const pulse = keyframes`
|
||||
0%, 100% { opacity: 0.2; transform: scale(0.8); }
|
||||
50% { opacity: 1; transform: scale(1); }
|
||||
`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
||||
{assetId != null ? (
|
||||
<Link href={`/maps/${assetId}`} passHref legacyBehavior>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', cursor: 'pointer' }} title="View related map">
|
||||
<Typography
|
||||
variant="h4"
|
||||
component="h1"
|
||||
gutterBottom
|
||||
sx={{ color: 'inherit', textDecoration: 'none', mr: 1 }}
|
||||
>
|
||||
{displayName} by {creator}
|
||||
</Typography>
|
||||
<LaunchIcon sx={{ fontSize: '1.5rem', color: 'text.secondary' }} />
|
||||
</Box>
|
||||
</Link>
|
||||
) : (
|
||||
<Typography
|
||||
variant="h4"
|
||||
component="h1"
|
||||
gutterBottom
|
||||
sx={{ color: 'inherit', textDecoration: 'none', mr: 1 }}
|
||||
>
|
||||
{displayName} by {creator}
|
||||
</Typography>
|
||||
)}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
{isProcessing && (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 0.5,
|
||||
mr: 0.5,
|
||||
height: 24
|
||||
}}>
|
||||
{[0, 1, 2].map((i) => (
|
||||
<Box
|
||||
key={i}
|
||||
sx={{
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'primary.main',
|
||||
animation: `${pulse} 1.4s ease-in-out infinite`,
|
||||
animationDelay: `${i * 0.2}s`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
<StatusChip status={statusId as number} />
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
||||
<Avatar
|
||||
src={submitterAvatarUrl}
|
||||
sx={{ mr: 1, width: 24, height: 24, border: '1px solid rgba(255, 255, 255, 0.1)', bgcolor: 'grey.900' }}
|
||||
/>
|
||||
<Link href={`https://www.roblox.com/users/${submitterId}/profile`} target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'none', color: 'inherit' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, '&:hover': { textDecoration: 'underline' } }}>
|
||||
<Typography>
|
||||
{submitterUsername ? `@${submitterUsername}` : submitterId}
|
||||
</Typography>
|
||||
<LaunchIcon sx={{ fontSize: '1rem', color: 'text.secondary' }} />
|
||||
</Box>
|
||||
</Link>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,87 +0,0 @@
|
||||
import React, {JSX} from "react";
|
||||
import {Cancel, CheckCircle, Pending} from "@mui/icons-material";
|
||||
import {Chip} from "@mui/material";
|
||||
|
||||
export const StatusChip = ({status}: { status: number }) => {
|
||||
let color: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' = 'default';
|
||||
let icon: JSX.Element = <Pending fontSize="small"/>;
|
||||
let label: string = 'Unknown';
|
||||
|
||||
switch (status) {
|
||||
case 0:
|
||||
color = 'warning';
|
||||
icon = <Pending fontSize="small"/>;
|
||||
label = 'Under Construction';
|
||||
break;
|
||||
case 1:
|
||||
color = 'warning';
|
||||
icon = <Pending fontSize="small"/>;
|
||||
label = 'Changes Requested';
|
||||
break;
|
||||
case 2:
|
||||
color = 'info';
|
||||
icon = <Pending fontSize="small"/>;
|
||||
label = 'Submitting';
|
||||
break;
|
||||
case 3:
|
||||
color = 'warning';
|
||||
icon = <CheckCircle fontSize="small"/>;
|
||||
label = 'Under Review';
|
||||
break;
|
||||
case 4:
|
||||
color = 'warning';
|
||||
icon = <Pending fontSize="small"/>;
|
||||
label = 'Script Review';
|
||||
break;
|
||||
case 5:
|
||||
color = 'info';
|
||||
icon = <Pending fontSize="small"/>;
|
||||
label = 'Validating';
|
||||
break;
|
||||
case 6:
|
||||
color = 'success';
|
||||
icon = <CheckCircle fontSize="small"/>;
|
||||
label = 'Validated';
|
||||
break;
|
||||
case 7:
|
||||
color = 'info';
|
||||
icon = <Pending fontSize="small"/>;
|
||||
label = 'Uploading';
|
||||
break;
|
||||
case 8:
|
||||
color = 'success';
|
||||
icon = <CheckCircle fontSize="small"/>;
|
||||
label = 'Uploaded';
|
||||
break;
|
||||
case 9:
|
||||
color = 'error';
|
||||
icon = <Cancel fontSize="small"/>;
|
||||
label = 'Rejected';
|
||||
break;
|
||||
case 10:
|
||||
color = 'success';
|
||||
icon = <CheckCircle fontSize="small"/>;
|
||||
label = 'Released';
|
||||
break;
|
||||
default:
|
||||
color = 'default';
|
||||
icon = <Pending fontSize="small"/>;
|
||||
label = 'Unknown';
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<Chip
|
||||
icon={icon}
|
||||
label={label}
|
||||
color={color}
|
||||
size="small"
|
||||
sx={{
|
||||
height: 24,
|
||||
fontSize: '0.75rem',
|
||||
fontWeight: 600,
|
||||
boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -3,10 +3,8 @@
|
||||
import Header from "./header";
|
||||
|
||||
export default function Webpage({children}: Readonly<{children?: React.ReactNode}>) {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
return <>
|
||||
<Header/>
|
||||
{children}
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import GameSelection from "./_game";
|
||||
import SendIcon from '@mui/icons-material/Send';
|
||||
import Webpage from "@/app/_components/webpage"
|
||||
import React, { useState } from "react";
|
||||
import {useTitle} from "@/app/hooks/useTitle";
|
||||
|
||||
import "./(styles)/page.scss"
|
||||
|
||||
@@ -21,8 +20,6 @@ interface IdResponse {
|
||||
}
|
||||
|
||||
export default function SubmissionInfoPage() {
|
||||
useTitle("Admin Submit");
|
||||
|
||||
const [game, setGame] = useState(1);
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
@@ -35,7 +32,7 @@ export default function SubmissionInfoPage() {
|
||||
DisplayName: (formData.get("display-name") as string) ?? "unknown", // TEMPORARY! TODO: Change
|
||||
Creator: (formData.get("creator") as string) ?? "unknown", // TEMPORARY! TODO: Change
|
||||
GameID: game,
|
||||
AssetID: Number((formData.get("asset-id") as string) ?? "-1"),
|
||||
AssetID: Number((formData.get("asset-id") as string) ?? "0"),
|
||||
};
|
||||
|
||||
console.log(payload)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,61 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
function chunkArray<T>(arr: T[], size: number): T[][] {
|
||||
const res: T[][] = [];
|
||||
for (let i = 0; i < arr.length; i += size) {
|
||||
res.push(arr.slice(i, i + size));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches thumbnail URLs for a batch of asset IDs using the unified /thumbnails/batch endpoint.
|
||||
* Handles loading and error state. Returns a mapping of assetId to URL.
|
||||
*/
|
||||
export function useBatchThumbnails(assetIds: (number | string)[] | undefined) {
|
||||
const [thumbnails, setThumbnails] = useState<Record<number, string>>({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!assetIds || assetIds.length === 0) {
|
||||
setThumbnails({});
|
||||
setLoading(false);
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
const filteredIds = assetIds.filter(Boolean);
|
||||
if (filteredIds.length === 0) {
|
||||
setThumbnails({});
|
||||
setLoading(false);
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const chunks = chunkArray(filteredIds, 50);
|
||||
Promise.all(
|
||||
chunks.map(chunk =>
|
||||
fetch(`/thumbnails/batch?type=asset&ids=${chunk.join(",")}&type=asset`)
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error(`Failed to fetch thumbnails: ${res.status}`);
|
||||
return res.json();
|
||||
})
|
||||
)
|
||||
)
|
||||
.then(datas => {
|
||||
const result: Record<number, string> = {};
|
||||
for (const data of datas) {
|
||||
for (const [id, url] of Object.entries(data)) {
|
||||
if (url) result[Number(id)] = url as string;
|
||||
}
|
||||
}
|
||||
setThumbnails(result);
|
||||
})
|
||||
.catch(err => setError(err))
|
||||
.finally(() => setLoading(false));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [assetIds && assetIds.filter(Boolean).join(",")]);
|
||||
|
||||
return { thumbnails, loading, error };
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
function chunkArray<T>(arr: T[], size: number): T[][] {
|
||||
const res: T[][] = [];
|
||||
for (let i = 0; i < arr.length; i += size) {
|
||||
res.push(arr.slice(i, i + size));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches avatar URLs for a batch of user IDs using the unified /thumbnails/batch?type=user endpoint.
|
||||
* Returns a mapping of userId to avatar URL.
|
||||
*/
|
||||
export function useBatchUserAvatars(userIds: (number | string)[] | undefined) {
|
||||
const [avatars, setAvatars] = useState<Record<number, string>>({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userIds || userIds.length === 0) {
|
||||
setAvatars({});
|
||||
setLoading(false);
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
const filteredIds = userIds.filter(Boolean);
|
||||
if (filteredIds.length === 0) {
|
||||
setAvatars({});
|
||||
setLoading(false);
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const chunks = chunkArray(filteredIds, 50);
|
||||
Promise.all(
|
||||
chunks.map(chunk =>
|
||||
fetch(`/thumbnails/batch?type=user&ids=${chunk.join(",")}`)
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error(`Failed to fetch user avatars: ${res.status}`);
|
||||
return res.json();
|
||||
})
|
||||
)
|
||||
)
|
||||
.then(datas => {
|
||||
const result: Record<number, string> = {};
|
||||
for (const data of datas) {
|
||||
for (const [id, url] of Object.entries(data)) {
|
||||
if (url) result[Number(id)] = url as string;
|
||||
}
|
||||
}
|
||||
setAvatars(result);
|
||||
})
|
||||
.catch(err => setError(err))
|
||||
.finally(() => setLoading(false));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [userIds && userIds.filter(Boolean).join(",")]);
|
||||
|
||||
return { avatars, loading, error };
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
function chunkArray<T>(arr: T[], size: number): T[][] {
|
||||
const res: T[][] = [];
|
||||
for (let i = 0; i < arr.length; i += size) {
|
||||
res.push(arr.slice(i, i + size));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches usernames for a batch of user IDs using the /proxy/users/batch?ids=... endpoint.
|
||||
* Returns a mapping of userId to username (or userId as string if not found).
|
||||
*/
|
||||
export function useBatchUsernames(userIds: (number | string)[] | undefined) {
|
||||
const [usernames, setUsernames] = useState<Record<number, string>>({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userIds || userIds.length === 0) {
|
||||
setUsernames({});
|
||||
setLoading(false);
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
const filteredIds = userIds.filter(Boolean);
|
||||
if (filteredIds.length === 0) {
|
||||
setUsernames({});
|
||||
setLoading(false);
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const chunks = chunkArray(filteredIds, 50);
|
||||
Promise.all(
|
||||
chunks.map(chunk =>
|
||||
fetch(`/proxy/users/batch?ids=${chunk.join(",")}`)
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error(`Failed to fetch usernames: ${res.status}`);
|
||||
return res.json();
|
||||
})
|
||||
)
|
||||
)
|
||||
.then(datas => {
|
||||
const result: Record<number, string> = {};
|
||||
for (const data of datas) {
|
||||
if (Array.isArray(data.data)) {
|
||||
for (const user of data.data) {
|
||||
result[user.id] = user.name || String(user.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
setUsernames(result);
|
||||
})
|
||||
.catch(err => setError(err))
|
||||
.finally(() => setLoading(false));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [userIds && userIds.filter(Boolean).join(",")]);
|
||||
|
||||
return { usernames, loading, error };
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
import {useState, useEffect, useCallback} from "react";
|
||||
import {Roles, RolesConstants} from "@/app/ts/Roles";
|
||||
import {AuditEvent} from "@/app/ts/AuditEvent";
|
||||
import {Status, StatusMatches} from "@/app/ts/Status";
|
||||
import {MapfixInfo} from "@/app/ts/Mapfix";
|
||||
import {SubmissionInfo} from "@/app/ts/Submission";
|
||||
|
||||
type ReviewItemType = "submissions" | "mapfixes";
|
||||
type ReviewData = MapfixInfo | SubmissionInfo;
|
||||
|
||||
interface UseReviewDataProps {
|
||||
itemType: ReviewItemType;
|
||||
itemId: string | number;
|
||||
}
|
||||
|
||||
interface UseReviewDataResult {
|
||||
data: ReviewData | null;
|
||||
auditEvents: AuditEvent[];
|
||||
roles: Roles;
|
||||
user: number | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
refreshData: (skipLoadingState?: boolean) => Promise<void>;
|
||||
}
|
||||
|
||||
export function useReviewData({itemType, itemId}: UseReviewDataProps): UseReviewDataResult {
|
||||
const [data, setData] = useState<ReviewData | null>(null);
|
||||
const [auditEvents, setAuditEvents] = useState<AuditEvent[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [user, setUser] = useState<number | null>(null);
|
||||
const [roles, setRoles] = useState<Roles>(RolesConstants.Empty);
|
||||
|
||||
const fetchData = useCallback(async (skipLoadingState = false) => {
|
||||
try {
|
||||
if (!skipLoadingState) {
|
||||
setLoading(true);
|
||||
}
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const [reviewData, auditData] = await Promise.all([
|
||||
fetch(`/api/${itemType}/${itemId}`).then(res => {
|
||||
if (!res.ok) throw new Error(`Failed to fetch ${itemType.slice(0, -1)}: ${res.status}`);
|
||||
return res.json();
|
||||
}),
|
||||
fetch(`/api/${itemType}/${itemId}/audit-events?Page=1&Limit=100`).then(res => {
|
||||
if (!res.ok) throw new Error(`Failed to fetch audit events: ${res.status}`);
|
||||
return res.json();
|
||||
})
|
||||
]);
|
||||
|
||||
setData(reviewData);
|
||||
setAuditEvents(auditData);
|
||||
} catch (error) {
|
||||
console.error(`Error fetching critical ${itemType} data:`, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
try {
|
||||
const userResponse = await fetch("/api/session/user");
|
||||
if (userResponse.ok) {
|
||||
const userData = await userResponse.json();
|
||||
setUser(userData.UserID);
|
||||
} else {
|
||||
console.warn(`Failed to fetch user: ${userResponse.status}`);
|
||||
setUser(null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Error fetching user data:", error);
|
||||
setUser(null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching review data:", error);
|
||||
setError(error instanceof Error ? error.message : `Failed to load ${itemType.slice(0, -1)} details`);
|
||||
} finally {
|
||||
if (!skipLoadingState) {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}, [itemId, itemType]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [fetchData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
if (StatusMatches(data.StatusID, [Status.Uploading, Status.Submitting, Status.Validating])) {
|
||||
const intervalId = setInterval(() => {
|
||||
fetchData(true);
|
||||
}, 5000);
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
}
|
||||
}
|
||||
}, [data, itemType, fetchData]);
|
||||
|
||||
return {
|
||||
data,
|
||||
auditEvents,
|
||||
roles,
|
||||
user,
|
||||
loading,
|
||||
error,
|
||||
refreshData: fetchData
|
||||
};
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export function useTitle(title: string) {
|
||||
useEffect(() => {
|
||||
document.title = `${title} | StrafesNET`;
|
||||
}, [title]);
|
||||
}
|
||||
@@ -1,26 +1,9 @@
|
||||
import "./globals.scss";
|
||||
import { SWRConfig } from "swr";
|
||||
import { getSessionUser, getSessionRoles } from "@/app/lib/session";
|
||||
import AppProviders from "@/app/_components/AppProviders";
|
||||
|
||||
export default async function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
|
||||
const user = await getSessionUser();
|
||||
const roles = await getSessionRoles();
|
||||
|
||||
export default function RootLayout({children}: Readonly<{children: React.ReactNode}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<SWRConfig value={{
|
||||
fallback: {
|
||||
"/api/session/user": user,
|
||||
"/api/session/roles": { Roles: roles }
|
||||
}
|
||||
}}>
|
||||
<AppProviders>
|
||||
{children}
|
||||
</AppProviders>
|
||||
</SWRConfig>
|
||||
</body>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import path from 'path';
|
||||
import { promises as fs } from 'fs';
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export async function errorImageResponse(
|
||||
statusCode: number = 500,
|
||||
options?: { message?: string }
|
||||
): Promise<NextResponse> {
|
||||
const file = `${statusCode}.png`;
|
||||
const filePath = path.join(process.cwd(), 'public/errors', file);
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'image/png',
|
||||
};
|
||||
if (options?.message) {
|
||||
headers['X-Error-Message'] = encodeURIComponent(options.message);
|
||||
}
|
||||
|
||||
try {
|
||||
const buffer = await fs.readFile(filePath);
|
||||
headers['Content-Length'] = buffer.length.toString();
|
||||
return new NextResponse(buffer, {
|
||||
status: statusCode,
|
||||
headers,
|
||||
});
|
||||
} catch {
|
||||
const fallback = path.join(process.cwd(), 'public/errors', '500.png');
|
||||
const buffer = await fs.readFile(fallback);
|
||||
headers['Content-Length'] = buffer.length.toString();
|
||||
return new NextResponse(buffer, {
|
||||
status: 500,
|
||||
headers,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
const BASE_URL = process.env.API_HOST;
|
||||
|
||||
export async function getSessionUser() {
|
||||
const cookieStore = await cookies();
|
||||
const cookieHeader = cookieStore.toString();
|
||||
const res = await fetch(`${BASE_URL}/session/user`, {
|
||||
headers: { Cookie: cookieHeader },
|
||||
cache: 'no-store',
|
||||
});
|
||||
if (!res.ok) return null;
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
export async function getSessionRoles() {
|
||||
const cookieStore = await cookies();
|
||||
const cookieHeader = cookieStore.toString();
|
||||
const res = await fetch(`${BASE_URL}/session/roles`, {
|
||||
headers: { Cookie: cookieHeader },
|
||||
cache: 'no-store',
|
||||
});
|
||||
if (!res.ok) return 0;
|
||||
const data = await res.json();
|
||||
return data.Roles ?? 0;
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import {createTheme} from "@mui/material";
|
||||
|
||||
export const theme = createTheme({
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
primary: {
|
||||
main: '#90caf9',
|
||||
},
|
||||
secondary: {
|
||||
main: '#f48fb1',
|
||||
},
|
||||
background: {
|
||||
default: '#121212',
|
||||
paper: '#1e1e1e',
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
|
||||
h5: {
|
||||
fontWeight: 500,
|
||||
letterSpacing: '0.5px',
|
||||
},
|
||||
subtitle1: {
|
||||
fontWeight: 500,
|
||||
fontSize: '0.95rem',
|
||||
},
|
||||
body2: {
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
caption: {
|
||||
fontSize: '0.75rem',
|
||||
},
|
||||
},
|
||||
shape: {
|
||||
borderRadius: 8,
|
||||
},
|
||||
components: {
|
||||
MuiCard: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: 8,
|
||||
overflow: 'hidden',
|
||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
||||
transition: 'transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out',
|
||||
'&:hover': {
|
||||
transform: 'translateY(-4px)',
|
||||
boxShadow: '0 8px 16px rgba(0, 0, 0, 0.2)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiCardMedia: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiCardContent: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
padding: 16,
|
||||
'&:last-child': {
|
||||
paddingBottom: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiChip: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
fontWeight: 500,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiDivider: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderColor: 'rgba(255, 255, 255, 0.1)',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundImage: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
75
web/src/app/mapfixes/(styles)/page.scss
Normal file
75
web/src/app/mapfixes/(styles)/page.scss
Normal file
@@ -0,0 +1,75 @@
|
||||
@forward "../../_components/styles/mapCard.scss";
|
||||
|
||||
@use "../../globals.scss";
|
||||
|
||||
a {
|
||||
color:rgb(255, 255, 255);
|
||||
|
||||
&:visited, &:hover, &:focus {
|
||||
text-decoration: none;
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
&:active {
|
||||
color: rgb(192, 192, 192)
|
||||
}
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
grid-template-rows: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin: 0.3rem;
|
||||
}
|
||||
|
||||
.pagination button {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 1.15rem;
|
||||
border: none;
|
||||
border-radius: 0.35rem;
|
||||
background-color: #33333350;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pagination button:disabled {
|
||||
background-color: #5555559a;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.pagination-dots {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background-color: #bbb;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dot.active {
|
||||
background-color: #333;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user