79 Commits

Author SHA1 Message Date
0324a57e5c kinda done 2025-04-09 15:36:45 -07:00
ca750669db t 2025-04-09 15:23:27 -07:00
ccf1ff6866 wip 2025-04-09 15:23:27 -07:00
1f1deacc2d submissions: filter AddInt method for type safety 2025-04-09 15:23:27 -07:00
d8095cee83 openapi: generate 2025-04-09 15:23:07 -07:00
28985bed55 openapi: date remains int64 2025-04-09 15:23:03 -07:00
a03e812e46 openapi: use unsigned integers 2025-04-09 14:51:29 -07:00
7334e88b55 validation: update api to yield a better error 2025-04-08 16:56:48 -07:00
b93c813dec submissions: fix faulty endpoints 2025-04-08 16:40:39 -07:00
926a90329b submissions-api: v0.7.0 2025-04-08 14:22:55 -07:00
18abbd92ce web: implement trigger-submit + transpose weakly associated action list 2025-04-08 13:45:53 -07:00
c923a8a076 submissions: implement validator-submitted endpoint 2025-04-08 13:21:07 -07:00
d6da6f003e submissions: implement reset-submitting endpoint 2025-04-08 13:17:39 -07:00
0dc7aec395 submissions: rename endpoints 2025-04-08 13:12:13 -07:00
c85cb63639 openapi: generate 2025-04-08 13:06:35 -07:00
6c865e8841 openapi: prepare for map checks 2025-04-08 13:06:26 -07:00
99a082afb5 submisions: improve error precision 2025-04-08 12:56:07 -07:00
434cd295f5 submissions: implement audit endpoints 2025-04-08 12:49:16 -07:00
bfc2a2cbca openapi: generate 2025-04-08 12:41:56 -07:00
c24db2c3a0 openapi: allow listing 0 items 2025-04-08 12:41:16 -07:00
68f2311658 openapi: audit endpoints 2025-04-08 12:41:15 -07:00
163412a253 openapi: extend api StatusID maximum to match changes 2025-04-08 00:01:07 -07:00
044033cabf submissions: implement audit logging
- use uint for Operation.Owner
- remove IsSubmitter
2025-04-07 20:29:32 -07:00
219a15f656 submissions: audit events db table 2025-04-07 20:29:29 -07:00
383bc783a4 submissions: audit model 2025-04-07 20:29:29 -07:00
24a5baae77 web: todo: hide Reset buttons for 10 seconds 2025-04-07 13:35:41 -07:00
4ba3b5cd01 web: change up status ids 2025-04-07 13:35:41 -07:00
f610fc1c0f submissions: change up status ids in preparation of submission validation 2025-04-07 13:35:41 -07:00
e67d679901 submissions: rename mapfix const to match submissions 2025-04-07 13:10:24 -07:00
3c3d09c4a7 web: display target asset thumbnail alongside mapfix 2025-04-06 16:09:00 -07:00
d02e3776f3 web: fix page dots 2025-04-06 15:50:08 -07:00
77222c84db web: plumb target asset id and submitter 2025-04-06 15:48:37 -07:00
412f34817c submissions: more filtering options for listing submissions 2025-04-06 15:31:45 -07:00
cac288d73b openapi: generate 2025-04-06 15:29:50 -07:00
29e414d6e7 openapi: more filtering options for listing submissions 2025-04-06 15:29:27 -07:00
c9ba2e3e6e web: use date descending sort 2025-04-06 15:15:31 -07:00
0666685a49 web: implement new list api with Total field for pages 2025-04-06 15:15:19 -07:00
ff9237e453 submissions: count total items 2025-04-06 15:13:27 -07:00
9b5f7e0b0c openapi: generate 2025-04-06 15:13:24 -07:00
e28c7e8149 openapi: include total count in list requests 2025-04-06 15:13:20 -07:00
220ea84e22 submissions: AddNotNil is for pointers 2025-04-05 19:36:36 -07:00
7648f407c5 openapi: generate 2025-04-05 19:27:55 -07:00
e0266c5d24 openapi: set minimum for all integers, maximum for some 2025-04-05 19:27:44 -07:00
9ab2e23fa9 submissions: do not allow changing model after submit 2025-04-05 19:00:08 -07:00
6b2f5e29e7 api: improve consistency with internal api 2025-04-05 18:56:39 -07:00
d42e89fcb4 submissions: switch to unsigned integers in database and nats messages 2025-04-05 17:26:35 -07:00
7e881e6ac5 submissions: omit user info check 2025-04-05 17:12:19 -07:00
2d57b945f2 submissions: what??? how did this ever work? 2025-04-05 17:11:10 -07:00
005e99424e validator: update rbx_asset to fix model info download 2025-04-05 14:54:26 -07:00
a330b1c43b validator: update rbx_asset api 2025-04-05 14:39:42 -07:00
d2662eb833 validator: switch to cloud api where possible 2025-04-05 14:39:42 -07:00
3ba599114d validator: relax read_dom trait bound 2025-04-05 14:39:42 -07:00
d53f61fb5b submissions: fix operations CountSince ()
Reviewed-on: 
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-04-05 19:41:42 +00:00
5d259e20f2 submissions: rate limit submit 2025-04-04 20:08:48 -07:00
21b6903943 submissions: count recent operations 2025-04-04 20:07:09 -07:00
14c7979310 web: activate ai dark mode 2025-04-04 19:33:48 -07:00
e376e02dc1 web: ai the maps page 2025-04-04 19:33:38 -07:00
4e7ee9dc5a rename "Accepted" status to "AcceptedUnvalidated" 2025-04-04 19:04:48 -07:00
ceaec14242 submissions-api: fix validated-model request 2025-04-04 18:41:33 -07:00
9372caa157 web: fix mapfix thumbnails 2025-04-04 17:11:41 -07:00
f73c274367 web: move _map to _mapImage 2025-04-04 17:11:32 -07:00
c50a28443e web: remove ratings 2025-04-04 17:02:51 -07:00
c7150f1e23 web: fix mapfixes cards linking to submissions 2025-04-04 17:02:51 -07:00
f16a817da2 web: maps: format date 2025-04-04 16:26:37 -07:00
e858d252ab web: add map image to map page 2025-04-04 16:13:22 -07:00
66e0d22ccd web: add bare bones map info 2025-04-04 15:56:18 -07:00
986ecfc7ad docker: use tagged muslrust 2025-04-04 15:42:43 -07:00
66890ccd44 validation: detect nats filter_subject mismatch and update consumer 2025-04-04 15:36:52 -07:00
ec15c1f2e5 Hide Irrelevant Review Buttons ()
Closes .

Reviewed-on: 
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-04-04 22:10:31 +00:00
8be9475ee5 web: route to provided path on operation success 2025-04-03 20:03:48 -07:00
0cb2bec6e0 web: fix mapfix submit 2025-04-03 19:24:43 -07:00
cf1906acaa web: fix mapfix href 2025-04-04 02:10:44 +00:00
7e93807298 docker: use itzaname docker proxy to avoid getting rate-limited 2025-04-03 19:05:15 -07:00
ee5b3331b4 validator: write correct asset version 2025-04-03 16:18:31 -07:00
29c0acf3b2 web: add fix map button 2025-04-03 15:47:44 -07:00
a844c4e90a validation: skip upload if model validates as-is 2025-04-03 15:36:06 -07:00
5ed15a6847 validation: rename error 2025-04-03 15:25:02 -07:00
1ff1cae709 web: reduce polling interval
The operations will usually take half a second.
2025-04-03 15:12:18 -07:00
c6ebe5a360 stop polling on completeion/fail 2025-04-03 15:45:59 -06:00
92 changed files with 14439 additions and 2125 deletions
Cargo.lockContainerfileopenapi-internal.yamlopenapi.yaml
pkg
validation
web

6
Cargo.lock generated

@ -1297,9 +1297,9 @@ dependencies = [
[[package]]
name = "rbx_asset"
version = "0.3.3"
version = "0.4.3"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "91722b37549ded270f39556194ca03d03e08bd70674d239ec845765ed9e42b7d"
checksum = "077e2a0b201a777dfd2ff822766ae7d0c8c3003206115da57f7bce15ee73cbc7"
dependencies = [
"chrono",
"flate2",
@ -1798,7 +1798,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "submissions-api"
version = "0.6.1"
version = "0.7.0"
dependencies = [
"reqwest",
"serde",

@ -1,5 +1,5 @@
# Stage 1: Build
FROM docker.io/golang:1.23 AS builder
FROM registry.itzana.me/docker-proxy/golang:1.24 AS builder
# Set the working directory in the container
WORKDIR /app
@ -14,7 +14,7 @@ COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o service ./cmd/maps-service/service.go
# Stage 2: Run
FROM alpine
FROM registry.itzana.me/docker-proxy/alpine:3.21
# Set up a non-root user for security
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

@ -47,13 +47,32 @@ paths:
required: true
schema:
type: integer
format: int64
format: uint64
minimum: 0
- name: ValidatedModelVersion
in: query
required: true
schema:
type: integer
format: int64
format: uint64
minimum: 0
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
operationId: actionMapfixSubmitted
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
responses:
"204":
description: Successful response
@ -183,13 +202,32 @@ paths:
required: true
schema:
type: integer
format: int64
format: uint64
minimum: 0
- name: ValidatedModelVersion
in: query
required: true
schema:
type: integer
format: int64
format: uint64
minimum: 0
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
operationId: actionSubmissionSubmitted
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
responses:
"204":
description: Successful response
@ -253,7 +291,8 @@ paths:
required: true
schema:
type: integer
format: int64
format: uint64
minimum: 0
responses:
"204":
description: Successful response
@ -282,12 +321,14 @@ paths:
in: query
schema:
type: integer
format: int64
format: uint64
minimum: 0
- name: Policy
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
responses:
"200":
description: Successful response
@ -356,12 +397,14 @@ paths:
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
- name: ResourceID
in: query
schema:
type: integer
format: int64
format: uint64
minimum: 0
responses:
"200":
description: Successful response
@ -431,7 +474,8 @@ components:
description: The unique identifier for a submission.
schema:
type: integer
format: int64
format: uint64
minimum: 0
OperationID:
name: OperationID
in: path
@ -439,7 +483,8 @@ components:
description: The unique identifier for a long-running operation.
schema:
type: integer
format: int32
format: uint32
minimum: 0
SubmissionID:
name: SubmissionID
in: path
@ -447,7 +492,8 @@ components:
description: The unique identifier for a submission.
schema:
type: integer
format: int64
format: uint64
minimum: 0
ScriptID:
name: ScriptID
in: path
@ -455,14 +501,15 @@ components:
description: The unique identifier for a script.
schema:
type: integer
format: int64
format: uint64
minimum: 0
Page:
name: Page
in: query
required: true
schema:
type: integer
format: int32
format: uint32
minimum: 1
Limit:
name: Limit
@ -470,7 +517,7 @@ components:
required: true
schema:
type: integer
format: int32
format: uint32
minimum: 1
maximum: 100
schemas:
@ -481,7 +528,8 @@ components:
properties:
MapfixID:
type: integer
format: int64
format: uint64
minimum: 0
SubmissionID:
required:
- SubmissionID
@ -489,7 +537,8 @@ components:
properties:
SubmissionID:
type: integer
format: int64
format: uint64
minimum: 0
ScriptID:
required:
- ScriptID
@ -497,7 +546,8 @@ components:
properties:
ScriptID:
type: integer
format: int64
format: uint64
minimum: 0
ScriptPolicyID:
required:
- ScriptPolicyID
@ -505,7 +555,8 @@ components:
properties:
ScriptPolicyID:
type: integer
format: int64
format: uint64
minimum: 0
MapfixCreate:
required:
- OperationID
@ -520,10 +571,12 @@ components:
properties:
OperationID:
type: integer
format: int32
format: uint32
minimum: 0
AssetOwner:
type: integer
format: int64
format: uint64
minimum: 0
DisplayName:
type: string
maxLength: 128
@ -532,16 +585,20 @@ components:
maxLength: 128
GameID:
type: integer
format: int32
format: uint32
minimum: 0
AssetID:
type: integer
format: int64
format: uint64
minimum: 0
AssetVersion:
type: integer
format: int64
format: uint64
minimum: 0
TargetAssetID:
type: integer
format: int64
format: uint64
minimum: 0
SubmissionCreate:
required:
- OperationID
@ -555,10 +612,12 @@ components:
properties:
OperationID:
type: integer
format: int32
format: uint32
minimum: 0
AssetOwner:
type: integer
format: int64
format: uint64
minimum: 0
DisplayName:
type: string
maxLength: 128
@ -567,13 +626,16 @@ components:
maxLength: 128
GameID:
type: integer
format: int32
format: uint32
minimum: 0
AssetID:
type: integer
format: int64
format: uint64
minimum: 0
AssetVersion:
type: integer
format: int64
format: uint64
minimum: 0
Script:
required:
- ID
@ -586,7 +648,8 @@ components:
properties:
ID:
type: integer
format: int64
format: uint64
minimum: 0
Name:
type: string
maxLength: 128
@ -599,10 +662,12 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: int32
format: uint32
minimum: 0
ResourceID:
type: integer
format: int64
format: uint64
minimum: 0
ScriptCreate:
required:
- Name
@ -619,10 +684,12 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: int32
format: uint32
minimum: 0
ResourceID:
type: integer
format: int64
format: uint64
minimum: 0
ScriptPolicy:
required:
- ID
@ -633,17 +700,20 @@ components:
properties:
ID:
type: integer
format: int64
format: uint64
minimum: 0
FromScriptHash:
type: string
minLength: 16
maxLength: 16
ToScriptID:
type: integer
format: int64
format: uint64
minimum: 0
Policy:
type: integer
format: int32
format: uint32
minimum: 0
ScriptPolicyCreate:
required:
- FromScriptID
@ -653,20 +723,24 @@ components:
properties:
FromScriptID:
type: integer
format: int64
format: uint64
minimum: 0
ToScriptID:
type: integer
format: int64
format: uint64
minimum: 0
Policy:
type: integer
format: int32
format: uint32
minimum: 0
Error:
description: Represents error object
type: object
properties:
code:
type: integer
format: int64
format: uint64
minimum: 0
message:
type: string
required:

@ -104,12 +104,16 @@ paths:
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 1
maximum: 5
- name: Sort
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
maximum: 4
responses:
"200":
description: Successful response
@ -171,21 +175,48 @@ paths:
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 1
maximum: 5
- name: Sort
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
maximum: 4
- name: Submitter
in: query
schema:
type: integer
format: uint64
minimum: 0
- name: AssetID
in: query
schema:
type: integer
format: uint64
minimum: 0
- name: TargetAssetID
in: query
schema:
type: integer
format: uint64
minimum: 0
- name: StatusID
in: query
schema:
type: integer
format: uint32
minimum: 0
maximum: 9
responses:
"200":
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Mapfix"
$ref: "#/components/schemas/Mapfixes"
default:
description: General Error
content:
@ -238,6 +269,56 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/audit-events:
get:
summary: Retrieve a list of audit events
operationId: listMapfixAuditEvents
tags:
- Mapfixes
security: []
parameters:
- $ref: '#/components/parameters/MapfixID'
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/Limit"
responses:
"200":
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/AuditEvent"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/comment:
post:
summary: Post a comment to the audit log
operationId: createMapfixAuditComment
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
requestBody:
required: true
content:
text/plain:
schema:
type: string
maxLength: 1024
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/model:
post:
summary: Update model following role restrictions
@ -251,13 +332,15 @@ paths:
required: true
schema:
type: integer
format: int64
- name: VersionID
format: uint64
minimum: 0
- name: ModelVersion
in: query
required: true
schema:
type: integer
format: int64
format: uint64
minimum: 0
responses:
"204":
description: Successful response
@ -284,10 +367,27 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/submit:
/mapfixes/{MapfixID}/status/trigger-submit:
post:
summary: Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted
operationId: actionMapfixSubmit
summary: Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting
operationId: actionMapfixTriggerSubmit
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/reset-submitting:
post:
summary: Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction
operationId: actionMapfixResetSubmitting
tags:
- Mapfixes
parameters:
@ -482,21 +582,48 @@ paths:
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 1
maximum: 5
- name: Sort
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
maximum: 4
- name: Submitter
in: query
schema:
type: integer
format: uint64
minimum: 0
- name: AssetID
in: query
schema:
type: integer
format: uint64
minimum: 0
- name: UploadedAssetID
in: query
schema:
type: integer
format: uint64
minimum: 0
- name: StatusID
in: query
schema:
type: integer
format: uint32
minimum: 0
maximum: 10
responses:
"200":
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Submission"
$ref: "#/components/schemas/Submissions"
default:
description: General Error
content:
@ -549,6 +676,56 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/audit-events:
get:
summary: Retrieve a list of audit events
operationId: listSubmissionAuditEvents
tags:
- Submissions
security: []
parameters:
- $ref: '#/components/parameters/SubmissionID'
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/Limit"
responses:
"200":
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/AuditEvent"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/comment:
post:
summary: Post a comment to the audit log
operationId: createSubmissionAuditComment
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
requestBody:
required: true
content:
text/plain:
schema:
type: string
maxLength: 1024
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/model:
post:
summary: Update model following role restrictions
@ -562,13 +739,15 @@ paths:
required: true
schema:
type: integer
format: int64
- name: VersionID
format: uint64
minimum: 0
- name: ModelVersion
in: query
required: true
schema:
type: integer
format: int64
format: uint64
minimum: 0
responses:
"204":
description: Successful response
@ -595,10 +774,27 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/submit:
/submissions/{SubmissionID}/status/trigger-submit:
post:
summary: Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted
operationId: actionSubmissionSubmit
summary: Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting
operationId: actionSubmissionTriggerSubmit
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/reset-submitting:
post:
summary: Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction
operationId: actionSubmissionResetSubmitting
tags:
- Submissions
parameters:
@ -793,12 +989,14 @@ paths:
in: query
schema:
type: integer
format: int64
format: uint64
minimum: 0
- name: Policy
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
responses:
"200":
description: Successful response
@ -928,12 +1126,14 @@ paths:
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
- name: ResourceID
in: query
schema:
type: integer
format: int64
format: uint64
minimum: 0
responses:
"200":
description: Successful response
@ -1047,7 +1247,8 @@ components:
description: The unique identifier for a map.
schema:
type: integer
format: int64
format: uint64
minimum: 0
MapfixID:
name: MapfixID
in: path
@ -1055,7 +1256,8 @@ components:
description: The unique identifier for a mapfix.
schema:
type: integer
format: int64
format: uint64
minimum: 0
OperationID:
name: OperationID
in: path
@ -1063,7 +1265,8 @@ components:
description: The unique identifier for a long-running operation.
schema:
type: integer
format: int32
format: uint32
minimum: 0
SubmissionID:
name: SubmissionID
in: path
@ -1071,7 +1274,8 @@ components:
description: The unique identifier for a submission.
schema:
type: integer
format: int64
format: uint64
minimum: 0
ScriptID:
name: ScriptID
in: path
@ -1079,7 +1283,8 @@ components:
description: The unique identifier for a script.
schema:
type: integer
format: int64
format: uint64
minimum: 0
ScriptPolicyID:
name: ScriptPolicyID
in: path
@ -1087,14 +1292,15 @@ components:
description: The unique identifier for a script policy.
schema:
type: integer
format: int64
format: uint64
minimum: 0
Page:
name: Page
in: query
required: true
schema:
type: integer
format: int32
format: uint32
minimum: 1
Limit:
name: Limit
@ -1102,10 +1308,44 @@ components:
required: true
schema:
type: integer
format: int32
minimum: 1
format: uint32
minimum: 0
maximum: 100
schemas:
AuditEvent:
type: object
required:
- ID
- Date
- User
- ResourceType
- ResourceID
- EventType
- EventData
properties:
ID:
type: integer
format: uint64
Date:
type: integer
format: int64
User:
type: integer
format: uint64
ResourceType:
type: integer
format: uint32
description: Is this a submission or is it a mapfix
ResourceID:
type: integer
format: uint64
EventType:
type: integer
format: uint32
EventData:
type: object
description: Arbitrary event data
additionalProperties: true
OperationID:
required:
- OperationID
@ -1113,7 +1353,8 @@ components:
properties:
OperationID:
type: integer
format: int32
format: uint32
minimum: 0
ScriptID:
required:
- ScriptID
@ -1121,7 +1362,8 @@ components:
properties:
ScriptID:
type: integer
format: int64
format: uint64
minimum: 0
ScriptPolicyID:
required:
- ScriptPolicyID
@ -1129,7 +1371,8 @@ components:
properties:
ScriptPolicyID:
type: integer
format: int64
format: uint64
minimum: 0
Roles:
required:
- Roles
@ -1137,7 +1380,8 @@ components:
properties:
Roles:
type: integer
format: int32
format: uint32
minimum: 0
User:
required:
- UserID
@ -1147,7 +1391,8 @@ components:
properties:
UserID:
type: integer
format: int64
format: uint64
minimum: 0
Username:
type: string
maxLength: 128
@ -1165,7 +1410,8 @@ components:
properties:
ID:
type: integer
format: int64
format: uint64
minimum: 0
DisplayName:
type: string
maxLength: 128
@ -1174,10 +1420,12 @@ components:
maxLength: 128
GameID:
type: integer
format: int32
format: uint32
minimum: 0
Date:
type: integer
format: int64
minimum: 0
Mapfix:
required:
- ID
@ -1197,7 +1445,8 @@ components:
properties:
ID:
type: integer
format: int64
format: uint64
minimum: 0
DisplayName:
type: string
maxLength: 128
@ -1206,33 +1455,55 @@ components:
maxLength: 128
GameID:
type: integer
format: int32
format: uint32
minimum: 0
CreatedAt:
type: integer
format: int64
minimum: 0
UpdatedAt:
type: integer
format: int64
minimum: 0
Submitter:
type: integer
format: int64
format: uint64
minimum: 0
AssetID:
type: integer
format: int64
format: uint64
minimum: 0
AssetVersion:
type: integer
format: int64
format: uint64
minimum: 0
Completed:
type: boolean
TargetAssetID:
type: integer
format: int64
format: uint64
minimum: 0
StatusID:
type: integer
format: int32
format: uint32
minimum: 0
StatusMessage:
type: string
maxLength: 256
Mapfixes:
type: object
required:
- Total
- Mapfixes
properties:
Total:
type: integer
format: uint64
minimum: 0
Mapfixes:
type: array
items:
$ref: "#/components/schemas/Mapfix"
MapfixTriggerCreate:
required:
- AssetID
@ -1241,10 +1512,12 @@ components:
properties:
AssetID:
type: integer
format: int64
format: uint64
minimum: 0
TargetAssetID:
type: integer
format: int64
format: uint64
minimum: 0
Operation:
required:
- OperationID
@ -1257,16 +1530,20 @@ components:
properties:
OperationID:
type: integer
format: int32
format: uint32
minimum: 0
Date:
type: integer
format: int64
minimum: 0
Owner:
type: integer
format: int64
format: uint64
minimum: 0
Status:
type: integer
format: int32
format: uint32
minimum: 0
StatusMessage:
type: string
maxLength: 256
@ -1294,7 +1571,8 @@ components:
properties:
ID:
type: integer
format: int64
format: uint64
minimum: 0
DisplayName:
type: string
maxLength: 128
@ -1303,39 +1581,63 @@ components:
maxLength: 128
GameID:
type: integer
format: int32
format: uint32
minimum: 0
CreatedAt:
type: integer
format: int64
format: uint64
minimum: 0
UpdatedAt:
type: integer
format: int64
format: uint64
minimum: 0
Submitter:
type: integer
format: int64
format: uint64
minimum: 0
AssetID:
type: integer
format: int64
format: uint64
minimum: 0
AssetVersion:
type: integer
format: int64
format: uint64
minimum: 0
ValidatedAssetID:
type: integer
format: int64
format: uint64
minimum: 0
ValidatedAssetVersion:
type: integer
format: int64
format: uint64
minimum: 0
Completed:
type: boolean
UploadedAssetID:
type: integer
format: int64
format: uint64
minimum: 0
StatusID:
type: integer
format: int32
format: uint32
minimum: 0
StatusMessage:
type: string
maxLength: 256
Submissions:
required:
- Total
- Submissions
type: object
properties:
Total:
type: integer
format: uint64
minimum: 0
Submissions:
type: array
items:
$ref: "#/components/schemas/Submission"
SubmissionTriggerCreate:
required:
- AssetID
@ -1343,7 +1645,8 @@ components:
properties:
AssetID:
type: integer
format: int64
format: uint64
minimum: 0
ReleaseInfo:
required:
- SubmissionID
@ -1352,7 +1655,8 @@ components:
properties:
SubmissionID:
type: integer
format: int64
format: uint64
minimum: 0
Date:
type: string
format: date-time
@ -1368,7 +1672,8 @@ components:
properties:
ID:
type: integer
format: int64
format: uint64
minimum: 0
Name:
type: string
maxLength: 128
@ -1381,10 +1686,12 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: int32
format: uint32
minimum: 0
ResourceID:
type: integer
format: int64
format: uint64
minimum: 0
ScriptCreate:
required:
- Name
@ -1401,10 +1708,12 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: int32
format: uint32
minimum: 0
ResourceID:
type: integer
format: int64
format: uint64
minimum: 0
ScriptUpdate:
required:
- ID
@ -1412,7 +1721,8 @@ components:
properties:
ID:
type: integer
format: int64
format: uint64
minimum: 0
Name:
type: string
maxLength: 128
@ -1421,10 +1731,12 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: int32
format: uint32
minimum: 0
ResourceID:
type: integer
format: int64
format: uint64
minimum: 0
ScriptPolicy:
required:
- ID
@ -1435,17 +1747,20 @@ components:
properties:
ID:
type: integer
format: int64
format: uint64
minimum: 0
FromScriptHash:
type: string
minLength: 16
maxLength: 16
ToScriptID:
type: integer
format: int64
format: uint64
minimum: 0
Policy:
type: integer
format: int32
format: uint32
minimum: 0
ScriptPolicyCreate:
required:
- FromScriptID
@ -1455,13 +1770,16 @@ components:
properties:
FromScriptID:
type: integer
format: int64
format: uint64
minimum: 0
ToScriptID:
type: integer
format: int64
format: uint64
minimum: 0
Policy:
type: integer
format: int32
format: uint32
minimum: 0
ScriptPolicyUpdate:
required:
- ID
@ -1469,23 +1787,28 @@ components:
properties:
ID:
type: integer
format: int64
format: uint64
minimum: 0
FromScriptID:
type: integer
format: int64
format: uint64
minimum: 0
ToScriptID:
type: integer
format: int64
format: uint64
minimum: 0
Policy:
type: integer
format: int32
format: uint32
minimum: 0
Error:
description: Represents error object
type: object
properties:
code:
type: integer
format: int64
format: uint64
minimum: 0
message:
type: string
required:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -9,25 +9,29 @@ const (
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
ActionMapfixRejectOperation OperationName = "ActionMapfixReject"
ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges"
ActionMapfixResetSubmittingOperation OperationName = "ActionMapfixResetSubmitting"
ActionMapfixRetryValidateOperation OperationName = "ActionMapfixRetryValidate"
ActionMapfixRevokeOperation OperationName = "ActionMapfixRevoke"
ActionMapfixSubmitOperation OperationName = "ActionMapfixSubmit"
ActionMapfixTriggerSubmitOperation OperationName = "ActionMapfixTriggerSubmit"
ActionMapfixTriggerUploadOperation OperationName = "ActionMapfixTriggerUpload"
ActionMapfixTriggerValidateOperation OperationName = "ActionMapfixTriggerValidate"
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject"
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
ActionSubmissionResetSubmittingOperation OperationName = "ActionSubmissionResetSubmitting"
ActionSubmissionRetryValidateOperation OperationName = "ActionSubmissionRetryValidate"
ActionSubmissionRevokeOperation OperationName = "ActionSubmissionRevoke"
ActionSubmissionSubmitOperation OperationName = "ActionSubmissionSubmit"
ActionSubmissionTriggerSubmitOperation OperationName = "ActionSubmissionTriggerSubmit"
ActionSubmissionTriggerUploadOperation OperationName = "ActionSubmissionTriggerUpload"
ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateMapfixOperation OperationName = "CreateMapfix"
CreateMapfixAuditCommentOperation OperationName = "CreateMapfixAuditComment"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
CreateSubmissionAuditCommentOperation OperationName = "CreateSubmissionAuditComment"
DeleteScriptOperation OperationName = "DeleteScript"
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"
GetMapOperation OperationName = "GetMap"
@ -36,10 +40,12 @@ const (
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"

File diff suppressed because it is too large Load Diff

@ -3,6 +3,7 @@
package api
import (
"fmt"
"io"
"mime"
"net/http"
@ -72,12 +73,54 @@ func (s *Server) decodeCreateMapfixRequest(r *http.Request) (
}
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) decodeCreateMapfixAuditCommentRequest(r *http.Request) (
req CreateMapfixAuditCommentReq,
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 = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = multierr.Append(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 == "text/plain":
reader := r.Body
request := CreateMapfixAuditCommentReq{Data: reader}
return request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeCreateScriptRequest(r *http.Request) (
req *ScriptCreate,
close func() error,
@ -206,6 +249,14 @@ func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
}
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)
@ -269,12 +320,54 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
}
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) decodeCreateSubmissionAuditCommentRequest(r *http.Request) (
req CreateSubmissionAuditCommentReq,
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 = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = multierr.Append(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 == "text/plain":
reader := r.Body
request := CreateSubmissionAuditCommentReq{Data: reader}
return request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeReleaseSubmissionsRequest(r *http.Request) (
req []ReleaseInfo,
close func() error,
@ -352,6 +445,23 @@ func (s *Server) decodeReleaseSubmissionsRequest(r *http.Request) (
}).ValidateLength(len(request)); err != nil {
return errors.Wrap(err, "array")
}
var failures []validate.FieldError
for i, elem := range request {
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
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
@ -490,6 +600,14 @@ func (s *Server) decodeUpdateScriptPolicyRequest(r *http.Request) (
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)

@ -25,6 +25,16 @@ func encodeCreateMapfixRequest(
return nil
}
func encodeCreateMapfixAuditCommentRequest(
req CreateMapfixAuditCommentReq,
r *http.Request,
) error {
const contentType = "text/plain"
body := req
ht.SetBody(r, body, contentType)
return nil
}
func encodeCreateScriptRequest(
req *ScriptCreate,
r *http.Request,
@ -67,6 +77,16 @@ func encodeCreateSubmissionRequest(
return nil
}
func encodeCreateSubmissionAuditCommentRequest(
req CreateSubmissionAuditCommentReq,
r *http.Request,
) error {
const contentType = "text/plain"
body := req
ht.SetBody(r, body, contentType)
return nil
}
func encodeReleaseSubmissionsRequest(
req []ReleaseInfo,
r *http.Request,

File diff suppressed because it is too large Load Diff

@ -34,6 +34,13 @@ func encodeActionMapfixRequestChangesResponse(response *ActionMapfixRequestChang
return nil
}
func encodeActionMapfixResetSubmittingResponse(response *ActionMapfixResetSubmittingNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixRetryValidateResponse(response *ActionMapfixRetryValidateNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -48,7 +55,7 @@ func encodeActionMapfixRevokeResponse(response *ActionMapfixRevokeNoContent, w h
return nil
}
func encodeActionMapfixSubmitResponse(response *ActionMapfixSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
func encodeActionMapfixTriggerSubmitResponse(response *ActionMapfixTriggerSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -97,6 +104,13 @@ func encodeActionSubmissionRequestChangesResponse(response *ActionSubmissionRequ
return nil
}
func encodeActionSubmissionResetSubmittingResponse(response *ActionSubmissionResetSubmittingNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionRetryValidateResponse(response *ActionSubmissionRetryValidateNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -111,7 +125,7 @@ func encodeActionSubmissionRevokeResponse(response *ActionSubmissionRevokeNoCont
return nil
}
func encodeActionSubmissionSubmitResponse(response *ActionSubmissionSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
func encodeActionSubmissionTriggerSubmitResponse(response *ActionSubmissionTriggerSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -153,6 +167,13 @@ func encodeCreateMapfixResponse(response *OperationID, w http.ResponseWriter, sp
return nil
}
func encodeCreateMapfixAuditCommentResponse(response *CreateMapfixAuditCommentNoContent, 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)
@ -195,6 +216,13 @@ func encodeCreateSubmissionResponse(response *OperationID, w http.ResponseWriter
return nil
}
func encodeCreateSubmissionAuditCommentResponse(response *CreateSubmissionAuditCommentNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeDeleteScriptResponse(response *DeleteScriptNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -293,7 +321,7 @@ func encodeGetSubmissionResponse(response *Submission, w http.ResponseWriter, sp
return nil
}
func encodeListMapfixesResponse(response []Mapfix, w http.ResponseWriter, span trace.Span) error {
func encodeListMapfixAuditEventsResponse(response []AuditEvent, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
@ -311,6 +339,20 @@ func encodeListMapfixesResponse(response []Mapfix, w http.ResponseWriter, span t
return nil
}
func encodeListMapfixesResponse(response *Mapfixes, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeListMapsResponse(response []Map, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
@ -365,7 +407,7 @@ func encodeListScriptsResponse(response []Script, w http.ResponseWriter, span tr
return nil
}
func encodeListSubmissionsResponse(response []Submission, w http.ResponseWriter, span trace.Span) error {
func encodeListSubmissionAuditEventsResponse(response []AuditEvent, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
@ -383,6 +425,20 @@ func encodeListSubmissionsResponse(response []Submission, w http.ResponseWriter,
return nil
}
func encodeListSubmissionsResponse(response *Submissions, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeReleaseSubmissionsResponse(response *ReleaseSubmissionsCreated, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))

@ -136,9 +136,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break
}
switch elem[0] {
case 'c': // Prefix: "completed"
case 'a': // Prefix: "audit-events"
if l := len("completed"); len(elem) >= l && elem[0:l] == "completed" {
if l := len("audit-events"); len(elem) >= l && elem[0:l] == "audit-events" {
elem = elem[l:]
} else {
break
@ -147,17 +147,75 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleSetMapfixCompletedRequest([1]string{
case "GET":
s.handleListMapfixAuditEventsRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
s.notAllowed(w, r, "GET")
}
return
}
case 'c': // Prefix: "com"
if l := len("com"); len(elem) >= l && elem[0:l] == "com" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'm': // Prefix: "ment"
if l := len("ment"); len(elem) >= l && elem[0:l] == "ment" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleCreateMapfixAuditCommentRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'p': // Prefix: "pleted"
if l := len("pleted"); len(elem) >= l && elem[0:l] == "pleted" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleSetMapfixCompletedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
}
case 'm': // Prefix: "model"
if l := len("model"); len(elem) >= l && elem[0:l] == "model" {
@ -260,6 +318,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break
}
switch elem[0] {
case 's': // Prefix: "submitting"
if l := len("submitting"); len(elem) >= l && elem[0:l] == "submitting" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionMapfixResetSubmittingRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'u': // Prefix: "uploading"
if l := len("uploading"); len(elem) >= l && elem[0:l] == "uploading" {
@ -352,28 +432,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
case 's': // Prefix: "submit"
if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionMapfixSubmitRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 't': // Prefix: "trigger-"
if l := len("trigger-"); len(elem) >= l && elem[0:l] == "trigger-" {
@ -386,6 +444,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break
}
switch elem[0] {
case 's': // Prefix: "submit"
if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionMapfixTriggerSubmitRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'u': // Prefix: "upload"
if l := len("upload"); len(elem) >= l && elem[0:l] == "upload" {
@ -832,9 +912,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break
}
switch elem[0] {
case 'c': // Prefix: "completed"
case 'a': // Prefix: "audit-events"
if l := len("completed"); len(elem) >= l && elem[0:l] == "completed" {
if l := len("audit-events"); len(elem) >= l && elem[0:l] == "audit-events" {
elem = elem[l:]
} else {
break
@ -843,17 +923,75 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleSetSubmissionCompletedRequest([1]string{
case "GET":
s.handleListSubmissionAuditEventsRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
s.notAllowed(w, r, "GET")
}
return
}
case 'c': // Prefix: "com"
if l := len("com"); len(elem) >= l && elem[0:l] == "com" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'm': // Prefix: "ment"
if l := len("ment"); len(elem) >= l && elem[0:l] == "ment" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleCreateSubmissionAuditCommentRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'p': // Prefix: "pleted"
if l := len("pleted"); len(elem) >= l && elem[0:l] == "pleted" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleSetSubmissionCompletedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
}
case 'm': // Prefix: "model"
if l := len("model"); len(elem) >= l && elem[0:l] == "model" {
@ -956,6 +1094,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break
}
switch elem[0] {
case 's': // Prefix: "submitting"
if l := len("submitting"); len(elem) >= l && elem[0:l] == "submitting" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionResetSubmittingRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'u': // Prefix: "uploading"
if l := len("uploading"); len(elem) >= l && elem[0:l] == "uploading" {
@ -1048,28 +1208,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
case 's': // Prefix: "submit"
if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionSubmitRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 't': // Prefix: "trigger-"
if l := len("trigger-"); len(elem) >= l && elem[0:l] == "trigger-" {
@ -1082,6 +1220,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break
}
switch elem[0] {
case 's': // Prefix: "submit"
if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionTriggerSubmitRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'u': // Prefix: "upload"
if l := len("upload"); len(elem) >= l && elem[0:l] == "upload" {
@ -1319,9 +1479,9 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break
}
switch elem[0] {
case 'c': // Prefix: "completed"
case 'a': // Prefix: "audit-events"
if l := len("completed"); len(elem) >= l && elem[0:l] == "completed" {
if l := len("audit-events"); len(elem) >= l && elem[0:l] == "audit-events" {
elem = elem[l:]
} else {
break
@ -1330,11 +1490,11 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = SetMapfixCompletedOperation
r.summary = "Called by maptest when a player completes the map"
r.operationID = "setMapfixCompleted"
r.pathPattern = "/mapfixes/{MapfixID}/completed"
case "GET":
r.name = ListMapfixAuditEventsOperation
r.summary = "Retrieve a list of audit events"
r.operationID = "listMapfixAuditEvents"
r.pathPattern = "/mapfixes/{MapfixID}/audit-events"
r.args = args
r.count = 1
return r, true
@ -1343,6 +1503,68 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
}
case 'c': // Prefix: "com"
if l := len("com"); len(elem) >= l && elem[0:l] == "com" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'm': // Prefix: "ment"
if l := len("ment"); len(elem) >= l && elem[0:l] == "ment" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = CreateMapfixAuditCommentOperation
r.summary = "Post a comment to the audit log"
r.operationID = "createMapfixAuditComment"
r.pathPattern = "/mapfixes/{MapfixID}/comment"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'p': // Prefix: "pleted"
if l := len("pleted"); len(elem) >= l && elem[0:l] == "pleted" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = SetMapfixCompletedOperation
r.summary = "Called by maptest when a player completes the map"
r.operationID = "setMapfixCompleted"
r.pathPattern = "/mapfixes/{MapfixID}/completed"
r.args = args
r.count = 1
return r, true
default:
return
}
}
}
case 'm': // Prefix: "model"
if l := len("model"); len(elem) >= l && elem[0:l] == "model" {
@ -1451,6 +1673,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break
}
switch elem[0] {
case 's': // Prefix: "submitting"
if l := len("submitting"); len(elem) >= l && elem[0:l] == "submitting" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionMapfixResetSubmittingOperation
r.summary = "Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction"
r.operationID = "actionMapfixResetSubmitting"
r.pathPattern = "/mapfixes/{MapfixID}/status/reset-submitting"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'u': // Prefix: "uploading"
if l := len("uploading"); len(elem) >= l && elem[0:l] == "uploading" {
@ -1551,30 +1797,6 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
case 's': // Prefix: "submit"
if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionMapfixSubmitOperation
r.summary = "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted"
r.operationID = "actionMapfixSubmit"
r.pathPattern = "/mapfixes/{MapfixID}/status/submit"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 't': // Prefix: "trigger-"
if l := len("trigger-"); len(elem) >= l && elem[0:l] == "trigger-" {
@ -1587,6 +1809,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break
}
switch elem[0] {
case 's': // Prefix: "submit"
if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionMapfixTriggerSubmitOperation
r.summary = "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting"
r.operationID = "actionMapfixTriggerSubmit"
r.pathPattern = "/mapfixes/{MapfixID}/status/trigger-submit"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'u': // Prefix: "upload"
if l := len("upload"); len(elem) >= l && elem[0:l] == "upload" {
@ -2113,9 +2359,9 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break
}
switch elem[0] {
case 'c': // Prefix: "completed"
case 'a': // Prefix: "audit-events"
if l := len("completed"); len(elem) >= l && elem[0:l] == "completed" {
if l := len("audit-events"); len(elem) >= l && elem[0:l] == "audit-events" {
elem = elem[l:]
} else {
break
@ -2124,11 +2370,11 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = SetSubmissionCompletedOperation
r.summary = "Called by maptest when a player completes the map"
r.operationID = "setSubmissionCompleted"
r.pathPattern = "/submissions/{SubmissionID}/completed"
case "GET":
r.name = ListSubmissionAuditEventsOperation
r.summary = "Retrieve a list of audit events"
r.operationID = "listSubmissionAuditEvents"
r.pathPattern = "/submissions/{SubmissionID}/audit-events"
r.args = args
r.count = 1
return r, true
@ -2137,6 +2383,68 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
}
case 'c': // Prefix: "com"
if l := len("com"); len(elem) >= l && elem[0:l] == "com" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'm': // Prefix: "ment"
if l := len("ment"); len(elem) >= l && elem[0:l] == "ment" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = CreateSubmissionAuditCommentOperation
r.summary = "Post a comment to the audit log"
r.operationID = "createSubmissionAuditComment"
r.pathPattern = "/submissions/{SubmissionID}/comment"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'p': // Prefix: "pleted"
if l := len("pleted"); len(elem) >= l && elem[0:l] == "pleted" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = SetSubmissionCompletedOperation
r.summary = "Called by maptest when a player completes the map"
r.operationID = "setSubmissionCompleted"
r.pathPattern = "/submissions/{SubmissionID}/completed"
r.args = args
r.count = 1
return r, true
default:
return
}
}
}
case 'm': // Prefix: "model"
if l := len("model"); len(elem) >= l && elem[0:l] == "model" {
@ -2245,6 +2553,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break
}
switch elem[0] {
case 's': // Prefix: "submitting"
if l := len("submitting"); len(elem) >= l && elem[0:l] == "submitting" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionResetSubmittingOperation
r.summary = "Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction"
r.operationID = "actionSubmissionResetSubmitting"
r.pathPattern = "/submissions/{SubmissionID}/status/reset-submitting"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'u': // Prefix: "uploading"
if l := len("uploading"); len(elem) >= l && elem[0:l] == "uploading" {
@ -2345,30 +2677,6 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
case 's': // Prefix: "submit"
if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionSubmitOperation
r.summary = "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted"
r.operationID = "actionSubmissionSubmit"
r.pathPattern = "/submissions/{SubmissionID}/status/submit"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 't': // Prefix: "trigger-"
if l := len("trigger-"); len(elem) >= l && elem[0:l] == "trigger-" {
@ -2381,6 +2689,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break
}
switch elem[0] {
case 's': // Prefix: "submit"
if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionTriggerSubmitOperation
r.summary = "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting"
r.operationID = "actionSubmissionTriggerSubmit"
r.pathPattern = "/submissions/{SubmissionID}/status/trigger-submit"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'u': // Prefix: "upload"
if l := len("upload"); len(elem) >= l && elem[0:l] == "upload" {

File diff suppressed because it is too large Load Diff

@ -26,6 +26,13 @@ type Handler interface {
//
// POST /mapfixes/{MapfixID}/status/request-changes
ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error
// ActionMapfixResetSubmitting implements actionMapfixResetSubmitting operation.
//
// Role Submitter manually resets submitting softlock and changes status from Submitting ->
// UnderConstruction.
//
// POST /mapfixes/{MapfixID}/status/reset-submitting
ActionMapfixResetSubmitting(ctx context.Context, params ActionMapfixResetSubmittingParams) error
// ActionMapfixRetryValidate implements actionMapfixRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
@ -38,12 +45,12 @@ type Handler interface {
//
// POST /mapfixes/{MapfixID}/status/revoke
ActionMapfixRevoke(ctx context.Context, params ActionMapfixRevokeParams) error
// ActionMapfixSubmit implements actionMapfixSubmit operation.
// ActionMapfixTriggerSubmit implements actionMapfixTriggerSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/submit
ActionMapfixSubmit(ctx context.Context, params ActionMapfixSubmitParams) error
// POST /mapfixes/{MapfixID}/status/trigger-submit
ActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) error
// ActionMapfixTriggerUpload implements actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@ -80,6 +87,13 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/request-changes
ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error
// ActionSubmissionResetSubmitting implements actionSubmissionResetSubmitting operation.
//
// Role Submitter manually resets submitting softlock and changes status from Submitting ->
// UnderConstruction.
//
// POST /submissions/{SubmissionID}/status/reset-submitting
ActionSubmissionResetSubmitting(ctx context.Context, params ActionSubmissionResetSubmittingParams) error
// ActionSubmissionRetryValidate implements actionSubmissionRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
@ -92,12 +106,12 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/revoke
ActionSubmissionRevoke(ctx context.Context, params ActionSubmissionRevokeParams) error
// ActionSubmissionSubmit implements actionSubmissionSubmit operation.
// ActionSubmissionTriggerSubmit implements actionSubmissionTriggerSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/submit
ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error
// POST /submissions/{SubmissionID}/status/trigger-submit
ActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) error
// ActionSubmissionTriggerUpload implements actionSubmissionTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@ -122,6 +136,12 @@ type Handler interface {
//
// POST /mapfixes
CreateMapfix(ctx context.Context, req *MapfixTriggerCreate) (*OperationID, error)
// CreateMapfixAuditComment implements createMapfixAuditComment operation.
//
// Post a comment to the audit log.
//
// POST /mapfixes/{MapfixID}/comment
CreateMapfixAuditComment(ctx context.Context, req CreateMapfixAuditCommentReq, params CreateMapfixAuditCommentParams) error
// CreateScript implements createScript operation.
//
// Create a new script.
@ -140,6 +160,12 @@ type Handler interface {
//
// POST /submissions
CreateSubmission(ctx context.Context, req *SubmissionTriggerCreate) (*OperationID, error)
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
//
// Post a comment to the audit log.
//
// POST /submissions/{SubmissionID}/comment
CreateSubmissionAuditComment(ctx context.Context, req CreateSubmissionAuditCommentReq, params CreateSubmissionAuditCommentParams) error
// DeleteScript implements deleteScript operation.
//
// Delete the specified script by ID.
@ -188,12 +214,18 @@ type Handler interface {
//
// GET /submissions/{SubmissionID}
GetSubmission(ctx context.Context, params GetSubmissionParams) (*Submission, error)
// ListMapfixAuditEvents implements listMapfixAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /mapfixes/{MapfixID}/audit-events
ListMapfixAuditEvents(ctx context.Context, params ListMapfixAuditEventsParams) ([]AuditEvent, error)
// ListMapfixes implements listMapfixes operation.
//
// Get list of mapfixes.
//
// GET /mapfixes
ListMapfixes(ctx context.Context, params ListMapfixesParams) ([]Mapfix, error)
ListMapfixes(ctx context.Context, params ListMapfixesParams) (*Mapfixes, error)
// ListMaps implements listMaps operation.
//
// Get list of maps.
@ -212,12 +244,18 @@ type Handler interface {
//
// GET /scripts
ListScripts(ctx context.Context, params ListScriptsParams) ([]Script, error)
// ListSubmissionAuditEvents implements listSubmissionAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /submissions/{SubmissionID}/audit-events
ListSubmissionAuditEvents(ctx context.Context, params ListSubmissionAuditEventsParams) ([]AuditEvent, error)
// ListSubmissions implements listSubmissions operation.
//
// Get list of submissions.
//
// GET /submissions
ListSubmissions(ctx context.Context, params ListSubmissionsParams) ([]Submission, error)
ListSubmissions(ctx context.Context, params ListSubmissionsParams) (*Submissions, error)
// ReleaseSubmissions implements releaseSubmissions operation.
//
// Release a set of uploaded maps.

@ -40,6 +40,16 @@ func (UnimplementedHandler) ActionMapfixRequestChanges(ctx context.Context, para
return ht.ErrNotImplemented
}
// ActionMapfixResetSubmitting implements actionMapfixResetSubmitting operation.
//
// Role Submitter manually resets submitting softlock and changes status from Submitting ->
// UnderConstruction.
//
// POST /mapfixes/{MapfixID}/status/reset-submitting
func (UnimplementedHandler) ActionMapfixResetSubmitting(ctx context.Context, params ActionMapfixResetSubmittingParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixRetryValidate implements actionMapfixRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
@ -58,12 +68,12 @@ func (UnimplementedHandler) ActionMapfixRevoke(ctx context.Context, params Actio
return ht.ErrNotImplemented
}
// ActionMapfixSubmit implements actionMapfixSubmit operation.
// ActionMapfixTriggerSubmit implements actionMapfixTriggerSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/submit
func (UnimplementedHandler) ActionMapfixSubmit(ctx context.Context, params ActionMapfixSubmitParams) error {
// POST /mapfixes/{MapfixID}/status/trigger-submit
func (UnimplementedHandler) ActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) error {
return ht.ErrNotImplemented
}
@ -121,6 +131,16 @@ func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context,
return ht.ErrNotImplemented
}
// ActionSubmissionResetSubmitting implements actionSubmissionResetSubmitting operation.
//
// Role Submitter manually resets submitting softlock and changes status from Submitting ->
// UnderConstruction.
//
// POST /submissions/{SubmissionID}/status/reset-submitting
func (UnimplementedHandler) ActionSubmissionResetSubmitting(ctx context.Context, params ActionSubmissionResetSubmittingParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionRetryValidate implements actionSubmissionRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
@ -139,12 +159,12 @@ func (UnimplementedHandler) ActionSubmissionRevoke(ctx context.Context, params A
return ht.ErrNotImplemented
}
// ActionSubmissionSubmit implements actionSubmissionSubmit operation.
// ActionSubmissionTriggerSubmit implements actionSubmissionTriggerSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/submit
func (UnimplementedHandler) ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error {
// POST /submissions/{SubmissionID}/status/trigger-submit
func (UnimplementedHandler) ActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) error {
return ht.ErrNotImplemented
}
@ -184,6 +204,15 @@ func (UnimplementedHandler) CreateMapfix(ctx context.Context, req *MapfixTrigger
return r, ht.ErrNotImplemented
}
// CreateMapfixAuditComment implements createMapfixAuditComment operation.
//
// Post a comment to the audit log.
//
// POST /mapfixes/{MapfixID}/comment
func (UnimplementedHandler) CreateMapfixAuditComment(ctx context.Context, req CreateMapfixAuditCommentReq, params CreateMapfixAuditCommentParams) error {
return ht.ErrNotImplemented
}
// CreateScript implements createScript operation.
//
// Create a new script.
@ -211,6 +240,15 @@ func (UnimplementedHandler) CreateSubmission(ctx context.Context, req *Submissio
return r, ht.ErrNotImplemented
}
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
//
// Post a comment to the audit log.
//
// POST /submissions/{SubmissionID}/comment
func (UnimplementedHandler) CreateSubmissionAuditComment(ctx context.Context, req CreateSubmissionAuditCommentReq, params CreateSubmissionAuditCommentParams) error {
return ht.ErrNotImplemented
}
// DeleteScript implements deleteScript operation.
//
// Delete the specified script by ID.
@ -283,12 +321,21 @@ func (UnimplementedHandler) GetSubmission(ctx context.Context, params GetSubmiss
return r, ht.ErrNotImplemented
}
// ListMapfixAuditEvents implements listMapfixAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /mapfixes/{MapfixID}/audit-events
func (UnimplementedHandler) ListMapfixAuditEvents(ctx context.Context, params ListMapfixAuditEventsParams) (r []AuditEvent, _ error) {
return r, ht.ErrNotImplemented
}
// ListMapfixes implements listMapfixes operation.
//
// Get list of mapfixes.
//
// GET /mapfixes
func (UnimplementedHandler) ListMapfixes(ctx context.Context, params ListMapfixesParams) (r []Mapfix, _ error) {
func (UnimplementedHandler) ListMapfixes(ctx context.Context, params ListMapfixesParams) (r *Mapfixes, _ error) {
return r, ht.ErrNotImplemented
}
@ -319,12 +366,21 @@ func (UnimplementedHandler) ListScripts(ctx context.Context, params ListScriptsP
return r, ht.ErrNotImplemented
}
// ListSubmissionAuditEvents implements listSubmissionAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /submissions/{SubmissionID}/audit-events
func (UnimplementedHandler) ListSubmissionAuditEvents(ctx context.Context, params ListSubmissionAuditEventsParams) (r []AuditEvent, _ error) {
return r, ht.ErrNotImplemented
}
// ListSubmissions implements listSubmissions operation.
//
// Get list of submissions.
//
// GET /submissions
func (UnimplementedHandler) ListSubmissions(ctx context.Context, params ListSubmissionsParams) (r []Submission, _ error) {
func (UnimplementedHandler) ListSubmissions(ctx context.Context, params ListSubmissionsParams) (r *Submissions, _ error) {
return r, ht.ErrNotImplemented
}

File diff suppressed because it is too large Load Diff

@ -3,6 +3,8 @@ package datastore
import (
"context"
"errors"
"time"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
@ -22,6 +24,7 @@ const (
)
type Datastore interface {
AuditEvents() AuditEvents
Mapfixes() Mapfixes
Operations() Operations
Submissions() Submissions
@ -29,6 +32,14 @@ type Datastore interface {
ScriptPolicy() ScriptPolicy
}
type AuditEvents interface {
Get(ctx context.Context, id int64) (model.AuditEvent, error)
Create(ctx context.Context, smap model.AuditEvent) (model.AuditEvent, error)
Update(ctx context.Context, id int64, values OptionalMap) error
Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page) ([]model.AuditEvent, error)
}
type Mapfixes interface {
Get(ctx context.Context, id int64) (model.Mapfix, error)
GetList(ctx context.Context, id []int64) ([]model.Mapfix, error)
@ -38,6 +49,7 @@ type Mapfixes interface {
IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.MapfixStatus, values OptionalMap) (model.Mapfix, error)
Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) ([]model.Mapfix, error)
ListWithTotal(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) (uint64, []model.Mapfix, error)
}
type Operations interface {
@ -45,6 +57,7 @@ type Operations interface {
Create(ctx context.Context, smap model.Operation) (model.Operation, error)
Update(ctx context.Context, id int32, values OptionalMap) error
Delete(ctx context.Context, id int32) error
CountSince(ctx context.Context, owner int64, since time.Time) (int64, error)
}
type Submissions interface {
@ -56,6 +69,7 @@ type Submissions interface {
IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.SubmissionStatus, values OptionalMap) (model.Submission, error)
Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) ([]model.Submission, error)
ListWithTotal(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) (int64, []model.Submission, error)
}
type Scripts interface {

@ -10,7 +10,17 @@ func Optional() OptionalMap {
return OptionalMap{filter: map[string]interface{}{}}
}
func (q OptionalMap) Add(column string, value interface{}) OptionalMap {
func (q OptionalMap) Add2(column string, value interface{}) OptionalMap {
q.filter[column] = value
return q
}
func (q OptionalMap) AddPostgresInt32(column string, value uint32) OptionalMap {
q.filter[column] = value
return q
}
func (q OptionalMap) AddPostgresInt64(column string, value uint64) OptionalMap {
q.filter[column] = value
return q
}

@ -0,0 +1,64 @@
package gormstore
import (
"context"
"errors"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"gorm.io/gorm"
)
type AuditEvents struct {
db *gorm.DB
}
func (env *AuditEvents) Get(ctx context.Context, id int64) (model.AuditEvent, error) {
var mdl model.AuditEvent
if err := env.db.First(&mdl, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return mdl, datastore.ErrNotExist
}
return mdl, err
}
return mdl, nil
}
func (env *AuditEvents) Create(ctx context.Context, smap model.AuditEvent) (model.AuditEvent, error) {
if err := env.db.Create(&smap).Error; err != nil {
return smap, err
}
return smap, nil
}
func (env *AuditEvents) Update(ctx context.Context, id int64, values datastore.OptionalMap) error {
if err := env.db.Model(&model.AuditEvent{}).Where("id = ?", id).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *AuditEvents) Delete(ctx context.Context, id int64) error {
if err := env.db.Delete(&model.AuditEvent{}, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *AuditEvents) List(ctx context.Context, filters datastore.OptionalMap, page model.Page) ([]model.AuditEvent, error) {
var events []model.AuditEvent
if err := env.db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&events).Error; err != nil {
return nil, err
}
return events, nil
}

@ -31,6 +31,7 @@ func New(ctx *cli.Context) (datastore.Datastore, error) {
if ctx.Bool("migrate") {
if err := db.AutoMigrate(
&model.AuditEvent{},
&model.Mapfix{},
&model.Operation{},
&model.Submission{},

@ -9,6 +9,10 @@ type Gormstore struct {
db *gorm.DB
}
func (g Gormstore) AuditEvents() datastore.AuditEvents {
return &AuditEvents{db: g.db}
}
func (g Gormstore) Mapfixes() datastore.Mapfixes {
return &Mapfixes{db: g.db}
}

@ -130,3 +130,19 @@ func (env *Mapfixes) List(ctx context.Context, filters datastore.OptionalMap, pa
return maps, nil
}
func (env *Mapfixes) ListWithTotal(ctx context.Context, filters datastore.OptionalMap, page model.Page, sort datastore.ListSort) (uint64, []model.Mapfix, error) {
// grab page items
maps, err := env.List(ctx, filters, page, sort)
if err != nil{
return 0, nil, err
}
// count total with filters
var total int64
if err := env.db.Model(&model.Mapfix{}).Where(filters.Map()).Count(&total).Error; err != nil {
return 0, nil, err
}
return uint64(total), maps, nil
}

@ -3,6 +3,7 @@ package gormstore
import (
"context"
"errors"
"time"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
@ -53,3 +54,12 @@ func (env *Operations) Delete(ctx context.Context, id int32) error {
return nil
}
func (env *Operations) CountSince(ctx context.Context, owner int64, since time.Time) (int64, error) {
var count int64
if err := env.db.Model(&model.Operation{}).Where("owner = ? AND created_at > ?",owner,since).Count(&count).Error; err != nil {
return count, err
}
return count, nil
}

@ -130,3 +130,19 @@ func (env *Submissions) List(ctx context.Context, filters datastore.OptionalMap,
return maps, nil
}
func (env *Submissions) ListWithTotal(ctx context.Context, filters datastore.OptionalMap, page model.Page, sort datastore.ListSort) (int64, []model.Submission, error) {
// grab page items
maps, err := env.List(ctx, filters, page, sort)
if err != nil{
return 0, nil, err
}
// count total with filters
var total int64
if err := env.db.Model(&model.Submission{}).Where(filters.Map()).Count(&total).Error; err != nil {
return 0, nil, err
}
return total, maps, nil
}

@ -34,6 +34,12 @@ type Invoker interface {
//
// POST /mapfixes/{MapfixID}/status/validator-failed
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
ActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) error
// ActionMapfixUploaded invokes actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@ -58,6 +64,12 @@ type Invoker interface {
//
// POST /submissions/{SubmissionID}/status/validator-failed
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
ActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) error
// ActionSubmissionUploaded invokes actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@ -229,7 +241,7 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@ -282,6 +294,97 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf
return result, nil
}
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
func (c *Client) ActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) error {
_, err := c.sendActionMapfixSubmitted(ctx, params)
return err
}
func (c *Client) sendActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) (res *ActionMapfixSubmittedNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixSubmitted"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-submitted"),
}
// Run stopwatch.
startTime := time.Now()
defer func() {
// Use floating point division here for higher precision (instead of Millisecond method).
elapsedDuration := time.Since(startTime)
c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...))
}()
// Increment request counter.
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, ActionMapfixSubmittedOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/mapfixes/"
{
// Encode "MapfixID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "MapfixID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/status/validator-submitted"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeActionMapfixSubmittedResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionMapfixUploaded invokes actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@ -338,7 +441,7 @@ func (c *Client) sendActionMapfixUploaded(ctx context.Context, params ActionMapf
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@ -429,7 +532,7 @@ func (c *Client) sendActionMapfixValidated(ctx context.Context, params ActionMap
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@ -520,7 +623,7 @@ func (c *Client) sendActionOperationFailed(ctx context.Context, params ActionOpe
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int32ToString(params.OperationID))
return e.EncodeValue(conv.Uint32ToString(params.OperationID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@ -629,7 +732,7 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@ -682,6 +785,97 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
return result, nil
}
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
func (c *Client) ActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) error {
_, err := c.sendActionSubmissionSubmitted(ctx, params)
return err
}
func (c *Client) sendActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) (res *ActionSubmissionSubmittedNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionSubmissionSubmitted"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validator-submitted"),
}
// Run stopwatch.
startTime := time.Now()
defer func() {
// Use floating point division here for higher precision (instead of Millisecond method).
elapsedDuration := time.Since(startTime)
c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...))
}()
// Increment request counter.
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, ActionSubmissionSubmittedOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/submissions/"
{
// Encode "SubmissionID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "SubmissionID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/status/validator-submitted"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeActionSubmissionSubmittedResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionSubmissionUploaded invokes actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@ -738,7 +932,7 @@ func (c *Client) sendActionSubmissionUploaded(ctx context.Context, params Action
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@ -762,7 +956,7 @@ func (c *Client) sendActionSubmissionUploaded(ctx context.Context, params Action
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int64ToString(params.UploadedAssetID))
return e.EncodeValue(conv.Uint64ToString(params.UploadedAssetID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@ -847,7 +1041,7 @@ func (c *Client) sendActionSubmissionValidated(ctx context.Context, params Actio
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@ -1238,7 +1432,7 @@ func (c *Client) sendGetScript(ctx context.Context, params GetScriptParams) (res
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.ScriptID))
return e.EncodeValue(conv.Uint64ToString(params.ScriptID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@ -1333,7 +1527,7 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int32ToString(params.Page))
return e.EncodeValue(conv.Uint32ToString(params.Page))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@ -1347,7 +1541,7 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int32ToString(params.Limit))
return e.EncodeValue(conv.Uint32ToString(params.Limit))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@ -1379,7 +1573,7 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.ToScriptID.Get(); ok {
return e.EncodeValue(conv.Int64ToString(val))
return e.EncodeValue(conv.Uint64ToString(val))
}
return nil
}); err != nil {
@ -1396,7 +1590,7 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.Policy.Get(); ok {
return e.EncodeValue(conv.Int32ToString(val))
return e.EncodeValue(conv.Uint32ToString(val))
}
return nil
}); err != nil {
@ -1488,7 +1682,7 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int32ToString(params.Page))
return e.EncodeValue(conv.Uint32ToString(params.Page))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@ -1502,7 +1696,7 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int32ToString(params.Limit))
return e.EncodeValue(conv.Uint32ToString(params.Limit))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@ -1568,7 +1762,7 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.ResourceType.Get(); ok {
return e.EncodeValue(conv.Int32ToString(val))
return e.EncodeValue(conv.Uint32ToString(val))
}
return nil
}); err != nil {
@ -1585,7 +1779,7 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.ResourceID.Get(); ok {
return e.EncodeValue(conv.Int64ToString(val))
return e.EncodeValue(conv.Uint64ToString(val))
}
return nil
}); err != nil {
@ -1672,7 +1866,7 @@ func (c *Client) sendUpdateMapfixValidatedModel(ctx context.Context, params Upda
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@ -1696,7 +1890,7 @@ func (c *Client) sendUpdateMapfixValidatedModel(ctx context.Context, params Upda
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelID))
return e.EncodeValue(conv.Uint64ToString(params.ValidatedModelID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@ -1710,7 +1904,7 @@ func (c *Client) sendUpdateMapfixValidatedModel(ctx context.Context, params Upda
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelVersion))
return e.EncodeValue(conv.Uint64ToString(params.ValidatedModelVersion))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@ -1795,7 +1989,7 @@ func (c *Client) sendUpdateSubmissionValidatedModel(ctx context.Context, params
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@ -1819,7 +2013,7 @@ func (c *Client) sendUpdateSubmissionValidatedModel(ctx context.Context, params
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelID))
return e.EncodeValue(conv.Uint64ToString(params.ValidatedModelID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@ -1833,7 +2027,7 @@ func (c *Client) sendUpdateSubmissionValidatedModel(ctx context.Context, params
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelVersion))
return e.EncodeValue(conv.Uint64ToString(params.ValidatedModelVersion))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}

@ -183,6 +183,155 @@ func (s *Server) handleActionMapfixAcceptedRequest(args [1]string, argsEscaped b
}
}
// handleActionMapfixSubmittedRequest handles actionMapfixSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
func (s *Server) handleActionMapfixSubmittedRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixSubmitted"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-submitted"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixSubmittedOperation,
trace.WithAttributes(otelAttrs...),
serverSpanKind,
)
defer span.End()
// Add Labeler to context.
labeler := &Labeler{attrs: otelAttrs}
ctx = contextWithLabeler(ctx, labeler)
// Run stopwatch.
startTime := time.Now()
defer func() {
elapsedDuration := time.Since(startTime)
attrSet := labeler.AttributeSet()
attrs := attrSet.ToSlice()
code := statusWriter.status
if code != 0 {
codeAttr := semconv.HTTPResponseStatusCode(code)
attrs = append(attrs, codeAttr)
span.SetAttributes(codeAttr)
}
attrOpt := metric.WithAttributes(attrs...)
// Increment request counter.
s.requests.Add(ctx, 1, attrOpt)
// Use floating point division here for higher precision (instead of Millisecond method).
s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt)
}()
var (
recordError = func(stage string, err error) {
span.RecordError(err)
// https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status
// Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges,
// unless there was another error (e.g., network error receiving the response body; or 3xx codes with
// max redirects exceeded), in which case status MUST be set to Error.
code := statusWriter.status
if code >= 100 && code < 500 {
span.SetStatus(codes.Error, stage)
}
attrSet := labeler.AttributeSet()
attrs := attrSet.ToSlice()
if code != 0 {
attrs = append(attrs, semconv.HTTPResponseStatusCode(code))
}
s.errors.Add(ctx, 1, metric.WithAttributes(attrs...))
}
err error
opErrContext = ogenerrors.OperationContext{
Name: ActionMapfixSubmittedOperation,
ID: "actionMapfixSubmitted",
}
)
params, err := decodeActionMapfixSubmittedParams(args, argsEscaped, r)
if err != nil {
err = &ogenerrors.DecodeParamsError{
OperationContext: opErrContext,
Err: err,
}
defer recordError("DecodeParams", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
var response *ActionMapfixSubmittedNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: ActionMapfixSubmittedOperation,
OperationSummary: "(Internal endpoint) Role Validator changes status from Submitting -> Submitted",
OperationID: "actionMapfixSubmitted",
Body: nil,
Params: middleware.Parameters{
{
Name: "MapfixID",
In: "path",
}: params.MapfixID,
},
Raw: r,
}
type (
Request = struct{}
Params = ActionMapfixSubmittedParams
Response = *ActionMapfixSubmittedNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackActionMapfixSubmittedParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.ActionMapfixSubmitted(ctx, params)
return response, err
},
)
} else {
err = s.h.ActionMapfixSubmitted(ctx, params)
}
if err != nil {
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {
if err := encodeErrorResponse(errRes, w, span); err != nil {
defer recordError("Internal", err)
}
return
}
if errors.Is(err, ht.ErrNotImplemented) {
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil {
defer recordError("Internal", err)
}
return
}
if err := encodeActionMapfixSubmittedResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleActionMapfixUploadedRequest handles actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@ -787,6 +936,155 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
}
}
// handleActionSubmissionSubmittedRequest handles actionSubmissionSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
func (s *Server) handleActionSubmissionSubmittedRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionSubmissionSubmitted"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validator-submitted"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionSubmittedOperation,
trace.WithAttributes(otelAttrs...),
serverSpanKind,
)
defer span.End()
// Add Labeler to context.
labeler := &Labeler{attrs: otelAttrs}
ctx = contextWithLabeler(ctx, labeler)
// Run stopwatch.
startTime := time.Now()
defer func() {
elapsedDuration := time.Since(startTime)
attrSet := labeler.AttributeSet()
attrs := attrSet.ToSlice()
code := statusWriter.status
if code != 0 {
codeAttr := semconv.HTTPResponseStatusCode(code)
attrs = append(attrs, codeAttr)
span.SetAttributes(codeAttr)
}
attrOpt := metric.WithAttributes(attrs...)
// Increment request counter.
s.requests.Add(ctx, 1, attrOpt)
// Use floating point division here for higher precision (instead of Millisecond method).
s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt)
}()
var (
recordError = func(stage string, err error) {
span.RecordError(err)
// https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status
// Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges,
// unless there was another error (e.g., network error receiving the response body; or 3xx codes with
// max redirects exceeded), in which case status MUST be set to Error.
code := statusWriter.status
if code >= 100 && code < 500 {
span.SetStatus(codes.Error, stage)
}
attrSet := labeler.AttributeSet()
attrs := attrSet.ToSlice()
if code != 0 {
attrs = append(attrs, semconv.HTTPResponseStatusCode(code))
}
s.errors.Add(ctx, 1, metric.WithAttributes(attrs...))
}
err error
opErrContext = ogenerrors.OperationContext{
Name: ActionSubmissionSubmittedOperation,
ID: "actionSubmissionSubmitted",
}
)
params, err := decodeActionSubmissionSubmittedParams(args, argsEscaped, r)
if err != nil {
err = &ogenerrors.DecodeParamsError{
OperationContext: opErrContext,
Err: err,
}
defer recordError("DecodeParams", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
var response *ActionSubmissionSubmittedNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: ActionSubmissionSubmittedOperation,
OperationSummary: "(Internal endpoint) Role Validator changes status from Submitting -> Submitted",
OperationID: "actionSubmissionSubmitted",
Body: nil,
Params: middleware.Parameters{
{
Name: "SubmissionID",
In: "path",
}: params.SubmissionID,
},
Raw: r,
}
type (
Request = struct{}
Params = ActionSubmissionSubmittedParams
Response = *ActionSubmissionSubmittedNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackActionSubmissionSubmittedParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.ActionSubmissionSubmitted(ctx, params)
return response, err
},
)
} else {
err = s.h.ActionSubmissionSubmitted(ctx, params)
}
if err != nil {
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {
if err := encodeErrorResponse(errRes, w, span); err != nil {
defer recordError("Internal", err)
}
return
}
if errors.Is(err, ht.ErrNotImplemented) {
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil {
defer recordError("Internal", err)
}
return
}
if err := encodeActionSubmissionSubmittedResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleActionSubmissionUploadedRequest handles actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.

@ -23,7 +23,7 @@ func (s *Error) Encode(e *jx.Encoder) {
func (s *Error) encodeFields(e *jx.Encoder) {
{
e.FieldStart("code")
e.Int64(s.Code)
e.UInt64(s.Code)
}
{
e.FieldStart("message")
@ -48,8 +48,8 @@ func (s *Error) Decode(d *jx.Decoder) error {
case "code":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.Code = int64(v)
v, err := d.UInt64()
s.Code = uint64(v)
if err != nil {
return err
}
@ -136,11 +136,11 @@ func (s *MapfixCreate) Encode(e *jx.Encoder) {
func (s *MapfixCreate) encodeFields(e *jx.Encoder) {
{
e.FieldStart("OperationID")
e.Int32(s.OperationID)
e.UInt32(s.OperationID)
}
{
e.FieldStart("AssetOwner")
e.Int64(s.AssetOwner)
e.UInt64(s.AssetOwner)
}
{
e.FieldStart("DisplayName")
@ -152,19 +152,19 @@ func (s *MapfixCreate) encodeFields(e *jx.Encoder) {
}
{
e.FieldStart("GameID")
e.Int32(s.GameID)
e.UInt32(s.GameID)
}
{
e.FieldStart("AssetID")
e.Int64(s.AssetID)
e.UInt64(s.AssetID)
}
{
e.FieldStart("AssetVersion")
e.Int64(s.AssetVersion)
e.UInt64(s.AssetVersion)
}
{
e.FieldStart("TargetAssetID")
e.Int64(s.TargetAssetID)
e.UInt64(s.TargetAssetID)
}
}
@ -191,8 +191,8 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
case "OperationID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int32()
s.OperationID = int32(v)
v, err := d.UInt32()
s.OperationID = uint32(v)
if err != nil {
return err
}
@ -203,8 +203,8 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
case "AssetOwner":
requiredBitSet[0] |= 1 << 1
if err := func() error {
v, err := d.Int64()
s.AssetOwner = int64(v)
v, err := d.UInt64()
s.AssetOwner = uint64(v)
if err != nil {
return err
}
@ -239,8 +239,8 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
case "GameID":
requiredBitSet[0] |= 1 << 4
if err := func() error {
v, err := d.Int32()
s.GameID = int32(v)
v, err := d.UInt32()
s.GameID = uint32(v)
if err != nil {
return err
}
@ -251,8 +251,8 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
case "AssetID":
requiredBitSet[0] |= 1 << 5
if err := func() error {
v, err := d.Int64()
s.AssetID = int64(v)
v, err := d.UInt64()
s.AssetID = uint64(v)
if err != nil {
return err
}
@ -263,8 +263,8 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
case "AssetVersion":
requiredBitSet[0] |= 1 << 6
if err := func() error {
v, err := d.Int64()
s.AssetVersion = int64(v)
v, err := d.UInt64()
s.AssetVersion = uint64(v)
if err != nil {
return err
}
@ -275,8 +275,8 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
case "TargetAssetID":
requiredBitSet[0] |= 1 << 7
if err := func() error {
v, err := d.Int64()
s.TargetAssetID = int64(v)
v, err := d.UInt64()
s.TargetAssetID = uint64(v)
if err != nil {
return err
}
@ -351,7 +351,7 @@ func (s *MapfixID) Encode(e *jx.Encoder) {
func (s *MapfixID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("MapfixID")
e.Int64(s.MapfixID)
e.UInt64(s.MapfixID)
}
}
@ -371,8 +371,8 @@ func (s *MapfixID) Decode(d *jx.Decoder) error {
case "MapfixID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.MapfixID = int64(v)
v, err := d.UInt64()
s.MapfixID = uint64(v)
if err != nil {
return err
}
@ -436,37 +436,37 @@ func (s *MapfixID) UnmarshalJSON(data []byte) error {
return s.Decode(d)
}
// Encode encodes int64 as json.
func (o OptInt64) Encode(e *jx.Encoder) {
// Encode encodes uint64 as json.
func (o OptUint64) Encode(e *jx.Encoder) {
if !o.Set {
return
}
e.Int64(int64(o.Value))
e.UInt64(uint64(o.Value))
}
// Decode decodes int64 from json.
func (o *OptInt64) Decode(d *jx.Decoder) error {
// Decode decodes uint64 from json.
func (o *OptUint64) Decode(d *jx.Decoder) error {
if o == nil {
return errors.New("invalid: unable to decode OptInt64 to nil")
return errors.New("invalid: unable to decode OptUint64 to nil")
}
o.Set = true
v, err := d.Int64()
v, err := d.UInt64()
if err != nil {
return err
}
o.Value = int64(v)
o.Value = uint64(v)
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s OptInt64) MarshalJSON() ([]byte, error) {
func (s OptUint64) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *OptInt64) UnmarshalJSON(data []byte) error {
func (s *OptUint64) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
@ -482,7 +482,7 @@ func (s *Script) Encode(e *jx.Encoder) {
func (s *Script) encodeFields(e *jx.Encoder) {
{
e.FieldStart("ID")
e.Int64(s.ID)
e.UInt64(s.ID)
}
{
e.FieldStart("Name")
@ -498,11 +498,11 @@ func (s *Script) encodeFields(e *jx.Encoder) {
}
{
e.FieldStart("ResourceType")
e.Int32(s.ResourceType)
e.UInt32(s.ResourceType)
}
{
e.FieldStart("ResourceID")
e.Int64(s.ResourceID)
e.UInt64(s.ResourceID)
}
}
@ -527,8 +527,8 @@ func (s *Script) Decode(d *jx.Decoder) error {
case "ID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.ID = int64(v)
v, err := d.UInt64()
s.ID = uint64(v)
if err != nil {
return err
}
@ -575,8 +575,8 @@ func (s *Script) Decode(d *jx.Decoder) error {
case "ResourceType":
requiredBitSet[0] |= 1 << 4
if err := func() error {
v, err := d.Int32()
s.ResourceType = int32(v)
v, err := d.UInt32()
s.ResourceType = uint32(v)
if err != nil {
return err
}
@ -587,8 +587,8 @@ func (s *Script) Decode(d *jx.Decoder) error {
case "ResourceID":
requiredBitSet[0] |= 1 << 5
if err := func() error {
v, err := d.Int64()
s.ResourceID = int64(v)
v, err := d.UInt64()
s.ResourceID = uint64(v)
if err != nil {
return err
}
@ -671,7 +671,7 @@ func (s *ScriptCreate) encodeFields(e *jx.Encoder) {
}
{
e.FieldStart("ResourceType")
e.Int32(s.ResourceType)
e.UInt32(s.ResourceType)
}
{
if s.ResourceID.Set {
@ -724,8 +724,8 @@ func (s *ScriptCreate) Decode(d *jx.Decoder) error {
case "ResourceType":
requiredBitSet[0] |= 1 << 2
if err := func() error {
v, err := d.Int32()
s.ResourceType = int32(v)
v, err := d.UInt32()
s.ResourceType = uint32(v)
if err != nil {
return err
}
@ -810,7 +810,7 @@ func (s *ScriptID) Encode(e *jx.Encoder) {
func (s *ScriptID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("ScriptID")
e.Int64(s.ScriptID)
e.UInt64(s.ScriptID)
}
}
@ -830,8 +830,8 @@ func (s *ScriptID) Decode(d *jx.Decoder) error {
case "ScriptID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.ScriptID = int64(v)
v, err := d.UInt64()
s.ScriptID = uint64(v)
if err != nil {
return err
}
@ -906,7 +906,7 @@ func (s *ScriptPolicy) Encode(e *jx.Encoder) {
func (s *ScriptPolicy) encodeFields(e *jx.Encoder) {
{
e.FieldStart("ID")
e.Int64(s.ID)
e.UInt64(s.ID)
}
{
e.FieldStart("FromScriptHash")
@ -914,11 +914,11 @@ func (s *ScriptPolicy) encodeFields(e *jx.Encoder) {
}
{
e.FieldStart("ToScriptID")
e.Int64(s.ToScriptID)
e.UInt64(s.ToScriptID)
}
{
e.FieldStart("Policy")
e.Int32(s.Policy)
e.UInt32(s.Policy)
}
}
@ -941,8 +941,8 @@ func (s *ScriptPolicy) Decode(d *jx.Decoder) error {
case "ID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.ID = int64(v)
v, err := d.UInt64()
s.ID = uint64(v)
if err != nil {
return err
}
@ -965,8 +965,8 @@ func (s *ScriptPolicy) Decode(d *jx.Decoder) error {
case "ToScriptID":
requiredBitSet[0] |= 1 << 2
if err := func() error {
v, err := d.Int64()
s.ToScriptID = int64(v)
v, err := d.UInt64()
s.ToScriptID = uint64(v)
if err != nil {
return err
}
@ -977,8 +977,8 @@ func (s *ScriptPolicy) Decode(d *jx.Decoder) error {
case "Policy":
requiredBitSet[0] |= 1 << 3
if err := func() error {
v, err := d.Int32()
s.Policy = int32(v)
v, err := d.UInt32()
s.Policy = uint32(v)
if err != nil {
return err
}
@ -1053,15 +1053,15 @@ func (s *ScriptPolicyCreate) Encode(e *jx.Encoder) {
func (s *ScriptPolicyCreate) encodeFields(e *jx.Encoder) {
{
e.FieldStart("FromScriptID")
e.Int64(s.FromScriptID)
e.UInt64(s.FromScriptID)
}
{
e.FieldStart("ToScriptID")
e.Int64(s.ToScriptID)
e.UInt64(s.ToScriptID)
}
{
e.FieldStart("Policy")
e.Int32(s.Policy)
e.UInt32(s.Policy)
}
}
@ -1083,8 +1083,8 @@ func (s *ScriptPolicyCreate) Decode(d *jx.Decoder) error {
case "FromScriptID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.FromScriptID = int64(v)
v, err := d.UInt64()
s.FromScriptID = uint64(v)
if err != nil {
return err
}
@ -1095,8 +1095,8 @@ func (s *ScriptPolicyCreate) Decode(d *jx.Decoder) error {
case "ToScriptID":
requiredBitSet[0] |= 1 << 1
if err := func() error {
v, err := d.Int64()
s.ToScriptID = int64(v)
v, err := d.UInt64()
s.ToScriptID = uint64(v)
if err != nil {
return err
}
@ -1107,8 +1107,8 @@ func (s *ScriptPolicyCreate) Decode(d *jx.Decoder) error {
case "Policy":
requiredBitSet[0] |= 1 << 2
if err := func() error {
v, err := d.Int32()
s.Policy = int32(v)
v, err := d.UInt32()
s.Policy = uint32(v)
if err != nil {
return err
}
@ -1183,7 +1183,7 @@ func (s *ScriptPolicyID) Encode(e *jx.Encoder) {
func (s *ScriptPolicyID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("ScriptPolicyID")
e.Int64(s.ScriptPolicyID)
e.UInt64(s.ScriptPolicyID)
}
}
@ -1203,8 +1203,8 @@ func (s *ScriptPolicyID) Decode(d *jx.Decoder) error {
case "ScriptPolicyID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.ScriptPolicyID = int64(v)
v, err := d.UInt64()
s.ScriptPolicyID = uint64(v)
if err != nil {
return err
}
@ -1279,11 +1279,11 @@ func (s *SubmissionCreate) Encode(e *jx.Encoder) {
func (s *SubmissionCreate) encodeFields(e *jx.Encoder) {
{
e.FieldStart("OperationID")
e.Int32(s.OperationID)
e.UInt32(s.OperationID)
}
{
e.FieldStart("AssetOwner")
e.Int64(s.AssetOwner)
e.UInt64(s.AssetOwner)
}
{
e.FieldStart("DisplayName")
@ -1295,15 +1295,15 @@ func (s *SubmissionCreate) encodeFields(e *jx.Encoder) {
}
{
e.FieldStart("GameID")
e.Int32(s.GameID)
e.UInt32(s.GameID)
}
{
e.FieldStart("AssetID")
e.Int64(s.AssetID)
e.UInt64(s.AssetID)
}
{
e.FieldStart("AssetVersion")
e.Int64(s.AssetVersion)
e.UInt64(s.AssetVersion)
}
}
@ -1329,8 +1329,8 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
case "OperationID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int32()
s.OperationID = int32(v)
v, err := d.UInt32()
s.OperationID = uint32(v)
if err != nil {
return err
}
@ -1341,8 +1341,8 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
case "AssetOwner":
requiredBitSet[0] |= 1 << 1
if err := func() error {
v, err := d.Int64()
s.AssetOwner = int64(v)
v, err := d.UInt64()
s.AssetOwner = uint64(v)
if err != nil {
return err
}
@ -1377,8 +1377,8 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
case "GameID":
requiredBitSet[0] |= 1 << 4
if err := func() error {
v, err := d.Int32()
s.GameID = int32(v)
v, err := d.UInt32()
s.GameID = uint32(v)
if err != nil {
return err
}
@ -1389,8 +1389,8 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
case "AssetID":
requiredBitSet[0] |= 1 << 5
if err := func() error {
v, err := d.Int64()
s.AssetID = int64(v)
v, err := d.UInt64()
s.AssetID = uint64(v)
if err != nil {
return err
}
@ -1401,8 +1401,8 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
case "AssetVersion":
requiredBitSet[0] |= 1 << 6
if err := func() error {
v, err := d.Int64()
s.AssetVersion = int64(v)
v, err := d.UInt64()
s.AssetVersion = uint64(v)
if err != nil {
return err
}
@ -1477,7 +1477,7 @@ func (s *SubmissionID) Encode(e *jx.Encoder) {
func (s *SubmissionID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("SubmissionID")
e.Int64(s.SubmissionID)
e.UInt64(s.SubmissionID)
}
}
@ -1497,8 +1497,8 @@ func (s *SubmissionID) Decode(d *jx.Decoder) error {
case "SubmissionID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.SubmissionID = int64(v)
v, err := d.UInt64()
s.SubmissionID = uint64(v)
if err != nil {
return err
}

@ -7,10 +7,12 @@ type OperationName = string
const (
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
ActionMapfixSubmittedOperation OperationName = "ActionMapfixSubmitted"
ActionMapfixUploadedOperation OperationName = "ActionMapfixUploaded"
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
ActionOperationFailedOperation OperationName = "ActionOperationFailed"
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionSubmittedOperation OperationName = "ActionSubmissionSubmitted"
ActionSubmissionUploadedOperation OperationName = "ActionSubmissionUploaded"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateMapfixOperation OperationName = "CreateMapfix"

File diff suppressed because it is too large Load Diff

@ -214,6 +214,14 @@ func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
}
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)

@ -52,6 +52,75 @@ func decodeActionMapfixAcceptedResponse(resp *http.Response) (res *ActionMapfixA
}
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 decodeActionMapfixSubmittedResponse(resp *http.Response) (res *ActionMapfixSubmittedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionMapfixSubmittedNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
// Validate response.
if err := func() error {
if err := response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
@ -103,6 +172,15 @@ func decodeActionMapfixUploadedResponse(resp *http.Response) (res *ActionMapfixU
}
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,
@ -154,6 +232,15 @@ func decodeActionMapfixValidatedResponse(resp *http.Response) (res *ActionMapfix
}
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,
@ -205,6 +292,15 @@ func decodeActionOperationFailedResponse(resp *http.Response) (res *ActionOperat
}
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,
@ -256,6 +352,75 @@ func decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSub
}
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 decodeActionSubmissionSubmittedResponse(resp *http.Response) (res *ActionSubmissionSubmittedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionSubmittedNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
// Validate response.
if err := func() error {
if err := response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
@ -307,6 +472,15 @@ func decodeActionSubmissionUploadedResponse(resp *http.Response) (res *ActionSub
}
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,
@ -358,6 +532,15 @@ func decodeActionSubmissionValidatedResponse(resp *http.Response) (res *ActionSu
}
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,
@ -405,6 +588,15 @@ func decodeCreateMapfixResponse(resp *http.Response) (res *MapfixID, _ error) {
}
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 &response, nil
default:
return res, validate.InvalidContentType(ct)
@ -441,6 +633,15 @@ func decodeCreateMapfixResponse(resp *http.Response) (res *MapfixID, _ error) {
}
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,
@ -488,6 +689,15 @@ func decodeCreateScriptResponse(resp *http.Response) (res *ScriptID, _ error) {
}
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 &response, nil
default:
return res, validate.InvalidContentType(ct)
@ -524,6 +734,15 @@ func decodeCreateScriptResponse(resp *http.Response) (res *ScriptID, _ error) {
}
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,
@ -571,6 +790,15 @@ func decodeCreateScriptPolicyResponse(resp *http.Response) (res *ScriptPolicyID,
}
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 &response, nil
default:
return res, validate.InvalidContentType(ct)
@ -607,6 +835,15 @@ func decodeCreateScriptPolicyResponse(resp *http.Response) (res *ScriptPolicyID,
}
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,
@ -654,6 +891,15 @@ func decodeCreateSubmissionResponse(resp *http.Response) (res *SubmissionID, _ e
}
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 &response, nil
default:
return res, validate.InvalidContentType(ct)
@ -690,6 +936,15 @@ func decodeCreateSubmissionResponse(resp *http.Response) (res *SubmissionID, _ e
}
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,
@ -782,6 +1037,15 @@ func decodeGetScriptResponse(resp *http.Response) (res *Script, _ error) {
}
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,
@ -899,6 +1163,15 @@ func decodeListScriptPolicyResponse(resp *http.Response) (res []ScriptPolicy, _
}
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,
@ -1016,6 +1289,15 @@ func decodeListScriptsResponse(resp *http.Response) (res []Script, _ error) {
}
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,
@ -1067,6 +1349,15 @@ func decodeUpdateMapfixValidatedModelResponse(resp *http.Response) (res *UpdateM
}
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,
@ -1118,6 +1409,15 @@ func decodeUpdateSubmissionValidatedModelResponse(resp *http.Response) (res *Upd
}
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,

@ -20,6 +20,13 @@ func encodeActionMapfixAcceptedResponse(response *ActionMapfixAcceptedNoContent,
return nil
}
func encodeActionMapfixSubmittedResponse(response *ActionMapfixSubmittedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixUploadedResponse(response *ActionMapfixUploadedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -48,6 +55,13 @@ func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNo
return nil
}
func encodeActionSubmissionSubmittedResponse(response *ActionSubmissionSubmittedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionUploadedResponse(response *ActionSubmissionUploadedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))

@ -147,6 +147,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
case 's': // Prefix: "submitted"
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionMapfixSubmittedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'u': // Prefix: "uploaded"
if l := len("uploaded"); len(elem) >= l && elem[0:l] == "uploaded" {
@ -454,6 +476,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
case 's': // Prefix: "submitted"
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionSubmittedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'u': // Prefix: "uploaded"
if l := len("uploaded"); len(elem) >= l && elem[0:l] == "uploaded" {
@ -716,6 +760,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
}
case 's': // Prefix: "submitted"
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionMapfixSubmittedOperation
r.summary = "(Internal endpoint) Role Validator changes status from Submitting -> Submitted"
r.operationID = "actionMapfixSubmitted"
r.pathPattern = "/mapfixes/{MapfixID}/status/validator-submitted"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'u': // Prefix: "uploaded"
if l := len("uploaded"); len(elem) >= l && elem[0:l] == "uploaded" {
@ -1059,6 +1127,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
}
case 's': // Prefix: "submitted"
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionSubmittedOperation
r.summary = "(Internal endpoint) Role Validator changes status from Submitting -> Submitted"
r.operationID = "actionSubmissionSubmitted"
r.pathPattern = "/submissions/{SubmissionID}/status/validator-submitted"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'u': // Prefix: "uploaded"
if l := len("uploaded"); len(elem) >= l && elem[0:l] == "uploaded" {

@ -13,6 +13,9 @@ func (s *ErrorStatusCode) Error() string {
// ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation.
type ActionMapfixAcceptedNoContent struct{}
// ActionMapfixSubmittedNoContent is response for ActionMapfixSubmitted operation.
type ActionMapfixSubmittedNoContent struct{}
// ActionMapfixUploadedNoContent is response for ActionMapfixUploaded operation.
type ActionMapfixUploadedNoContent struct{}
@ -25,6 +28,9 @@ type ActionOperationFailedNoContent struct{}
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
type ActionSubmissionAcceptedNoContent struct{}
// ActionSubmissionSubmittedNoContent is response for ActionSubmissionSubmitted operation.
type ActionSubmissionSubmittedNoContent struct{}
// ActionSubmissionUploadedNoContent is response for ActionSubmissionUploaded operation.
type ActionSubmissionUploadedNoContent struct{}
@ -34,12 +40,12 @@ type ActionSubmissionValidatedNoContent struct{}
// Represents error object.
// Ref: #/components/schemas/Error
type Error struct {
Code int64 `json:"code"`
Code uint64 `json:"code"`
Message string `json:"message"`
}
// GetCode returns the value of Code.
func (s *Error) GetCode() int64 {
func (s *Error) GetCode() uint64 {
return s.Code
}
@ -49,7 +55,7 @@ func (s *Error) GetMessage() string {
}
// SetCode sets the value of Code.
func (s *Error) SetCode(val int64) {
func (s *Error) SetCode(val uint64) {
s.Code = val
}
@ -86,23 +92,23 @@ func (s *ErrorStatusCode) SetResponse(val Error) {
// Ref: #/components/schemas/MapfixCreate
type MapfixCreate struct {
OperationID int32 `json:"OperationID"`
AssetOwner int64 `json:"AssetOwner"`
OperationID uint32 `json:"OperationID"`
AssetOwner uint64 `json:"AssetOwner"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
TargetAssetID int64 `json:"TargetAssetID"`
GameID uint32 `json:"GameID"`
AssetID uint64 `json:"AssetID"`
AssetVersion uint64 `json:"AssetVersion"`
TargetAssetID uint64 `json:"TargetAssetID"`
}
// GetOperationID returns the value of OperationID.
func (s *MapfixCreate) GetOperationID() int32 {
func (s *MapfixCreate) GetOperationID() uint32 {
return s.OperationID
}
// GetAssetOwner returns the value of AssetOwner.
func (s *MapfixCreate) GetAssetOwner() int64 {
func (s *MapfixCreate) GetAssetOwner() uint64 {
return s.AssetOwner
}
@ -117,32 +123,32 @@ func (s *MapfixCreate) GetCreator() string {
}
// GetGameID returns the value of GameID.
func (s *MapfixCreate) GetGameID() int32 {
func (s *MapfixCreate) GetGameID() uint32 {
return s.GameID
}
// GetAssetID returns the value of AssetID.
func (s *MapfixCreate) GetAssetID() int64 {
func (s *MapfixCreate) GetAssetID() uint64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *MapfixCreate) GetAssetVersion() int64 {
func (s *MapfixCreate) GetAssetVersion() uint64 {
return s.AssetVersion
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *MapfixCreate) GetTargetAssetID() int64 {
func (s *MapfixCreate) GetTargetAssetID() uint64 {
return s.TargetAssetID
}
// SetOperationID sets the value of OperationID.
func (s *MapfixCreate) SetOperationID(val int32) {
func (s *MapfixCreate) SetOperationID(val uint32) {
s.OperationID = val
}
// SetAssetOwner sets the value of AssetOwner.
func (s *MapfixCreate) SetAssetOwner(val int64) {
func (s *MapfixCreate) SetAssetOwner(val uint64) {
s.AssetOwner = val
}
@ -157,132 +163,40 @@ func (s *MapfixCreate) SetCreator(val string) {
}
// SetGameID sets the value of GameID.
func (s *MapfixCreate) SetGameID(val int32) {
func (s *MapfixCreate) SetGameID(val uint32) {
s.GameID = val
}
// SetAssetID sets the value of AssetID.
func (s *MapfixCreate) SetAssetID(val int64) {
func (s *MapfixCreate) SetAssetID(val uint64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *MapfixCreate) SetAssetVersion(val int64) {
func (s *MapfixCreate) SetAssetVersion(val uint64) {
s.AssetVersion = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *MapfixCreate) SetTargetAssetID(val int64) {
func (s *MapfixCreate) SetTargetAssetID(val uint64) {
s.TargetAssetID = val
}
// Ref: #/components/schemas/MapfixID
type MapfixID struct {
MapfixID int64 `json:"MapfixID"`
MapfixID uint64 `json:"MapfixID"`
}
// GetMapfixID returns the value of MapfixID.
func (s *MapfixID) GetMapfixID() int64 {
func (s *MapfixID) GetMapfixID() uint64 {
return s.MapfixID
}
// SetMapfixID sets the value of MapfixID.
func (s *MapfixID) SetMapfixID(val int64) {
func (s *MapfixID) SetMapfixID(val uint64) {
s.MapfixID = val
}
// NewOptInt32 returns new OptInt32 with value set to v.
func NewOptInt32(v int32) OptInt32 {
return OptInt32{
Value: v,
Set: true,
}
}
// OptInt32 is optional int32.
type OptInt32 struct {
Value int32
Set bool
}
// IsSet returns true if OptInt32 was set.
func (o OptInt32) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptInt32) Reset() {
var v int32
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptInt32) SetTo(v int32) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptInt32) Get() (v int32, ok bool) {
if !o.Set {
return v, false
}
return o.Value, true
}
// Or returns value if set, or given parameter if does not.
func (o OptInt32) Or(d int32) int32 {
if v, ok := o.Get(); ok {
return v
}
return d
}
// NewOptInt64 returns new OptInt64 with value set to v.
func NewOptInt64(v int64) OptInt64 {
return OptInt64{
Value: v,
Set: true,
}
}
// OptInt64 is optional int64.
type OptInt64 struct {
Value int64
Set bool
}
// IsSet returns true if OptInt64 was set.
func (o OptInt64) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptInt64) Reset() {
var v int64
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptInt64) SetTo(v int64) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptInt64) Get() (v int64, ok bool) {
if !o.Set {
return v, false
}
return o.Value, true
}
// Or returns value if set, or given parameter if does not.
func (o OptInt64) Or(d int64) int64 {
if v, ok := o.Get(); ok {
return v
}
return d
}
// NewOptString returns new OptString with value set to v.
func NewOptString(v string) OptString {
return OptString{
@ -329,18 +243,110 @@ func (o OptString) Or(d string) string {
return d
}
// NewOptUint32 returns new OptUint32 with value set to v.
func NewOptUint32(v uint32) OptUint32 {
return OptUint32{
Value: v,
Set: true,
}
}
// OptUint32 is optional uint32.
type OptUint32 struct {
Value uint32
Set bool
}
// IsSet returns true if OptUint32 was set.
func (o OptUint32) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptUint32) Reset() {
var v uint32
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptUint32) SetTo(v uint32) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptUint32) Get() (v uint32, ok bool) {
if !o.Set {
return v, false
}
return o.Value, true
}
// Or returns value if set, or given parameter if does not.
func (o OptUint32) Or(d uint32) uint32 {
if v, ok := o.Get(); ok {
return v
}
return d
}
// NewOptUint64 returns new OptUint64 with value set to v.
func NewOptUint64(v uint64) OptUint64 {
return OptUint64{
Value: v,
Set: true,
}
}
// OptUint64 is optional uint64.
type OptUint64 struct {
Value uint64
Set bool
}
// IsSet returns true if OptUint64 was set.
func (o OptUint64) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptUint64) Reset() {
var v uint64
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptUint64) SetTo(v uint64) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptUint64) Get() (v uint64, ok bool) {
if !o.Set {
return v, false
}
return o.Value, true
}
// Or returns value if set, or given parameter if does not.
func (o OptUint64) Or(d uint64) uint64 {
if v, ok := o.Get(); ok {
return v
}
return d
}
// Ref: #/components/schemas/Script
type Script struct {
ID int64 `json:"ID"`
ID uint64 `json:"ID"`
Name string `json:"Name"`
Hash string `json:"Hash"`
Source string `json:"Source"`
ResourceType int32 `json:"ResourceType"`
ResourceID int64 `json:"ResourceID"`
ResourceType uint32 `json:"ResourceType"`
ResourceID uint64 `json:"ResourceID"`
}
// GetID returns the value of ID.
func (s *Script) GetID() int64 {
func (s *Script) GetID() uint64 {
return s.ID
}
@ -360,17 +366,17 @@ func (s *Script) GetSource() string {
}
// GetResourceType returns the value of ResourceType.
func (s *Script) GetResourceType() int32 {
func (s *Script) GetResourceType() uint32 {
return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *Script) GetResourceID() int64 {
func (s *Script) GetResourceID() uint64 {
return s.ResourceID
}
// SetID sets the value of ID.
func (s *Script) SetID(val int64) {
func (s *Script) SetID(val uint64) {
s.ID = val
}
@ -390,21 +396,21 @@ func (s *Script) SetSource(val string) {
}
// SetResourceType sets the value of ResourceType.
func (s *Script) SetResourceType(val int32) {
func (s *Script) SetResourceType(val uint32) {
s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *Script) SetResourceID(val int64) {
func (s *Script) SetResourceID(val uint64) {
s.ResourceID = val
}
// Ref: #/components/schemas/ScriptCreate
type ScriptCreate struct {
Name string `json:"Name"`
Source string `json:"Source"`
ResourceType int32 `json:"ResourceType"`
ResourceID OptInt64 `json:"ResourceID"`
Name string `json:"Name"`
Source string `json:"Source"`
ResourceType uint32 `json:"ResourceType"`
ResourceID OptUint64 `json:"ResourceID"`
}
// GetName returns the value of Name.
@ -418,12 +424,12 @@ func (s *ScriptCreate) GetSource() string {
}
// GetResourceType returns the value of ResourceType.
func (s *ScriptCreate) GetResourceType() int32 {
func (s *ScriptCreate) GetResourceType() uint32 {
return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *ScriptCreate) GetResourceID() OptInt64 {
func (s *ScriptCreate) GetResourceID() OptUint64 {
return s.ResourceID
}
@ -438,40 +444,40 @@ func (s *ScriptCreate) SetSource(val string) {
}
// SetResourceType sets the value of ResourceType.
func (s *ScriptCreate) SetResourceType(val int32) {
func (s *ScriptCreate) SetResourceType(val uint32) {
s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *ScriptCreate) SetResourceID(val OptInt64) {
func (s *ScriptCreate) SetResourceID(val OptUint64) {
s.ResourceID = val
}
// Ref: #/components/schemas/ScriptID
type ScriptID struct {
ScriptID int64 `json:"ScriptID"`
ScriptID uint64 `json:"ScriptID"`
}
// GetScriptID returns the value of ScriptID.
func (s *ScriptID) GetScriptID() int64 {
func (s *ScriptID) GetScriptID() uint64 {
return s.ScriptID
}
// SetScriptID sets the value of ScriptID.
func (s *ScriptID) SetScriptID(val int64) {
func (s *ScriptID) SetScriptID(val uint64) {
s.ScriptID = val
}
// Ref: #/components/schemas/ScriptPolicy
type ScriptPolicy struct {
ID int64 `json:"ID"`
ID uint64 `json:"ID"`
FromScriptHash string `json:"FromScriptHash"`
ToScriptID int64 `json:"ToScriptID"`
Policy int32 `json:"Policy"`
ToScriptID uint64 `json:"ToScriptID"`
Policy uint32 `json:"Policy"`
}
// GetID returns the value of ID.
func (s *ScriptPolicy) GetID() int64 {
func (s *ScriptPolicy) GetID() uint64 {
return s.ID
}
@ -481,17 +487,17 @@ func (s *ScriptPolicy) GetFromScriptHash() string {
}
// GetToScriptID returns the value of ToScriptID.
func (s *ScriptPolicy) GetToScriptID() int64 {
func (s *ScriptPolicy) GetToScriptID() uint64 {
return s.ToScriptID
}
// GetPolicy returns the value of Policy.
func (s *ScriptPolicy) GetPolicy() int32 {
func (s *ScriptPolicy) GetPolicy() uint32 {
return s.Policy
}
// SetID sets the value of ID.
func (s *ScriptPolicy) SetID(val int64) {
func (s *ScriptPolicy) SetID(val uint64) {
s.ID = val
}
@ -501,85 +507,85 @@ func (s *ScriptPolicy) SetFromScriptHash(val string) {
}
// SetToScriptID sets the value of ToScriptID.
func (s *ScriptPolicy) SetToScriptID(val int64) {
func (s *ScriptPolicy) SetToScriptID(val uint64) {
s.ToScriptID = val
}
// SetPolicy sets the value of Policy.
func (s *ScriptPolicy) SetPolicy(val int32) {
func (s *ScriptPolicy) SetPolicy(val uint32) {
s.Policy = val
}
// Ref: #/components/schemas/ScriptPolicyCreate
type ScriptPolicyCreate struct {
FromScriptID int64 `json:"FromScriptID"`
ToScriptID int64 `json:"ToScriptID"`
Policy int32 `json:"Policy"`
FromScriptID uint64 `json:"FromScriptID"`
ToScriptID uint64 `json:"ToScriptID"`
Policy uint32 `json:"Policy"`
}
// GetFromScriptID returns the value of FromScriptID.
func (s *ScriptPolicyCreate) GetFromScriptID() int64 {
func (s *ScriptPolicyCreate) GetFromScriptID() uint64 {
return s.FromScriptID
}
// GetToScriptID returns the value of ToScriptID.
func (s *ScriptPolicyCreate) GetToScriptID() int64 {
func (s *ScriptPolicyCreate) GetToScriptID() uint64 {
return s.ToScriptID
}
// GetPolicy returns the value of Policy.
func (s *ScriptPolicyCreate) GetPolicy() int32 {
func (s *ScriptPolicyCreate) GetPolicy() uint32 {
return s.Policy
}
// SetFromScriptID sets the value of FromScriptID.
func (s *ScriptPolicyCreate) SetFromScriptID(val int64) {
func (s *ScriptPolicyCreate) SetFromScriptID(val uint64) {
s.FromScriptID = val
}
// SetToScriptID sets the value of ToScriptID.
func (s *ScriptPolicyCreate) SetToScriptID(val int64) {
func (s *ScriptPolicyCreate) SetToScriptID(val uint64) {
s.ToScriptID = val
}
// SetPolicy sets the value of Policy.
func (s *ScriptPolicyCreate) SetPolicy(val int32) {
func (s *ScriptPolicyCreate) SetPolicy(val uint32) {
s.Policy = val
}
// Ref: #/components/schemas/ScriptPolicyID
type ScriptPolicyID struct {
ScriptPolicyID int64 `json:"ScriptPolicyID"`
ScriptPolicyID uint64 `json:"ScriptPolicyID"`
}
// GetScriptPolicyID returns the value of ScriptPolicyID.
func (s *ScriptPolicyID) GetScriptPolicyID() int64 {
func (s *ScriptPolicyID) GetScriptPolicyID() uint64 {
return s.ScriptPolicyID
}
// SetScriptPolicyID sets the value of ScriptPolicyID.
func (s *ScriptPolicyID) SetScriptPolicyID(val int64) {
func (s *ScriptPolicyID) SetScriptPolicyID(val uint64) {
s.ScriptPolicyID = val
}
// Ref: #/components/schemas/SubmissionCreate
type SubmissionCreate struct {
OperationID int32 `json:"OperationID"`
AssetOwner int64 `json:"AssetOwner"`
OperationID uint32 `json:"OperationID"`
AssetOwner uint64 `json:"AssetOwner"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
GameID uint32 `json:"GameID"`
AssetID uint64 `json:"AssetID"`
AssetVersion uint64 `json:"AssetVersion"`
}
// GetOperationID returns the value of OperationID.
func (s *SubmissionCreate) GetOperationID() int32 {
func (s *SubmissionCreate) GetOperationID() uint32 {
return s.OperationID
}
// GetAssetOwner returns the value of AssetOwner.
func (s *SubmissionCreate) GetAssetOwner() int64 {
func (s *SubmissionCreate) GetAssetOwner() uint64 {
return s.AssetOwner
}
@ -594,27 +600,27 @@ func (s *SubmissionCreate) GetCreator() string {
}
// GetGameID returns the value of GameID.
func (s *SubmissionCreate) GetGameID() int32 {
func (s *SubmissionCreate) GetGameID() uint32 {
return s.GameID
}
// GetAssetID returns the value of AssetID.
func (s *SubmissionCreate) GetAssetID() int64 {
func (s *SubmissionCreate) GetAssetID() uint64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *SubmissionCreate) GetAssetVersion() int64 {
func (s *SubmissionCreate) GetAssetVersion() uint64 {
return s.AssetVersion
}
// SetOperationID sets the value of OperationID.
func (s *SubmissionCreate) SetOperationID(val int32) {
func (s *SubmissionCreate) SetOperationID(val uint32) {
s.OperationID = val
}
// SetAssetOwner sets the value of AssetOwner.
func (s *SubmissionCreate) SetAssetOwner(val int64) {
func (s *SubmissionCreate) SetAssetOwner(val uint64) {
s.AssetOwner = val
}
@ -629,32 +635,32 @@ func (s *SubmissionCreate) SetCreator(val string) {
}
// SetGameID sets the value of GameID.
func (s *SubmissionCreate) SetGameID(val int32) {
func (s *SubmissionCreate) SetGameID(val uint32) {
s.GameID = val
}
// SetAssetID sets the value of AssetID.
func (s *SubmissionCreate) SetAssetID(val int64) {
func (s *SubmissionCreate) SetAssetID(val uint64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *SubmissionCreate) SetAssetVersion(val int64) {
func (s *SubmissionCreate) SetAssetVersion(val uint64) {
s.AssetVersion = val
}
// Ref: #/components/schemas/SubmissionID
type SubmissionID struct {
SubmissionID int64 `json:"SubmissionID"`
SubmissionID uint64 `json:"SubmissionID"`
}
// GetSubmissionID returns the value of SubmissionID.
func (s *SubmissionID) GetSubmissionID() int64 {
func (s *SubmissionID) GetSubmissionID() uint64 {
return s.SubmissionID
}
// SetSubmissionID sets the value of SubmissionID.
func (s *SubmissionID) SetSubmissionID(val int64) {
func (s *SubmissionID) SetSubmissionID(val uint64) {
s.SubmissionID = val
}

@ -14,6 +14,12 @@ type Handler interface {
//
// POST /mapfixes/{MapfixID}/status/validator-failed
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixSubmitted implements actionMapfixSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
ActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) error
// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@ -38,6 +44,12 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/validator-failed
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionSubmitted implements actionSubmissionSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
ActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) error
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.

@ -22,6 +22,15 @@ func (UnimplementedHandler) ActionMapfixAccepted(ctx context.Context, params Act
return ht.ErrNotImplemented
}
// ActionMapfixSubmitted implements actionMapfixSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
func (UnimplementedHandler) ActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@ -58,6 +67,15 @@ func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params
return ht.ErrNotImplemented
}
// ActionSubmissionSubmitted implements actionSubmissionSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
func (UnimplementedHandler) ActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.

@ -8,12 +8,107 @@ import (
"github.com/ogen-go/ogen/validate"
)
func (s *Error) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
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(s.Code)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "code",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ErrorStatusCode) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := s.Response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Response",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *MapfixCreate) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
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(s.OperationID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "OperationID",
Error: 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(s.AssetOwner)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetOwner",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
@ -52,6 +147,118 @@ func (s *MapfixCreate) Validate() error {
Error: 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(s.GameID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "GameID",
Error: 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(s.AssetID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetID",
Error: 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(s.AssetVersion)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetVersion",
Error: 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(s.TargetAssetID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "TargetAssetID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *MapfixID) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
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(s.MapfixID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "MapfixID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
@ -64,6 +271,26 @@ func (s *Script) Validate() error {
}
var failures []validate.FieldError
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(s.ID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ID",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
@ -121,6 +348,46 @@ func (s *Script) Validate() error {
Error: 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(s.ResourceType)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ResourceType",
Error: 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(s.ResourceID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ResourceID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
@ -171,6 +438,85 @@ func (s *ScriptCreate) Validate() error {
Error: 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(s.ResourceType)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ResourceType",
Error: err,
})
}
if err := func() error {
if value, ok := s.ResourceID.Get(); ok {
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(value)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
return err
}
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ResourceID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ScriptID) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
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(s.ScriptID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ScriptID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
@ -183,6 +529,26 @@ func (s *ScriptPolicy) Validate() error {
}
var failures []validate.FieldError
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(s.ID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ID",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 16,
@ -202,6 +568,150 @@ func (s *ScriptPolicy) Validate() error {
Error: 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(s.ToScriptID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ToScriptID",
Error: 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(s.Policy)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Policy",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ScriptPolicyCreate) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
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(s.FromScriptID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "FromScriptID",
Error: 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(s.ToScriptID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ToScriptID",
Error: 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(s.Policy)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Policy",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ScriptPolicyID) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
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(s.ScriptPolicyID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ScriptPolicyID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
@ -214,6 +724,46 @@ func (s *SubmissionCreate) Validate() error {
}
var failures []validate.FieldError
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(s.OperationID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "OperationID",
Error: 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(s.AssetOwner)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetOwner",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
@ -252,6 +802,98 @@ func (s *SubmissionCreate) Validate() error {
Error: 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(s.GameID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "GameID",
Error: 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(s.AssetID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetID",
Error: 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(s.AssetVersion)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetVersion",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *SubmissionID) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
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(s.SubmissionID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "SubmissionID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}

53
pkg/model/audit_event.go Normal file

@ -0,0 +1,53 @@
package model
import (
"encoding/json"
"time"
)
type AuditEventType uint32
// User clicked "Submit", "Accept" etc
const AuditEventTypeAction AuditEventType = 0
type AuditEventDataAction struct {
TargetStatus uint32 `json:"target_status"`
}
// User wrote a comment
const AuditEventTypeComment AuditEventType = 1
type AuditEventDataComment struct {
Comment string `json:"comment"`
}
// User changed Model
const AuditEventTypeChangeModel AuditEventType = 2
type AuditEventDataChangeModel struct {
OldModelID uint64 `json:"old_model_id"`
OldModelVersion uint64 `json:"old_model_version"`
NewModelID uint64 `json:"new_model_id"`
NewModelVersion uint64 `json:"new_model_version"`
}
// Validator validates model
const AuditEventTypeChangeValidatedModel AuditEventType = 3
type AuditEventDataChangeValidatedModel struct {
ValidatedModelID uint64 `json:"validated_model_id"`
ValidatedModelVersion uint64 `json:"validated_model_version"`
}
// User changed DisplayName / Creator
const AuditEventTypeChangeDisplayName AuditEventType = 4
const AuditEventTypeChangeCreator AuditEventType = 5
type AuditEventDataChangeName struct {
OldName string `json:"old_name"`
NewName string `json:"new_name"`
}
type AuditEvent struct {
ID uint64 `gorm:"primaryKey"`
CreatedAt time.Time
User uint64
ResourceType ResourceType // is this a submission or is it a mapfix
ResourceID uint64 // submission / mapfix / map ID
EventType AuditEventType
EventData json.RawMessage `gorm:"type:jsonb"`
}

@ -2,39 +2,42 @@ package model
import "time"
type MapfixStatus int32
type MapfixStatus uint32
const (
// Phase: Final MapfixStatus
MapfixStatusRejected MapfixStatus = 8
MapfixStatusUploaded MapfixStatus = 7 // uploaded to the group, final status for mapfixes
// Phase: Creation
MapfixStatusUnderConstruction MapfixStatus = 0
MapfixStatusChangesRequested MapfixStatus = 1
// Phase: Review
MapfixStatusSubmitting MapfixStatus = 2
MapfixStatusSubmitted MapfixStatus = 3
// Phase: Testing
MapfixStatusUploading MapfixStatus = 6
MapfixStatusValidated MapfixStatus = 5
MapfixStatusValidating MapfixStatus = 4
MapfixStatusAccepted MapfixStatus = 3 // pending script review, can re-trigger validation
MapfixStatusAcceptedUnvalidated MapfixStatus = 4 // pending script review, can re-trigger validation
MapfixStatusValidating MapfixStatus = 5
MapfixStatusValidated MapfixStatus = 6
MapfixStatusUploading MapfixStatus = 7
// Phase: Creation
MapfixStatusChangesRequested MapfixStatus = 2
MapfixStatusSubmitted MapfixStatus = 1
MapfixStatusUnderConstruction MapfixStatus = 0
// Phase: Final MapfixStatus
MapfixStatusUploaded MapfixStatus = 8 // uploaded to the group, but pending release
MapfixStatusRejected MapfixStatus = 9
)
type Mapfix struct {
ID int64 `gorm:"primaryKey"`
ID uint64 `gorm:"primaryKey"`
DisplayName string
Creator string
GameID int32
GameID uint32
CreatedAt time.Time
UpdatedAt time.Time
Submitter int64 // UserID
AssetID int64
AssetVersion int64
ValidatedAssetID int64
ValidatedAssetVersion int64
Submitter uint64 // UserID
AssetID uint64
AssetVersion uint64
ValidatedAssetID uint64
ValidatedAssetVersion uint64
Completed bool // Has this version of the map been completed at least once on maptest
TargetAssetID int64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
TargetAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
StatusID MapfixStatus
StatusMessage string
}

@ -7,42 +7,42 @@ package model
type CreateSubmissionRequest struct {
// operation_id is passed back in the response message
OperationID int32
ModelID int64
OperationID uint32
ModelID uint64
}
type CreateMapfixRequest struct {
OperationID int32
ModelID int64
TargetAssetID int64
OperationID uint32
ModelID uint64
TargetAssetID uint64
}
type ValidateSubmissionRequest struct {
// submission_id is passed back in the response message
SubmissionID int64
ModelID int64
ModelVersion int64
ValidatedModelID *int64 // optional value
SubmissionID uint64
ModelID uint64
ModelVersion uint64
ValidatedModelID *uint64 // optional value
}
type ValidateMapfixRequest struct {
MapfixID int64
ModelID int64
ModelVersion int64
ValidatedModelID *int64 // optional value
MapfixID uint64
ModelID uint64
ModelVersion uint64
ValidatedModelID *uint64 // optional value
}
// Create a new map
type UploadSubmissionRequest struct {
SubmissionID int64
ModelID int64
ModelVersion int64
SubmissionID uint64
ModelID uint64
ModelVersion uint64
ModelName string
}
type UploadMapfixRequest struct {
MapfixID int64
ModelID int64
ModelVersion int64
TargetAssetID int64
MapfixID uint64
ModelID uint64
ModelVersion uint64
TargetAssetID uint64
}

@ -2,7 +2,7 @@ package model
import "time"
type OperationStatus int32
type OperationStatus uint32
const (
OperationStatusCreated OperationStatus = 0
OperationStatusCompleted OperationStatus = 1
@ -10,9 +10,9 @@ const (
)
type Operation struct {
ID int32 `gorm:"primaryKey"`
ID uint32 `gorm:"primaryKey"`
CreatedAt time.Time
Owner int64 // UserID
Owner uint64 // UserID
StatusID OperationStatus
StatusMessage string
Path string // redirect to view completed operation e.g. "/mapfixes/4"

@ -1,6 +1,6 @@
package model
type Page struct {
Number int32
Size int32
Number uint32
Size uint32
}

@ -2,7 +2,7 @@ package model
import "time"
type Policy int32
type Policy uint32
const (
ScriptPolicyNone Policy = 0 // not yet reviewed
@ -13,7 +13,7 @@ const (
)
type ScriptPolicy struct {
ID int64 `gorm:"primaryKey"`
ID uint64 `gorm:"primaryKey"`
// Hash of the source code that leads to this policy.
// If this is a replacement mapping, the original source may not be pointed to by any policy.
// The original source should still exist in the scripts table, which can be located by the same hash.
@ -21,7 +21,7 @@ type ScriptPolicy struct {
// The ID of the replacement source (ScriptPolicyReplace)
// or verbatim source (ScriptPolicyAllowed)
// or 0 (other)
ToScriptID int64
ToScriptID uint64
Policy Policy
CreatedAt time.Time
UpdatedAt time.Time

@ -23,7 +23,7 @@ func HashParse(hash string) (uint64, error){
return strconv.ParseUint(hash, 16, 64)
}
type ResourceType int32
type ResourceType uint32
const (
ResourceUnknown ResourceType = 0
ResourceMapfix ResourceType = 1
@ -31,12 +31,17 @@ const (
)
type Script struct {
ID int64 `gorm:"primaryKey"`
ID uint64 `gorm:"primaryKey"`
Name string
Hash int64 // postgres does not support unsigned integers, so we have to pretend
hash uint64
Source string
ResourceType ResourceType // is this a submission or is it a mapfix
ResourceID int64 // which submission / mapfix did this script first appear in
ResourceID uint64 // which submission / mapfix did this script first appear in
CreatedAt time.Time
UpdatedAt time.Time
}
// postgres does not support unsigned integers, so we have to pretend
func (script *Script) GetPostgresInt64() (int64) {
return int64(script.hash)
}

@ -2,40 +2,43 @@ package model
import "time"
type SubmissionStatus int32
type SubmissionStatus uint32
const (
// Phase: Final SubmissionStatus
SubmissionStatusReleased SubmissionStatus = 9
SubmissionStatusRejected SubmissionStatus = 8
// Phase: Creation
SubmissionStatusUnderConstruction SubmissionStatus = 0
SubmissionStatusChangesRequested SubmissionStatus = 1
// Phase: Review
SubmissionStatusSubmitting SubmissionStatus = 2
SubmissionStatusSubmitted SubmissionStatus = 3
// Phase: Testing
SubmissionStatusUploaded SubmissionStatus = 7 // uploaded to the group, but pending release
SubmissionStatusUploading SubmissionStatus = 6
SubmissionStatusValidated SubmissionStatus = 5
SubmissionStatusValidating SubmissionStatus = 4
SubmissionStatusAccepted SubmissionStatus = 3 // pending script review, can re-trigger validation
SubmissionStatusAcceptedUnvalidated SubmissionStatus = 4 // pending script review, can re-trigger validation
SubmissionStatusValidating SubmissionStatus = 5
SubmissionStatusValidated SubmissionStatus = 6
SubmissionStatusUploading SubmissionStatus = 7
SubmissionStatusUploaded SubmissionStatus = 8 // uploaded to the group, but pending release
// Phase: Creation
SubmissionStatusChangesRequested SubmissionStatus = 2
SubmissionStatusSubmitted SubmissionStatus = 1
SubmissionStatusUnderConstruction SubmissionStatus = 0
// Phase: Final SubmissionStatus
SubmissionStatusRejected SubmissionStatus = 9
SubmissionStatusReleased SubmissionStatus = 10
)
type Submission struct {
ID int64 `gorm:"primaryKey"`
ID uint64 `gorm:"primaryKey"`
DisplayName string
Creator string
GameID int32
GameID uint32
CreatedAt time.Time
UpdatedAt time.Time
Submitter int64 // UserID
AssetID int64
AssetVersion int64
ValidatedAssetID int64
ValidatedAssetVersion int64
Submitter uint64 // UserID
AssetID uint64
AssetVersion uint64
ValidatedAssetID uint64
ValidatedAssetVersion uint64
Completed bool // Has this version of the map been completed at least once on maptest
UploadedAssetID int64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
UploadedAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
StatusID SubmissionStatus
StatusMessage string
}

200
pkg/service/audit_events.go Normal file

@ -0,0 +1,200 @@
package service
import (
"context"
"encoding/json"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
// CreateMapfixAuditComment implements createMapfixAuditComment operation.
//
// Post a comment to the audit log
//
// POST /mapfixes/{MapfixID}/comment
func (svc *Service) CreateMapfixAuditComment(ctx context.Context, req api.CreateMapfixAuditCommentReq, params api.CreateMapfixAuditCommentParams) (error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleMapfixReview()
if err != nil {
return err
}
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.AddPostgresInt32("resource_type", uint32(model.ResourceMapfix))
filter.AddPostgresInt64("resource_id", params.MapfixID)
items, err := svc.DB.AuditEvents().List(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
})
if err != nil {
return nil, err
}
var resp []api.AuditEvent
for _, item := range items {
EventData := api.AuditEventEventData{}
err = EventData.UnmarshalJSON(item.EventData)
if err != nil {
return nil, err
}
resp = append(resp, api.AuditEvent{
ID: item.ID,
Date: item.CreatedAt.Unix(),
User: item.User,
ResourceType: uint32(item.ResourceType),
ResourceID: item.ResourceID,
EventType: uint32(item.EventType),
EventData: EventData,
})
}
return resp, nil
}
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
//
// Post a comment to the audit log
//
// POST /submissions/{SubmissionID}/comment
func (svc *Service) CreateSubmissionAuditComment(ctx context.Context, req api.CreateSubmissionAuditCommentReq, params api.CreateSubmissionAuditCommentParams) (error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionReview()
if err != nil {
return err
}
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
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeComment,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ListSubmissionAuditEvents invokes listSubmissionAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /submissions/{SubmissionID}/audit-events
func (svc *Service) ListSubmissionAuditEvents(ctx context.Context, params api.ListSubmissionAuditEventsParams) ([]api.AuditEvent, error) {
filter := datastore.Optional()
filter.AddPostgresInt32("resource_type", uint32(model.ResourceSubmission))
filter.AddPostgresInt64("resource_id", params.SubmissionID)
items, err := svc.DB.AuditEvents().List(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
})
if err != nil {
return nil, err
}
var resp []api.AuditEvent
for _, item := range items {
EventData := api.AuditEventEventData{}
err = EventData.UnmarshalJSON(item.EventData)
if err != nil {
return nil, err
}
resp = append(resp, api.AuditEvent{
ID: item.ID,
Date: item.CreatedAt.Unix(),
User: item.User,
ResourceType: uint32(item.ResourceType),
ResourceID: item.ResourceID,
EventType: uint32(item.EventType),
EventData: EventData,
})
}
return resp, nil
}

@ -25,18 +25,29 @@ var(
model.MapfixStatusUploading,
model.MapfixStatusValidated,
model.MapfixStatusValidating,
model.MapfixStatusAccepted,
model.MapfixStatusAcceptedUnvalidated,
}
// Allow 5 mapfixes every 10 minutes
CreateMapfixRateLimit int64 = 5
CreateMapfixRecencyWindow = time.Second*600
)
var (
ErrCreationPhaseMapfixesLimit = errors.New("Active mapfixes limited to 20")
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")
)
// POST /mapfixes
func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTriggerCreate) (*api.OperationID, error) {
// sanitization
if request.AssetID<0 || request.TargetAssetID<0{
return nil, ErrNegativeID
}
var ModelID=uint64(request.AssetID);
var TargetAssetID=uint64(request.TargetAssetID);
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
@ -54,7 +65,7 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
filter.Add("status_id", CreationPhaseMapfixStatuses)
creation_mapfixes, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
Number: 1,
Size: int32(CreationPhaseMapfixesLimit),
Size: uint32(CreationPhaseMapfixesLimit),
},datastore.ListSortDisabled)
if err != nil {
return nil, err
@ -76,8 +87,23 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
}
}
// Check if too many operations have been created recently
{
count, err := svc.DB.Operations().CountSince(ctx,
int64(userId),
time.Now().Add(-CreateMapfixRecencyWindow),
)
if err != nil {
return nil, err
}
if CreateMapfixRateLimit < count {
return nil, ErrCreateMapfixRateLimit
}
}
operation, err := svc.DB.Operations().Create(ctx, model.Operation{
Owner: int64(userId),
Owner: userId,
StatusID: model.OperationStatusCreated,
})
if err != nil {
@ -86,8 +112,8 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
create_request := model.CreateMapfixRequest{
OperationID: operation.ID,
ModelID: request.AssetID,
TargetAssetID: request.TargetAssetID,
ModelID: ModelID,
TargetAssetID: TargetAssetID,
}
j, err := json.Marshal(create_request)
@ -95,7 +121,10 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
return nil, err
}
svc.Nats.Publish("maptest.mapfixes.create", []byte(j))
_, err = svc.Nats.Publish("maptest.mapfixes.create", []byte(j))
if err != nil {
return nil, err
}
return &api.OperationID{
OperationID: operation.ID,
@ -108,7 +137,7 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
//
// GET /mapfixes/{MapfixID}
func (svc *Service) GetMapfix(ctx context.Context, params api.GetMapfixParams) (*api.Mapfix, error) {
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
mapfix, err := svc.DB.Mapfixes().Get(ctx, int64(params.MapfixID))
if err != nil {
return nil, err
}
@ -116,15 +145,15 @@ func (svc *Service) GetMapfix(ctx context.Context, params api.GetMapfixParams) (
ID: mapfix.ID,
DisplayName: mapfix.DisplayName,
Creator: mapfix.Creator,
GameID: mapfix.GameID,
GameID: uint32(mapfix.GameID),
CreatedAt: mapfix.CreatedAt.Unix(),
UpdatedAt: mapfix.UpdatedAt.Unix(),
Submitter: int64(mapfix.Submitter),
AssetID: int64(mapfix.AssetID),
AssetVersion: int64(mapfix.AssetVersion),
Submitter: mapfix.Submitter,
AssetID: mapfix.AssetID,
AssetVersion: mapfix.AssetVersion,
Completed: mapfix.Completed,
TargetAssetID: int64(mapfix.TargetAssetID),
StatusID: int32(mapfix.StatusID),
TargetAssetID: mapfix.TargetAssetID,
StatusID: uint32(mapfix.StatusID),
StatusMessage: mapfix.StatusMessage,
}, nil
}
@ -134,22 +163,34 @@ func (svc *Service) GetMapfix(ctx context.Context, params api.GetMapfixParams) (
// Get list of mapfixes.
//
// GET /mapfixes
func (svc *Service) ListMapfixes(ctx context.Context, params api.ListMapfixesParams) ([]api.Mapfix, error) {
func (svc *Service) ListMapfixes(ctx context.Context, params api.ListMapfixesParams) (*api.Mapfixes, error) {
filter := datastore.Optional()
if params.DisplayName.IsSet(){
filter.Add("display_name", params.DisplayName.Value)
filter.Add2("display_name", params.DisplayName.Value)
}
if params.Creator.IsSet(){
filter.Add("creator", params.Creator.Value)
filter.Add2("creator", params.Creator.Value)
}
if params.GameID.IsSet(){
filter.Add("game_id", params.GameID.Value)
filter.AddPostgresInt32("game_id", params.GameID.Value)
}
if params.Submitter.IsSet(){
filter.AddPostgresInt64("submitter", params.Submitter.Value)
}
if params.AssetID.IsSet(){
filter.AddPostgresInt64("asset_id", params.AssetID.Value)
}
if params.TargetAssetID.IsSet(){
filter.AddPostgresInt64("target_asset_id", params.TargetAssetID.Value)
}
if params.StatusID.IsSet(){
filter.AddPostgresInt32("status_id", params.StatusID.Value)
}
sort := datastore.ListSort(params.Sort.Or(int32(datastore.ListSortDisabled)))
sort := datastore.ListSort(params.Sort.Or(uint32(datastore.ListSortDisabled)))
items, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
total, items, err := svc.DB.Mapfixes().ListWithTotal(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
},sort)
@ -157,25 +198,26 @@ func (svc *Service) ListMapfixes(ctx context.Context, params api.ListMapfixesPar
return nil, err
}
var resp []api.Mapfix
var resp api.Mapfixes
resp.Total=total
for _, item := range items {
resp = append(resp, api.Mapfix{
resp.Mapfixes = append(resp.Mapfixes, api.Mapfix{
ID: item.ID,
DisplayName: item.DisplayName,
Creator: item.Creator,
GameID: item.GameID,
CreatedAt: item.CreatedAt.Unix(),
UpdatedAt: item.UpdatedAt.Unix(),
Submitter: int64(item.Submitter),
AssetID: int64(item.AssetID),
AssetVersion: int64(item.AssetVersion),
Submitter: item.Submitter,
AssetID: item.AssetID,
AssetVersion: item.AssetVersion,
Completed: item.Completed,
TargetAssetID: int64(item.TargetAssetID),
StatusID: int32(item.StatusID),
TargetAssetID: item.TargetAssetID,
StatusID: uint32(item.StatusID),
})
}
return resp, nil
return &resp, nil
}
// PatchMapfixCompleted implements patchMapfixCompleted operation.
@ -220,22 +262,58 @@ func (svc *Service) UpdateMapfixModel(ctx context.Context, params api.UpdateMapf
return err
}
has_role, err := userInfo.IsSubmitter(uint64(mapfix.Submitter))
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check if caller is the submitter
has_role := userId == mapfix.Submitter
if !has_role {
return ErrPermissionDeniedNotSubmitter
}
OldModelID := mapfix.AssetID
OldModelVersion := mapfix.AssetVersion
NewModelID := uint64(params.ModelID)
NewModelVersion := uint64(params.ModelVersion)
// check if Status is ChangesRequested|Submitted|UnderConstruction
pmap := datastore.Optional()
pmap.AddNotNil("asset_id", params.ModelID)
pmap.AddNotNil("asset_version", params.VersionID)
pmap.Add("asset_id", NewModelID)
pmap.Add("asset_version", NewModelVersion)
//always reset completed when model changes
pmap.Add("completed", false)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusChangesRequested, model.MapfixStatusSubmitted, model.MapfixStatusUnderConstruction}, pmap)
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusChangesRequested, model.MapfixStatusSubmitted, model.MapfixStatusUnderConstruction}, pmap)
if err != nil {
return err
}
event_data := model.AuditEventDataChangeModel{
OldModelID: OldModelID,
OldModelVersion: OldModelVersion,
NewModelID: NewModelID,
NewModelVersion: NewModelVersion,
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeChangeModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixReject invokes actionMapfixReject operation.
@ -255,13 +333,45 @@ func (svc *Service) ActionMapfixReject(ctx context.Context, params api.ActionMap
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
return ErrPermissionDeniedNeedRoleMapfixReview
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// transaction
target_status := model.MapfixStatusRejected
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusRejected)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitted}, smap)
smap.Add("status_id", target_status)
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitted}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixRequestChanges invokes actionMapfixRequestChanges operation.
@ -281,13 +391,13 @@ func (svc *Service) ActionMapfixRequestChanges(ctx context.Context, params api.A
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
return ErrPermissionDeniedNeedRoleMapfixReview
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusChangesRequested)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidated, model.MapfixStatusAccepted, model.MapfixStatusSubmitted}, smap)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidated, model.MapfixStatusAcceptedUnvalidated, model.MapfixStatusSubmitted}, smap)
}
// ActionMapfixRevoke invokes actionMapfixRevoke operation.
@ -307,27 +417,56 @@ func (svc *Service) ActionMapfixRevoke(ctx context.Context, params api.ActionMap
return err
}
has_role, err := userInfo.IsSubmitter(uint64(mapfix.Submitter))
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check if caller is the submitter
has_role := userId == mapfix.Submitter
if !has_role {
return ErrPermissionDeniedNotSubmitter
}
// transaction
target_status := model.MapfixStatusUnderConstruction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusUnderConstruction)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitted, model.MapfixStatusChangesRequested}, smap)
smap.Add("status_id", target_status)
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitted, model.MapfixStatusChangesRequested}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixSubmit invokes actionMapfixSubmit operation.
// ActionMapfixTriggerSubmit invokes actionMapfixTriggerSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/submit
func (svc *Service) ActionMapfixSubmit(ctx context.Context, params api.ActionMapfixSubmitParams) error {
// POST /mapfixes/{MapfixID}/status/trigger-submit
func (svc *Service) ActionMapfixTriggerSubmit(ctx context.Context, params api.ActionMapfixTriggerSubmitParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
@ -339,19 +478,114 @@ func (svc *Service) ActionMapfixSubmit(ctx context.Context, params api.ActionMap
return err
}
has_role, err := userInfo.IsSubmitter(uint64(mapfix.Submitter))
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check if caller is the submitter
has_role := userId == mapfix.Submitter
if !has_role {
return ErrPermissionDeniedNotSubmitter
}
// transaction
target_status := model.MapfixStatusSubmitted
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusSubmitted)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUnderConstruction, model.MapfixStatusChangesRequested}, smap)
smap.Add("status_id", target_status)
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUnderConstruction, model.MapfixStatusChangesRequested}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixResetSubmitting implements actionMapfixResetSubmitting operation.
//
// Role MapfixReview changes status from Submitting -> UnderConstruction.
//
// POST /mapfixes/{MapfixID}/status/reset-submitting
func (svc *Service) ActionMapfixResetSubmitting(ctx context.Context, params api.ActionMapfixResetSubmittingParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check when mapfix was updated
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
if err != nil {
return err
}
if time.Now().Before(mapfix.UpdatedAt.Add(time.Second*10)) {
// the last time the mapfix was updated must be longer than 10 seconds ago
return ErrDelayReset
}
// check if caller has required role
has_role := userId == mapfix.Submitter
if !has_role {
return ErrPermissionDeniedNotSubmitter
}
// transaction
target_status := model.MapfixStatusUnderConstruction
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("status_message", "Manually forced reset")
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixTriggerUpload invokes actionMapfixTriggerUpload operation.
@ -371,12 +605,18 @@ func (svc *Service) ActionMapfixTriggerUpload(ctx context.Context, params api.Ac
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapUpload
return ErrPermissionDeniedNeedRoleMapfixUpload
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// transaction
target_status := model.MapfixStatusUploading
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusUploading)
smap.Add("status_id", target_status)
mapfix, err := svc.DB.Mapfixes().IfStatusThenUpdateAndGet(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidated}, smap)
if err != nil {
return err
@ -395,7 +635,31 @@ func (svc *Service) ActionMapfixTriggerUpload(ctx context.Context, params api.Ac
return err
}
svc.Nats.Publish("maptest.mapfixes.uploadfix", []byte(j))
_, err = svc.Nats.Publish("maptest.mapfixes.upload", []byte(j))
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
@ -417,7 +681,12 @@ func (svc *Service) ActionMapfixValidated(ctx context.Context, params api.Action
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapUpload
return ErrPermissionDeniedNeedRoleMapfixUpload
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check when mapfix was updated
@ -431,9 +700,36 @@ func (svc *Service) ActionMapfixValidated(ctx context.Context, params api.Action
}
// transaction
target_status := model.MapfixStatusValidated
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusValidated)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUploading}, smap)
smap.Add("status_id", target_status)
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUploading}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixTriggerValidate invokes actionMapfixTriggerValidate operation.
@ -453,7 +749,7 @@ func (svc *Service) ActionMapfixTriggerValidate(ctx context.Context, params api.
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
return ErrPermissionDeniedNeedRoleMapfixReview
}
// read mapfix (this could be done with a transaction WHERE clause)
@ -462,11 +758,13 @@ func (svc *Service) ActionMapfixTriggerValidate(ctx context.Context, params api.
return err
}
has_role, err = userInfo.IsSubmitter(uint64(mapfix.Submitter))
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check if caller is NOT the submitter
has_role = userId == mapfix.Submitter
if has_role {
return ErrAcceptOwnMapfix
}
@ -489,8 +787,9 @@ func (svc *Service) ActionMapfixTriggerValidate(ctx context.Context, params api.
}
// transaction
target_status := model.MapfixStatusValidating
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusValidating)
smap.Add("status_id", target_status)
mapfix, err = svc.DB.Mapfixes().IfStatusThenUpdateAndGet(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitted}, smap)
if err != nil {
return err
@ -513,7 +812,31 @@ func (svc *Service) ActionMapfixTriggerValidate(ctx context.Context, params api.
return err
}
svc.Nats.Publish("maptest.mapfixes.validate", []byte(j))
_, err = svc.Nats.Publish("maptest.mapfixes.validate", []byte(j))
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
@ -535,13 +858,19 @@ func (svc *Service) ActionMapfixRetryValidate(ctx context.Context, params api.Ac
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
return ErrPermissionDeniedNeedRoleMapfixReview
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// transaction
target_status := model.MapfixStatusValidating
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusValidating)
mapfix, err := svc.DB.Mapfixes().IfStatusThenUpdateAndGet(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusAccepted}, smap)
smap.Add("status_id", target_status)
mapfix, err := svc.DB.Mapfixes().IfStatusThenUpdateAndGet(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusAcceptedUnvalidated}, smap)
if err != nil {
return err
}
@ -563,7 +892,31 @@ func (svc *Service) ActionMapfixRetryValidate(ctx context.Context, params api.Ac
return err
}
svc.Nats.Publish("maptest.mapfixes.validate", []byte(j))
_, err = svc.Nats.Publish("maptest.mapfixes.validate", []byte(j))
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
@ -585,7 +938,12 @@ func (svc *Service) ActionMapfixAccepted(ctx context.Context, params api.ActionM
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
return ErrPermissionDeniedNeedRoleMapfixReview
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check when mapfix was updated
@ -599,8 +957,35 @@ func (svc *Service) ActionMapfixAccepted(ctx context.Context, params api.ActionM
}
// transaction
target_status := model.MapfixStatusAcceptedUnvalidated
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusAccepted)
smap.Add("status_id", target_status)
smap.Add("status_message", "Manually forced reset")
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}

@ -24,11 +24,13 @@ func (svc *Service) GetOperation(ctx context.Context, params api.GetOperationPar
return nil, err
}
has_role, err := userInfo.IsSubmitter(uint64(operation.Owner))
userId, err := userInfo.GetUserID()
if err != nil {
return nil, err
}
// check if caller is operation owner
// check if caller is the submitter
has_role := userId == operation.Owner
if !has_role {
return nil, ErrPermissionDeniedNotSubmitter
}
@ -36,7 +38,7 @@ func (svc *Service) GetOperation(ctx context.Context, params api.GetOperationPar
return &api.Operation{
OperationID: operation.ID,
Date: operation.CreatedAt.Unix(),
Owner: operation.Owner,
Owner: int64(operation.Owner),
Status: int32(operation.StatusID),
StatusMessage: operation.StatusMessage,
Path: operation.Path,

@ -63,13 +63,13 @@ func (svc *Service) ListScriptPolicy(ctx context.Context, params api.ListScriptP
if err != nil {
return nil, err
}
filter.AddNotNil("from_script_hash", int64(hash)) // No type safety!
filter.Add("from_script_hash", int64(hash)) // No type safety!
}
if params.ToScriptID.IsSet(){
filter.AddNotNil("to_script_id", params.ToScriptID.Value)
filter.Add("to_script_id", params.ToScriptID.Value)
}
if params.Policy.IsSet(){
filter.AddNotNil("policy", params.Policy.Value)
filter.Add("policy", params.Policy.Value)
}
items, err := svc.DB.ScriptPolicy().List(ctx, filter, model.Page{
@ -121,13 +121,6 @@ func (svc *Service) DeleteScriptPolicy(ctx context.Context, params api.DeleteScr
//
// GET /script-policy/{ScriptPolicyID}
func (svc *Service) GetScriptPolicy(ctx context.Context, params api.GetScriptPolicyParams) (*api.ScriptPolicy, error) {
_, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
// Read permission for script policy only requires you to be logged in
policy, err := svc.DB.ScriptPolicy().Get(ctx, params.ScriptPolicyID)
if err != nil {
return nil, err

@ -57,19 +57,19 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
if err != nil {
return nil, err
}
filter.AddNotNil("hash", int64(hash)) // No type safety!
filter.Add("hash", int64(hash)) // No type safety!
}
if params.Name.IsSet(){
filter.AddNotNil("name", params.Name.Value)
filter.Add("name", params.Name.Value)
}
if params.Source.IsSet(){
filter.AddNotNil("source", params.Source.Value)
filter.Add("source", params.Source.Value)
}
if params.ResourceType.IsSet(){
filter.AddNotNil("resource_type", params.ResourceType.Value)
filter.Add("resource_type", params.ResourceType.Value)
}
if params.ResourceID.IsSet(){
filter.AddNotNil("resource_id", params.ResourceID.Value)
filter.Add("resource_id", params.ResourceID.Value)
}
items, err := svc.DB.Scripts().List(ctx, filter, model.Page{
@ -122,13 +122,6 @@ func (svc *Service) DeleteScript(ctx context.Context, params api.DeleteScriptPar
//
// GET /scripts/{ScriptID}
func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (*api.Script, error) {
_, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
// Read permission for scripts only requires you to be logged in
script, err := svc.DB.Scripts().Get(ctx, params.ScriptID)
if err != nil {
return nil, err

@ -88,13 +88,6 @@ func (usr UserInfoHandle) Validate() (bool, error) {
}
return validate.Valid, nil
}
func (usr UserInfoHandle) IsSubmitter(submitter uint64) (bool, error) {
userId, err := usr.GetUserID()
if err != nil {
return false, err
}
return userId == submitter, nil
}
func (usr UserInfoHandle) hasRoles(wantRoles Roles) (bool, error) {
haveroles, err := usr.GetRoles()
if err != nil {

@ -19,11 +19,14 @@ var (
ErrDelayReset = errors.New("Please give the validator at least 10 seconds to operate before attempting to reset the status")
ErrPermissionDeniedNotSubmitter = fmt.Errorf("%w: You must be the submitter to perform this action", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionRelease = fmt.Errorf("%w: Need Role SubmissionRelease", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapUpload = fmt.Errorf("%w: Need Role MapUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapReview = fmt.Errorf("%w: Need Role MapReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapfixUpload = fmt.Errorf("%w: Need Role MapfixUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapfixReview = fmt.Errorf("%w: Need Role MapfixReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionUpload = fmt.Errorf("%w: Need Role SubmissionUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionReview = fmt.Errorf("%w: Need Role SubmissionReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapDownload = fmt.Errorf("%w: Need Role MapDownload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleScriptWrite = fmt.Errorf("%w: Need Role ScriptWrite", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMaptest = fmt.Errorf("%w: Need Role Maptest", ErrPermissionDenied)
ErrNegativeID = errors.New("A negative ID was provided")
)
type Service struct {

@ -25,8 +25,11 @@ var(
model.SubmissionStatusUploading,
model.SubmissionStatusValidated,
model.SubmissionStatusValidating,
model.SubmissionStatusAccepted,
model.SubmissionStatusAcceptedUnvalidated,
}
// Allow 5 submissions every 10 minutes
CreateSubmissionRateLimit int64 = 5
CreateSubmissionRecencyWindow = time.Second*600
)
var (
@ -35,10 +38,17 @@ var (
ErrReleaseInvalidStatus = errors.New("Only submissions with Uploaded status can be released")
ErrReleaseNoUploadedAssetID = errors.New("Only submissions with a UploadedAssetID can be released")
ErrAcceptOwnSubmission = fmt.Errorf("%w: You cannot accept your own submission as the submitter", ErrPermissionDenied)
ErrCreateSubmissionRateLimit = errors.New("You must not create more than 5 submissions every 10 minutes")
)
// POST /submissions
func (svc *Service) CreateSubmission(ctx context.Context, request *api.SubmissionTriggerCreate) (*api.OperationID, error) {
// sanitization
if request.AssetID<0{
return nil, ErrNegativeID
}
var ModelID=uint64(request.AssetID);
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
@ -66,8 +76,24 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
return nil, ErrCreationPhaseSubmissionsLimit
}
}
// Check if too many operations have been created recently
{
count, err := svc.DB.Operations().CountSince(ctx,
int64(userId),
time.Now().Add(-CreateSubmissionRecencyWindow),
)
if err != nil {
return nil, err
}
if CreateSubmissionRateLimit < count {
return nil, ErrCreateSubmissionRateLimit
}
}
operation, err := svc.DB.Operations().Create(ctx, model.Operation{
Owner: int64(userId),
Owner: userId,
StatusID: model.OperationStatusCreated,
})
if err != nil {
@ -76,7 +102,7 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
create_request := model.CreateSubmissionRequest{
OperationID: operation.ID,
ModelID: request.AssetID,
ModelID: ModelID,
}
j, err := json.Marshal(create_request)
@ -84,7 +110,10 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
return nil, err
}
svc.Nats.Publish("maptest.submissions.create", []byte(j))
_, err = svc.Nats.Publish("maptest.submissions.create", []byte(j))
if err != nil {
return nil, err
}
return &api.OperationID{
OperationID: operation.ID,
@ -105,7 +134,7 @@ func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionP
ID: submission.ID,
DisplayName: submission.DisplayName,
Creator: submission.Creator,
GameID: submission.GameID,
GameID: int32(submission.GameID),
CreatedAt: submission.CreatedAt.Unix(),
UpdatedAt: submission.UpdatedAt.Unix(),
Submitter: int64(submission.Submitter),
@ -123,7 +152,7 @@ func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionP
// Get list of submissions.
//
// GET /submissions
func (svc *Service) ListSubmissions(ctx context.Context, params api.ListSubmissionsParams) ([]api.Submission, error) {
func (svc *Service) ListSubmissions(ctx context.Context, params api.ListSubmissionsParams) (*api.Submissions, error) {
filter := datastore.Optional()
if params.DisplayName.IsSet(){
@ -135,10 +164,22 @@ func (svc *Service) ListSubmissions(ctx context.Context, params api.ListSubmissi
if params.GameID.IsSet(){
filter.Add("game_id", params.GameID.Value)
}
if params.Submitter.IsSet(){
filter.Add("submitter", params.Submitter.Value)
}
if params.AssetID.IsSet(){
filter.Add("asset_id", params.AssetID.Value)
}
if params.UploadedAssetID.IsSet(){
filter.Add("uploaded_asset_id", params.UploadedAssetID.Value)
}
if params.StatusID.IsSet(){
filter.Add("status_id", params.StatusID.Value)
}
sort := datastore.ListSort(params.Sort.Or(int32(datastore.ListSortDisabled)))
items, err := svc.DB.Submissions().List(ctx, filter, model.Page{
total, items, err := svc.DB.Submissions().ListWithTotal(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
},sort)
@ -146,13 +187,14 @@ func (svc *Service) ListSubmissions(ctx context.Context, params api.ListSubmissi
return nil, err
}
var resp []api.Submission
var resp api.Submissions
resp.Total=total
for _, item := range items {
resp = append(resp, api.Submission{
resp.Submissions = append(resp.Submissions, api.Submission{
ID: item.ID,
DisplayName: item.DisplayName,
Creator: item.Creator,
GameID: item.GameID,
GameID: int32(item.GameID),
CreatedAt: item.CreatedAt.Unix(),
UpdatedAt: item.UpdatedAt.Unix(),
Submitter: int64(item.Submitter),
@ -164,7 +206,7 @@ func (svc *Service) ListSubmissions(ctx context.Context, params api.ListSubmissi
})
}
return resp, nil
return &resp, nil
}
// PatchSubmissionCompleted implements patchSubmissionCompleted operation.
@ -209,22 +251,58 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
return err
}
has_role, err := userInfo.IsSubmitter(uint64(submission.Submitter))
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check if caller is the submitter
has_role := userId == submission.Submitter
if !has_role {
return ErrPermissionDeniedNotSubmitter
}
OldModelID := submission.AssetID
OldModelVersion := submission.AssetVersion
NewModelID := uint64(params.ModelID)
NewModelVersion := uint64(params.ModelVersion)
// check if Status is ChangesRequested|Submitted|UnderConstruction
pmap := datastore.Optional()
pmap.AddNotNil("asset_id", params.ModelID)
pmap.AddNotNil("asset_version", params.VersionID)
pmap.Add("asset_id", NewModelID)
pmap.Add("asset_version", NewModelVersion)
//always reset completed when model changes
pmap.Add("completed", false)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusChangesRequested, model.SubmissionStatusSubmitted, model.SubmissionStatusUnderConstruction}, pmap)
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusChangesRequested, model.SubmissionStatusUnderConstruction}, pmap)
if err != nil {
return err
}
event_data := model.AuditEventDataChangeModel{
OldModelID: OldModelID,
OldModelVersion: OldModelVersion,
NewModelID: NewModelID,
NewModelVersion: NewModelVersion,
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: submission.ID,
EventType: model.AuditEventTypeChangeModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionReject invokes actionSubmissionReject operation.
@ -244,13 +322,45 @@ func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.Actio
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
return ErrPermissionDeniedNeedRoleSubmissionReview
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// transaction
target_status := model.SubmissionStatusRejected
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusRejected)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted}, smap)
smap.Add("status_id", target_status)
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
@ -270,13 +380,45 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params a
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
return ErrPermissionDeniedNeedRoleSubmissionReview
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// transaction
target_status := model.SubmissionStatusChangesRequested
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusChangesRequested)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidated, model.SubmissionStatusAccepted, model.SubmissionStatusSubmitted}, smap)
smap.Add("status_id", target_status)
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidated, model.SubmissionStatusAcceptedUnvalidated, model.SubmissionStatusSubmitted}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionRevoke invokes actionSubmissionRevoke operation.
@ -296,27 +438,56 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio
return err
}
has_role, err := userInfo.IsSubmitter(uint64(submission.Submitter))
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check if caller is the submitter
has_role := userId == submission.Submitter
if !has_role {
return ErrPermissionDeniedNotSubmitter
}
// transaction
target_status := model.SubmissionStatusUnderConstruction
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusUnderConstruction)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted, model.SubmissionStatusChangesRequested}, smap)
smap.Add("status_id", target_status)
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted, model.SubmissionStatusChangesRequested}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionSubmit invokes actionSubmissionSubmit operation.
// ActionSubmissionTriggerSubmit invokes actionSubmissionTriggerSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/submit
func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.ActionSubmissionSubmitParams) error {
// POST /submissions/{SubmissionID}/status/trigger-submit
func (svc *Service) ActionSubmissionTriggerSubmit(ctx context.Context, params api.ActionSubmissionTriggerSubmitParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
@ -328,19 +499,114 @@ func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.Actio
return err
}
has_role, err := userInfo.IsSubmitter(uint64(submission.Submitter))
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check if caller is the submitter
has_role := userId == submission.Submitter
if !has_role {
return ErrPermissionDeniedNotSubmitter
}
// transaction
target_status := model.SubmissionStatusSubmitted
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusSubmitted)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUnderConstruction, model.SubmissionStatusChangesRequested}, smap)
smap.Add("status_id", target_status)
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUnderConstruction, model.SubmissionStatusChangesRequested}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionResetSubmitting implements actionSubmissionResetSubmitting operation.
//
// Role SubmissionReview changes status from Submitting -> UnderConstruction.
//
// POST /submissions/{SubmissionID}/status/reset-submitting
func (svc *Service) ActionSubmissionResetSubmitting(ctx context.Context, params api.ActionSubmissionResetSubmittingParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check when submission was updated
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil {
return err
}
if time.Now().Before(submission.UpdatedAt.Add(time.Second*10)) {
// the last time the submission was updated must be longer than 10 seconds ago
return ErrDelayReset
}
// check if caller has required role
has_role := userId == submission.Submitter
if !has_role {
return ErrPermissionDeniedNotSubmitter
}
// transaction
target_status := model.SubmissionStatusUnderConstruction
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("status_message", "Manually forced reset")
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation.
@ -360,12 +626,18 @@ func (svc *Service) ActionSubmissionTriggerUpload(ctx context.Context, params ap
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapUpload
return ErrPermissionDeniedNeedRoleSubmissionUpload
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// transaction
target_status := model.SubmissionStatusUploading
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusUploading)
smap.Add("status_id", target_status)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidated}, smap)
if err != nil {
return err
@ -387,12 +659,36 @@ func (svc *Service) ActionSubmissionTriggerUpload(ctx context.Context, params ap
return err
}
svc.Nats.Publish("maptest.submissions.upload", []byte(j))
_, err = svc.Nats.Publish("maptest.submissions.upload", []byte(j))
if err != nil {
return err
}
} else {
// refuse to operate
return ErrUploadedAssetIDAlreadyExists
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
@ -413,7 +709,12 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params api.Ac
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapUpload
return ErrPermissionDeniedNeedRoleSubmissionUpload
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check when submission was updated
@ -427,9 +728,36 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params api.Ac
}
// transaction
target_status := model.SubmissionStatusValidated
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusValidated)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
smap.Add("status_id", target_status)
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation.
@ -449,7 +777,7 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
return ErrPermissionDeniedNeedRoleSubmissionReview
}
// read submission (this could be done with a transaction WHERE clause)
@ -458,18 +786,21 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
return err
}
has_role, err = userInfo.IsSubmitter(uint64(submission.Submitter))
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check if caller is NOT the submitter
has_role = userId == submission.Submitter
if has_role {
return ErrAcceptOwnSubmission
}
// transaction
target_status := model.SubmissionStatusValidating
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusValidating)
smap.Add("status_id", target_status)
submission, err = svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted}, smap)
if err != nil {
return err
@ -492,7 +823,31 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
return err
}
svc.Nats.Publish("maptest.submissions.validate", []byte(j))
_, err = svc.Nats.Publish("maptest.submissions.validate", []byte(j))
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
@ -514,13 +869,19 @@ func (svc *Service) ActionSubmissionRetryValidate(ctx context.Context, params ap
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
return ErrPermissionDeniedNeedRoleSubmissionReview
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// transaction
target_status := model.SubmissionStatusValidating
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusValidating)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusAccepted}, smap)
smap.Add("status_id", target_status)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusAcceptedUnvalidated}, smap)
if err != nil {
return err
}
@ -542,7 +903,31 @@ func (svc *Service) ActionSubmissionRetryValidate(ctx context.Context, params ap
return err
}
svc.Nats.Publish("maptest.submissions.validate", []byte(j))
_, err = svc.Nats.Publish("maptest.submissions.validate", []byte(j))
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
@ -564,7 +949,12 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params api.Act
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
return ErrPermissionDeniedNeedRoleSubmissionReview
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
// check when submission was updated
@ -578,10 +968,37 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params api.Act
}
// transaction
target_status := model.SubmissionStatusAcceptedUnvalidated
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusAccepted)
smap.Add("status_id", target_status)
smap.Add("status_message", "Manually forced reset")
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ReleaseSubmissions invokes releaseSubmissions operation.
@ -627,12 +1044,13 @@ func (svc *Service) ReleaseSubmissions(ctx context.Context, request []api.Releas
for i,submission := range submissions{
date := request[i].Date.Unix()
var GameID = int32(submission.GameID)
// create each map with go-grpc
_, err := svc.Client.Create(ctx, &maps.MapRequest{
ID: submission.UploadedAssetID,
ID: int64(submission.UploadedAssetID),
DisplayName: &submission.DisplayName,
Creator: &submission.Creator,
GameID: &submission.GameID,
GameID: &GameID,
Date: &date,
})
if err != nil {

@ -2,6 +2,7 @@ package service_internal
import (
"context"
"encoding/json"
"errors"
"fmt"
@ -16,7 +17,7 @@ var(
model.MapfixStatusUploading,
model.MapfixStatusValidated,
model.MapfixStatusValidating,
model.MapfixStatusAccepted,
model.MapfixStatusAcceptedUnvalidated,
model.MapfixStatusChangesRequested,
model.MapfixStatusSubmitted,
model.MapfixStatusUnderConstruction,
@ -34,13 +35,82 @@ var(
//
// POST /mapfixes/{MapfixID}/validated-model
func (svc *Service) UpdateMapfixValidatedModel(ctx context.Context, params internal.UpdateMapfixValidatedModelParams) error {
ValidatedModelID := uint64(params.ValidatedModelID)
ValidatedModelVersion := uint64(params.ValidatedModelVersion)
// check if Status is ChangesRequested|Submitted|UnderConstruction
pmap := datastore.Optional()
pmap.AddNotNil("validated_asset_id", params.ValidatedModelID)
pmap.AddNotNil("validated_asset_version", params.ValidatedModelVersion)
pmap.Add("validated_asset_id", ValidatedModelID)
pmap.Add("validated_asset_version", ValidatedModelVersion)
// DO NOT reset completed when validated model is updated
// pmap.Add("completed", false)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, pmap)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, pmap)
if err != nil {
return err
}
event_data := model.AuditEventDataChangeValidatedModel{
ValidatedModelID: ValidatedModelID,
ValidatedModelVersion: ValidatedModelVersion,
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeChangeValidatedModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixValidate invokes actionMapfixValidate operation.
//
// Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
func (svc *Service) ActionMapfixSubmitted(ctx context.Context, params internal.ActionMapfixSubmittedParams) error {
// transaction
target_status := model.MapfixStatusSubmitted
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixValidate invokes actionMapfixValidate operation.
@ -62,10 +132,37 @@ func (svc *Service) ActionMapfixValidated(ctx context.Context, params internal.A
// POST /mapfixes/{MapfixID}/status/validator-failed
func (svc *Service) ActionMapfixAccepted(ctx context.Context, params internal.ActionMapfixAcceptedParams) error {
// transaction
target_status := model.MapfixStatusAcceptedUnvalidated
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusAccepted)
smap.Add("status_id", target_status)
smap.Add("status_message", params.StatusMessage)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixUploaded implements actionMapfixUploaded operation.
@ -75,13 +172,54 @@ func (svc *Service) ActionMapfixAccepted(ctx context.Context, params internal.Ac
// POST /mapfixes/{MapfixID}/status/validator-uploaded
func (svc *Service) ActionMapfixUploaded(ctx context.Context, params internal.ActionMapfixUploadedParams) error {
// transaction
target_status := model.MapfixStatusUploaded
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusUploaded)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUploading}, smap)
smap.Add("status_id", target_status)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUploading}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// POST /mapfixes
func (svc *Service) CreateMapfix(ctx context.Context, request *internal.MapfixCreate) (*internal.MapfixID, error) {
// sanitization
if request.GameID<0||
request.AssetOwner<0||
request.AssetID<0||
request.AssetVersion<0||
request.TargetAssetID<0{
return nil, ErrNegativeID
}
var GameID=uint32(request.GameID);
var Submitter=uint64(request.AssetOwner);
var AssetID=uint64(request.AssetID);
var AssetVersion=uint64(request.AssetVersion);
var TargetAssetID=uint64(request.TargetAssetID);
// Check if an active mapfix with the same asset id exists
{
filter := datastore.Optional()
@ -107,7 +245,7 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *internal.MapfixCr
// check if user owns asset
// TODO: allow bypass by admin
if operation.Owner != request.AssetOwner {
if operation.Owner != Submitter {
return nil, ErrNotAssetOwner
}
@ -115,12 +253,12 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *internal.MapfixCr
ID: 0,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: request.GameID,
Submitter: request.AssetOwner,
AssetID: request.AssetID,
AssetVersion: request.AssetVersion,
GameID: GameID,
Submitter: Submitter,
AssetID: AssetID,
AssetVersion: AssetVersion,
Completed: false,
TargetAssetID: request.TargetAssetID,
TargetAssetID: TargetAssetID,
StatusID: model.MapfixStatusUnderConstruction,
})
if err != nil {

@ -4,7 +4,7 @@ import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/internal"
api "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
@ -49,13 +49,13 @@ func (svc *Service) ListScriptPolicy(ctx context.Context, params api.ListScriptP
if err != nil {
return nil, err
}
filter.AddNotNil("from_script_hash", int64(hash)) // No type safety!
filter.Add("from_script_hash", int64(hash)) // No type safety!
}
if params.ToScriptID.IsSet(){
filter.AddNotNil("to_script_id", params.ToScriptID.Value)
filter.Add("to_script_id", params.ToScriptID.Value)
}
if params.Policy.IsSet(){
filter.AddNotNil("policy", params.Policy.Value)
filter.Add("policy", params.Policy.Value)
}
items, err := svc.DB.ScriptPolicy().List(ctx, filter, model.Page{

@ -4,7 +4,7 @@ import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/internal"
api "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
@ -44,19 +44,19 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
if err != nil {
return nil, err
}
filter.AddNotNil("hash", int64(hash)) // No type safety!
filter.Add("hash", int64(hash)) // No type safety!
}
if params.Name.IsSet(){
filter.AddNotNil("name", params.Name.Value)
filter.Add("name", params.Name.Value)
}
if params.Source.IsSet(){
filter.AddNotNil("source", params.Source.Value)
filter.Add("source", params.Source.Value)
}
if params.ResourceType.IsSet(){
filter.AddNotNil("resource_type", params.ResourceType.Value)
filter.Add("resource_type", params.ResourceType.Value)
}
if params.ResourceID.IsSet(){
filter.AddNotNil("resource_id", params.ResourceID.Value)
filter.Add("resource_id", params.ResourceID.Value)
}
items, err := svc.DB.Scripts().List(ctx, filter, model.Page{

@ -3,12 +3,21 @@ package service_internal
import (
"context"
"errors"
"math"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
"github.com/nats-io/nats.go"
)
const (
ValidtorUserID uint64 = uint64(math.MaxInt64)
)
var (
ErrNegativeID = errors.New("A negative ID was provided")
)
type Service struct {
DB datastore.Datastore
Nats nats.JetStreamContext

@ -2,6 +2,7 @@ package service_internal
import (
"context"
"encoding/json"
"errors"
"fmt"
@ -16,7 +17,7 @@ var(
model.SubmissionStatusUploading,
model.SubmissionStatusValidated,
model.SubmissionStatusValidating,
model.SubmissionStatusAccepted,
model.SubmissionStatusAcceptedUnvalidated,
model.SubmissionStatusChangesRequested,
model.SubmissionStatusSubmitted,
model.SubmissionStatusUnderConstruction,
@ -33,13 +34,82 @@ var(
//
// POST /submissions/{SubmissionID}/validated-model
func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params internal.UpdateSubmissionValidatedModelParams) error {
ValidatedModelID := uint64(params.ValidatedModelID)
ValidatedModelVersion := uint64(params.ValidatedModelVersion)
// check if Status is ChangesRequested|Submitted|UnderConstruction
pmap := datastore.Optional()
pmap.AddNotNil("validated_asset_id", params.ValidatedModelID)
pmap.AddNotNil("validated_asset_version", params.ValidatedModelVersion)
pmap.Add("validated_asset_id", ValidatedModelID)
pmap.Add("validated_asset_version", ValidatedModelVersion)
// DO NOT reset completed when validated model is updated
// pmap.Add("completed", false)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, pmap)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, pmap)
if err != nil {
return err
}
event_data := model.AuditEventDataChangeValidatedModel{
ValidatedModelID: ValidatedModelID,
ValidatedModelVersion: ValidatedModelVersion,
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeChangeValidatedModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
//
// Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
func (svc *Service) ActionSubmissionSubmitted(ctx context.Context, params internal.ActionSubmissionSubmittedParams) error {
// transaction
target_status := model.SubmissionStatusSubmitted
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
@ -49,9 +119,36 @@ func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params i
// POST /submissions/{SubmissionID}/status/validator-validated
func (svc *Service) ActionSubmissionValidated(ctx context.Context, params internal.ActionSubmissionValidatedParams) error {
// transaction
target_status := model.SubmissionStatusValidated
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusValidated)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
smap.Add("status_id", target_status)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
@ -61,10 +158,37 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params intern
// POST /submissions/{SubmissionID}/status/validator-failed
func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params internal.ActionSubmissionAcceptedParams) error {
// transaction
target_status := model.SubmissionStatusAcceptedUnvalidated
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusAccepted)
smap.Add("status_id", target_status)
smap.Add("status_message", params.StatusMessage)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
@ -74,14 +198,53 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params interna
// POST /submissions/{SubmissionID}/status/validator-uploaded
func (svc *Service) ActionSubmissionUploaded(ctx context.Context, params internal.ActionSubmissionUploadedParams) error {
// transaction
target_status := model.SubmissionStatusUploaded
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusUploaded)
smap.Add("status_id", target_status)
smap.Add("uploaded_asset_id", params.UploadedAssetID)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// POST /submissions
func (svc *Service) CreateSubmission(ctx context.Context, request *internal.SubmissionCreate) (*internal.SubmissionID, error) {
// sanitization
if request.GameID<0||
request.AssetOwner<0||
request.AssetID<0||
request.AssetVersion<0{
return nil, ErrNegativeID
}
var GameID=uint32(request.GameID);
var Submitter=uint64(request.AssetOwner);
var AssetID=uint64(request.AssetID);
var AssetVersion=uint64(request.AssetVersion);
// Check if an active submission with the same asset id exists
{
filter := datastore.Optional()
@ -107,7 +270,7 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *internal.Subm
// check if user owns asset
// TODO: allow bypass by admin
if operation.Owner != request.AssetOwner {
if operation.Owner != Submitter {
return nil, ErrNotAssetOwner
}
@ -115,10 +278,10 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *internal.Subm
ID: 0,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: request.GameID,
Submitter: request.AssetOwner,
AssetID: request.AssetID,
AssetVersion: request.AssetVersion,
GameID: GameID,
Submitter: Submitter,
AssetID: AssetID,
AssetVersion: AssetVersion,
Completed: false,
StatusID: model.SubmissionStatusUnderConstruction,
})

@ -7,7 +7,7 @@ edition = "2021"
submissions-api = { path = "api", features = ["internal"], default-features = false, registry = "strafesnet" }
async-nats = "0.40.0"
futures = "0.3.31"
rbx_asset = { version = "0.3.3", registry = "strafesnet" }
rbx_asset = { version = "0.4.3", 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"}

@ -1,6 +1,6 @@
# Using the `rust-musl-builder` as base image, instead of
# the official Rust toolchain
FROM docker.io/clux/muslrust:stable AS chef
FROM registry.itzana.me/docker-proxy/clux/muslrust:1.86.0-stable AS chef
USER root
RUN cargo install cargo-chef
WORKDIR /app
@ -17,7 +17,7 @@ RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path r
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl --bin maps-validation
FROM docker.io/alpine:latest AS runtime
FROM registry.itzana.me/docker-proxy/alpine:3.21 AS runtime
RUN addgroup -S myuser && adduser -S myuser -G myuser
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/maps-validation /usr/local/bin/
USER myuser

@ -1,6 +1,6 @@
[package]
name = "submissions-api"
version = "0.6.1"
version = "0.7.0"
edition = "2021"
publish = ["strafesnet"]
repository = "https://git.itzana.me/StrafesNET/maps-service"

@ -22,7 +22,7 @@ macro_rules! query_pairs{
macro_rules! action{
($system:expr,$fname:ident,$config:ident,$config_type:ident,$action:expr,$config_submission_id:expr,$(($param:expr,$value:expr))*)=>{
pub async fn $fname(&self,$config:$config_type)->Result<(),Error>{
let url_raw=format!(concat!("{}/",$system,"/{}/status/",$action),self.0.base_url,$config_submission_id);
let url_raw=format!(concat!("{}/",$system,"/{}/",$action),self.0.base_url,$config_submission_id);
let url=query_pairs!(reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?,$(($param,$value))*);
response_ok(
@ -162,15 +162,15 @@ impl Context{
.json().await.map_err(Error::ReqwestJson)
}
// simple submission endpoints
action!("submissions",action_submission_validated,config,SubmissionID,"validator-validated",config.0,);
action!("submissions",action_submission_validated,config,SubmissionID,"status/validator-validated",config.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,"validator-uploaded",config.SubmissionID,
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,"validator-failed",config.SubmissionID,
action!("submissions",action_submission_accepted,config,ActionSubmissionAcceptedRequest,"status/validator-failed",config.SubmissionID,
("StatusMessage",config.StatusMessage.as_str())
);
pub async fn create_mapfix<'a>(&self,config:CreateMapfixRequest<'a>)->Result<MapfixIDResponse,Error>{
@ -185,17 +185,17 @@ impl Context{
.json().await.map_err(Error::ReqwestJson)
}
// simple mapfixes endpoints
action!("mapfixes",action_mapfix_validated,config,MapfixID,"validator-validated",config.0,);
action!("mapfixes",action_mapfix_validated,config,MapfixID,"status/validator-validated",config.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,"validator-uploaded",config.MapfixID,);
action!("mapfixes",action_mapfix_accepted,config,ActionMapfixAcceptedRequest,"validator-failed",config.MapfixID,
action!("mapfixes",action_mapfix_uploaded,config,ActionMapfixUploadedRequest,"status/validator-uploaded",config.MapfixID,);
action!("mapfixes",action_mapfix_accepted,config,ActionMapfixAcceptedRequest,"status/validator-failed",config.MapfixID,
("StatusMessage",config.StatusMessage.as_str())
);
// simple operation endpoint
action!("operations",action_operation_failed,config,ActionOperationFailedRequest,"operation-failed",config.OperationID,
action!("operations",action_operation_failed,config,ActionOperationFailedRequest,"status/operation-failed",config.OperationID,
("StatusMessage",config.StatusMessage.as_str())
);
}

@ -3,13 +3,13 @@ use crate::rbx_util::{get_mapinfo,read_dom,MapInfo,ReadDomError,GetMapInfoError,
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
ModelVersionsPage(rbx_asset::cookie::PageError),
EmptyVersionsPage,
CreatorTypeMustBeUser(rbx_asset::cookie::CreatorType),
ModelDetails(rbx_asset::cookie::GetError),
ModelInfoDownload(rbx_asset::cookie::GetAssetV2Error),
ModelFileDownload(rbx_asset::cookie::GetError),
NoLocations,
CreatorTypeMustBeUser,
ModelInfoDownload(rbx_asset::cloud::GetError),
ModelLocationDownload(rbx_asset::cloud::GetError),
NonFreeModel,
ModelFileDownload(rbx_asset::cloud::GetError),
ParseUserID(core::num::ParseIntError),
ParseModelVersion(core::num::ParseIntError),
ModelFileDecode(ReadDomError),
GetMapInfo(GetMapInfoError),
ParseGameID(ParseGameIDError),
@ -35,34 +35,36 @@ pub struct CreateResult{
}
impl crate::message_handler::MessageHandler{
pub async fn create_inner(&self,create_info:CreateRequest)->Result<CreateResult,Error>{
// discover asset creator
let creator_fut=async{
self.cookie_context.get_asset_details(
rbx_asset::cookie::GetAssetDetailsRequest{asset_id:create_info.ModelID}
).await.map_err(Error::ModelDetails)
// discover asset creator and latest version
let info=self.cloud_context.get_asset_info(
rbx_asset::cloud::GetAssetLatestRequest{asset_id:create_info.ModelID}
).await.map_err(Error::ModelInfoDownload)?;
// reject models created by a group
let rbx_asset::cloud::Creator::userId(user_id_string)=info.creationContext.creator else{
return Err(Error::CreatorTypeMustBeUser);
};
// parse user string and model version string
let user_id:u64=user_id_string.parse().map_err(Error::ParseUserID)?;
let asset_version=info.revisionId.parse().map_err(Error::ParseModelVersion)?;
// download the location of the map model
let location=self.cloud_context.get_asset_version_location(rbx_asset::cloud::GetAssetVersionRequest{
asset_id:create_info.ModelID,
version:asset_version,
}).await.map_err(Error::ModelLocationDownload)?;
// if the location does not exist, you are not allowed to donwload it
let Some(location)=location.location else{
return Err(Error::NonFreeModel);
};
// download the map model
let asset_fut=async{
let asset_info=self.cookie_context.get_asset_v2(rbx_asset::cookie::GetAssetRequest{
asset_id:create_info.ModelID,
version:None,
}).await.map_err(Error::ModelInfoDownload)?;
let location=asset_info.info.locations.first().ok_or(Error::NoLocations)?;
let data=self.cookie_context.get_asset_v2_download(location).await.map_err(Error::ModelFileDownload)?;
Ok((asset_info.version,data))
};
let (details,(asset_version,model_data))=tokio::try_join!(creator_fut,asset_fut)?;
if details.Creator.CreatorType!=rbx_asset::cookie::CreatorType::User{
return Err(Error::CreatorTypeMustBeUser(details.Creator.CreatorType));
}
let model_data=self.cloud_context.get_asset(&location).await.map_err(Error::ModelFileDownload)?;
// decode dom (slow!)
let dom=read_dom(&mut std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?;
let dom=read_dom(std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?;
// parse create fields out of asset
let MapInfo{
@ -74,7 +76,7 @@ impl crate::message_handler::MessageHandler{
let game_id=game_id.map_err(Error::ParseGameID)?;
Ok(CreateResult{
AssetOwner:details.Creator.Id as i64,
AssetOwner:user_id as i64,
DisplayName:display_name.unwrap_or_default().to_owned(),
Creator:creator.unwrap_or_default().to_owned(),
GameID:game_id as i32,

@ -20,6 +20,7 @@ pub enum StartupError{
NatsConnect(async_nats::ConnectError),
NatsGetStream(async_nats::jetstream::context::GetStreamError),
NatsConsumer(async_nats::jetstream::stream::ConsumerError),
NatsConsumerUpdate(async_nats::jetstream::stream::ConsumerUpdateError),
NatsStream(async_nats::jetstream::consumer::StreamError),
}
impl std::fmt::Display for StartupError{
@ -41,9 +42,12 @@ async fn main()->Result<(),StartupError>{
Err(e)=>Err(e).expect("ROBLOX_GROUP_ID env required"),
};
// talk to roblox through STRAFESNET_CI2 account
// create / upload models through STRAFESNET_CI2 account
let cookie=std::env::var("RBXCOOKIE").expect("RBXCOOKIE env required");
let cookie_context=rbx_asset::cookie::CookieContext::new(rbx_asset::cookie::Cookie::new(cookie));
let cookie_context=rbx_asset::cookie::Context::new(rbx_asset::cookie::Cookie::new(cookie));
// download models through cloud api
let api_key=std::env::var("RBX_API_KEY").expect("RBX_API_KEY env required");
let cloud_context=rbx_asset::cloud::Context::new(rbx_asset::cloud::ApiKey::new(api_key));
// maps-service api
let api_host_internal=std::env::var("API_HOST_INTERNAL").expect("API_HOST_INTERNAL env required");
@ -52,20 +56,35 @@ async fn main()->Result<(),StartupError>{
// nats
let nats_host=std::env::var("NATS_HOST").expect("NATS_HOST env required");
let nats_fut=async{
const STREAM_NAME:&str="maptest";
const DURABLE_NAME:&str="validation";
const FILTER_SUBJECT:&str="maptest.>";
let nats_config=async_nats::jetstream::consumer::pull::Config{
name:Some(DURABLE_NAME.to_owned()),
durable_name:Some(DURABLE_NAME.to_owned()),
filter_subject:FILTER_SUBJECT.to_owned(),
..Default::default()
};
let nasty=async_nats::connect(nats_host).await.map_err(StartupError::NatsConnect)?;
// use nats jetstream
async_nats::jetstream::new(nasty)
.get_stream("maptest").await.map_err(StartupError::NatsGetStream)?
.get_or_create_consumer("validation",async_nats::jetstream::consumer::pull::Config{
name:Some("validation".to_owned()),
durable_name:Some("validation".to_owned()),
filter_subject:"maptest.>".to_owned(),
..Default::default()
}).await.map_err(StartupError::NatsConsumer)?
.messages().await.map_err(StartupError::NatsStream)
let stream=async_nats::jetstream::new(nasty)
.get_stream(STREAM_NAME).await.map_err(StartupError::NatsGetStream)?;
let consumer=stream.get_or_create_consumer(DURABLE_NAME,nats_config.clone()).await.map_err(StartupError::NatsConsumer)?;
// check if config matches expected config
if consumer.cached_info().config.filter_subject!=FILTER_SUBJECT{
stream.update_consumer(nats_config).await.map_err(StartupError::NatsConsumerUpdate)?;
}
// only need messages
consumer.messages().await.map_err(StartupError::NatsStream)
};
let message_handler=message_handler::MessageHandler::new(cookie_context,group_id,api);
let message_handler=message_handler::MessageHandler::new(cloud_context,cookie_context,group_id,api);
// Create a signal listener for SIGTERM
let mut sig_term=tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()).expect("Failed to create SIGTERM signal listener");

@ -26,18 +26,21 @@ fn from_slice<'a,T:serde::de::Deserialize<'a>>(slice:&'a [u8])->Result<T,HandleM
}
pub struct MessageHandler{
pub(crate) cookie_context:rbx_asset::cookie::CookieContext,
pub(crate) cloud_context:rbx_asset::cloud::Context,
pub(crate) cookie_context:rbx_asset::cookie::Context,
pub(crate) group_id:Option<u64>,
pub(crate) api:submissions_api::internal::Context,
}
impl MessageHandler{
pub fn new(
cookie_context:rbx_asset::cookie::CookieContext,
cloud_context:rbx_asset::cloud::Context,
cookie_context:rbx_asset::cookie::Context,
group_id:Option<u64>,
api:submissions_api::internal::Context,
)->Self{
Self{
cloud_context,
cookie_context,
group_id,
api,

@ -15,10 +15,10 @@ impl std::fmt::Display for ReadDomError{
}
impl std::error::Error for ReadDomError{}
pub fn read_dom<R:std::io::Read+std::io::Seek>(input:&mut R)->Result<rbx_dom_weak::WeakDom,ReadDomError>{
pub fn read_dom<R:std::io::Read+std::io::Seek>(mut input:R)->Result<rbx_dom_weak::WeakDom,ReadDomError>{
let mut first_8=[0u8;8];
std::io::Read::read_exact(input,&mut first_8).map_err(ReadDomError::Read)?;
std::io::Seek::rewind(input).map_err(ReadDomError::Seek)?;
std::io::Read::read_exact(&mut input,&mut first_8).map_err(ReadDomError::Read)?;
std::io::Seek::rewind(&mut input).map_err(ReadDomError::Seek)?;
match &first_8[0..4]{
b"<rob"=>{
match &first_8[4..8]{

@ -3,7 +3,9 @@ use crate::nats_types::UploadMapfixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Get(rbx_asset::cookie::GetError),
GetLocation(rbx_asset::cloud::GetError),
NonFreeModel,
Get(rbx_asset::cloud::GetError),
Json(serde_json::Error),
Upload(rbx_asset::cookie::UploadError),
ApiActionMapfixUploaded(submissions_api::Error),
@ -17,11 +19,19 @@ impl std::error::Error for Error{}
impl crate::message_handler::MessageHandler{
pub async fn upload_mapfix(&self,upload_info:UploadMapfixRequest)->Result<(),Error>{
// download the map model version
let model_data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
// download the location of the map model
let location=self.cloud_context.get_asset_version_location(rbx_asset::cloud::GetAssetVersionRequest{
asset_id:upload_info.ModelID,
version:Some(upload_info.ModelVersion),
}).await.map_err(Error::Get)?;
version:upload_info.ModelVersion,
}).await.map_err(Error::GetLocation)?;
// if the location does not exist, you are not allowed to donwload it
let Some(location)=location.location else{
return Err(Error::NonFreeModel);
};
// download the map model
let model_data=self.cloud_context.get_asset(&location).await.map_err(Error::Get)?;
// upload the map to the strafesnet group
let _upload_response=self.cookie_context.upload(rbx_asset::cookie::UploadRequest{

@ -3,7 +3,9 @@ use crate::nats_types::UploadSubmissionRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Get(rbx_asset::cookie::GetError),
GetLocation(rbx_asset::cloud::GetError),
NonFreeModel,
Get(rbx_asset::cloud::GetError),
Json(serde_json::Error),
Create(rbx_asset::cookie::CreateError),
SystemTime(std::time::SystemTimeError),
@ -18,11 +20,19 @@ impl std::error::Error for Error{}
impl crate::message_handler::MessageHandler{
pub async fn upload_submission(&self,upload_info:UploadSubmissionRequest)->Result<(),Error>{
// download the map model version
let model_data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
// download the location of the map model
let location=self.cloud_context.get_asset_version_location(rbx_asset::cloud::GetAssetVersionRequest{
asset_id:upload_info.ModelID,
version:Some(upload_info.ModelVersion),
}).await.map_err(Error::Get)?;
version:upload_info.ModelVersion,
}).await.map_err(Error::GetLocation)?;
// if the location does not exist, you are not allowed to donwload it
let Some(location)=location.location else{
return Err(Error::NonFreeModel);
};
// download the map model
let model_data=self.cloud_context.get_asset(&location).await.map_err(Error::Get)?;
// upload the map to the strafesnet group
let upload_response=self.cookie_context.create(rbx_asset::cookie::CreateRequest{

@ -32,11 +32,13 @@ fn hash_source(source:&str)->String{
#[allow(dead_code)]
#[derive(Debug)]
pub enum ValidateError{
pub enum Error{
ScriptFlaggedIllegalKeyword(String),
ScriptBlocked(Option<submissions_api::types::ScriptID>),
ScriptNotYetReviewed(Option<submissions_api::types::ScriptID>),
ModelFileDownload(rbx_asset::cookie::GetError),
ModelLocationDownload(rbx_asset::cloud::GetError),
NonFreeModel,
ModelFileDownload(rbx_asset::cloud::GetError),
ModelFileDecode(ReadDomError),
ApiGetScriptPolicyFromHash(submissions_api::types::SingleItemError),
ApiGetScript(submissions_api::Error),
@ -51,12 +53,12 @@ pub enum ValidateError{
AssetUpload(rbx_asset::cookie::UploadError),
AssetCreate(rbx_asset::cookie::CreateError),
}
impl std::fmt::Display for ValidateError{
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for ValidateError{}
impl std::error::Error for Error{}
#[allow(nonstandard_style)]
pub struct ValidateRequest{
@ -88,15 +90,23 @@ impl From<crate::nats_types::ValidateSubmissionRequest> for ValidateRequest{
}
impl crate::message_handler::MessageHandler{
pub async fn validate_inner(&self,validate_info:ValidateRequest)->Result<(),ValidateError>{
// download map
let data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
pub async fn validate_inner(&self,validate_info:ValidateRequest)->Result<(),Error>{
// download the location of the map model
let location=self.cloud_context.get_asset_version_location(rbx_asset::cloud::GetAssetVersionRequest{
asset_id:validate_info.ModelID,
version:Some(validate_info.ModelVersion),
}).await.map_err(ValidateError::ModelFileDownload)?;
version:validate_info.ModelVersion,
}).await.map_err(Error::ModelLocationDownload)?;
// if the location does not exist, you are not allowed to donwload it
let Some(location)=location.location else{
return Err(Error::NonFreeModel);
};
// download the map model
let model_data=self.cloud_context.get_asset(&location).await.map_err(Error::ModelFileDownload)?;
// decode dom (slow!)
let mut dom=read_dom(&mut std::io::Cursor::new(data)).map_err(ValidateError::ModelFileDecode)?;
let mut dom=read_dom(std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?;
/* VALIDATE MAP */
@ -111,7 +121,7 @@ impl crate::message_handler::MessageHandler{
// immediately abort
// grab path to offending script
let path=get_partial_path(&dom,script);
return Err(ValidateError::ScriptFlaggedIllegalKeyword(path));
return Err(Error::ScriptFlaggedIllegalKeyword(path));
}
// associate a name and policy with the source code
// policy will be fetched from the database to replace the default policy
@ -132,7 +142,7 @@ impl crate::message_handler::MessageHandler{
// fetch the script policy
let script_policy=self.api.get_script_policy_from_hash(submissions_api::types::HashRequest{
hash:hash.as_str(),
}).await.map_err(ValidateError::ApiGetScriptPolicyFromHash)?;
}).await.map_err(Error::ApiGetScriptPolicyFromHash)?;
// write the policy to the script_map, fetching the replacement code if necessary
if let Some(script_policy)=script_policy{
@ -144,7 +154,7 @@ impl crate::message_handler::MessageHandler{
submissions_api::types::Policy::Replace=>{
let script=self.api.get_script(submissions_api::types::GetScriptRequest{
ScriptID:script_policy.ToScriptID,
}).await.map_err(ValidateError::ApiGetScript)?;
}).await.map_err(Error::ApiGetScript)?;
Policy::Replace(script.Source)
},
};
@ -160,14 +170,14 @@ impl crate::message_handler::MessageHandler{
Source:source.as_str(),
ResourceType:resource_type,
ResourceID:Some(resource_id),
}).await.map_err(ValidateError::ApiCreateScript)?;
}).await.map_err(Error::ApiCreateScript)?;
// create a None policy (pending review by yours truly)
self.api.create_script_policy(submissions_api::types::CreateScriptPolicyRequest{
ToScriptID:script.ScriptID,
FromScriptID:script.ScriptID,
Policy:submissions_api::types::Policy::None,
}).await.map_err(ValidateError::ApiCreateScriptPolicy)?;
}).await.map_err(Error::ApiCreateScriptPolicy)?;
}
Ok(())
@ -175,7 +185,7 @@ impl crate::message_handler::MessageHandler{
.await?;
// make the replacements
let mut modified=true;
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"){
@ -184,8 +194,8 @@ impl crate::message_handler::MessageHandler{
let hash=hash_source(source.as_str());
let script=self.api.get_script_from_hash(submissions_api::types::HashRequest{
hash:hash.as_str(),
}).await.map_err(ValidateError::ApiGetScriptFromHash)?;
return Err(ValidateError::ScriptBlocked(script.map(|s|s.ID)));
}).await.map_err(Error::ApiGetScriptFromHash)?;
return Err(Error::ScriptBlocked(script.map(|s|s.ID)));
},
None
|Some(Policy::None)
@ -193,8 +203,8 @@ impl crate::message_handler::MessageHandler{
let hash=hash_source(source.as_str());
let script=self.api.get_script_from_hash(submissions_api::types::HashRequest{
hash:hash.as_str(),
}).await.map_err(ValidateError::ApiGetScriptFromHash)?;
return Err(ValidateError::ScriptNotYetReviewed(script.map(|s|s.ID)));
}).await.map_err(Error::ApiGetScriptFromHash)?;
return Err(Error::ScriptNotYetReviewed(script.map(|s|s.ID)));
},
Some(Policy::Allowed)=>(),
Some(Policy::Delete)=>{
@ -211,19 +221,17 @@ impl crate::message_handler::MessageHandler{
}
}
println!("[Validator] Forcing model upload! modified=true");
// if the model was validated, the submission must be changed to use the modified model
if modified{
let (validated_model_id,validated_model_version)=if modified{
// serialize model (slow!)
let mut data=Vec::new();
let &[map_ref]=dom.root().children()else{
return Err(ValidateError::ModelFileRootMustHaveOneChild);
return Err(Error::ModelFileRootMustHaveOneChild);
};
rbx_binary::to_writer(&mut data,&dom,&[map_ref]).map_err(ValidateError::ModelFileEncode)?;
rbx_binary::to_writer(&mut data,&dom,&[map_ref]).map_err(Error::ModelFileEncode)?;
// upload a model lol
let model_id=if let Some(model_id)=validate_info.ValidatedModelID{
if let Some(model_id)=validate_info.ValidatedModelID{
// upload to existing id
let response=self.cookie_context.upload(rbx_asset::cookie::UploadRequest{
assetid:model_id,
@ -232,13 +240,13 @@ impl crate::message_handler::MessageHandler{
ispublic:None,
allowComments:None,
groupId:None,
},data).await.map_err(ValidateError::AssetUpload)?;
},data).await.map_err(Error::AssetUpload)?;
response.AssetId
(response.AssetId,response.AssetVersion)
}else{
// grab the map instance from the map re
// grab the map instance from the map ref
let Some(map_instance)=dom.get_by_ref(map_ref)else{
return Err(ValidateError::ModelFileChildRefIsNil);
return Err(Error::ModelFileChildRefIsNil);
};
// create new model
let response=self.cookie_context.create(rbx_asset::cookie::CreateRequest{
@ -247,29 +255,31 @@ impl crate::message_handler::MessageHandler{
ispublic:true,
allowComments:true,
groupId:None,
},data).await.map_err(ValidateError::AssetCreate)?;
},data).await.map_err(Error::AssetCreate)?;
response.AssetId
};
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,
ModelID:model_id,
ModelVersion:1, //TODO
}).await.map_err(ValidateError::ApiUpdateMapfixModel)?;
},
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,
ModelID:model_id,
ModelVersion:1, //TODO
}).await.map_err(ValidateError::ApiUpdateSubmissionModel)?;
},
(response.AssetId,response.AssetVersion)
}
}else{
(validate_info.ModelID,validate_info.ModelVersion)
};
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,
ModelID:validated_model_id,
ModelVersion:validated_model_version,
}).await.map_err(Error::ApiUpdateMapfixModel)?;
},
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,
ModelID:validated_model_id,
ModelVersion:validated_model_version,
}).await.map_err(Error::ApiUpdateSubmissionModel)?;
},
}
Ok(())

@ -1,4 +1,4 @@
FROM oven/bun:latest
FROM registry.itzana.me/docker-proxy/oven/bun:1.2.8
WORKDIR /app
@ -10,4 +10,4 @@ ENV NEXT_TELEMETRY_DISABLED=1
RUN bun install
RUN bun run build
ENTRYPOINT ["bun", "run", "start"]
ENTRYPOINT ["bun", "run", "start"]

@ -12,7 +12,7 @@ interface SubmissionCardProps {
id: number;
}
export default function SubmissionCard(props: SubmissionCardProps) {
export function SubmissionCard(props: SubmissionCardProps) {
return (
<Link href={`/submissions/${props.id}`}>
<div className="submissionCard">
@ -40,3 +40,32 @@ export default function SubmissionCard(props: SubmissionCardProps) {
</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>
);
}

@ -13,6 +13,8 @@ interface CreatorAndReviewStatus {
creator: MapfixInfo["DisplayName"],
review: MapfixInfo["StatusID"],
status_message: MapfixInfo["StatusMessage"],
submitter: MapfixInfo["Submitter"],
target_asset_id: MapfixInfo["TargetAssetID"],
comments: Comment[],
name: string
}

@ -1,14 +0,0 @@
import { MapfixInfo } from "@/app/ts/Mapfix"
interface AssetID {
id: MapfixInfo["AssetID"]
}
function MapImage() {
return <p>Fetching map image...</p>
}
export {
type AssetID,
MapImage
}

@ -0,0 +1,31 @@
import Image from "next/image";
import { MapfixInfo } from "@/app/ts/Mapfix"
interface AssetID {
id: MapfixInfo["AssetID"]
}
function MapImage({ id }: AssetID) {
if (!id) {
return <p>Missing asset ID</p>;
}
const imageUrl = `/thumbnails/asset/${id}`;
return (
<Image
src={imageUrl}
alt="Map Thumbnail"
layout="responsive"
width={512}
height={512}
priority={true}
className="map-image"
/>
);
}
export {
type AssetID,
MapImage
}

@ -1,21 +1,38 @@
import { Roles, RolesConstants } from "@/app/ts/Roles";
import { MapfixStatus } from "@/app/ts/Mapfix";
import { Button, ButtonOwnProps } from "@mui/material";
import { useState, useEffect } from "react";
type Actions = "Completed" | "Submit" | "Reject" | "Revoke"
type ApiActions = Lowercase<Actions> | "trigger-validate" | "retry-validate" | "trigger-upload" | "reset-uploading" | "reset-validating"
type Review = Actions | "Accept" | "Validate" | "Upload" | "Reset Uploading (fix softlocked status)" | "Reset Validating (fix softlocked status)" | "Request Changes"
interface ReviewAction {
name: string,
action: string,
}
const ReviewActions = {
Submit: {name:"Submit",action:"trigger-submit"} as ReviewAction,
Revoke: {name:"Revoke",action:"revoke"} as ReviewAction,
Accept: {name:"Accept",action:"trigger-validate"} as ReviewAction,
Reject: {name:"Reject",action:"reject"} as ReviewAction,
Validate: {name:"Validate",action:"retry-validate"} as ReviewAction,
ResetValidating: {name:"Reset Validating (fix softlocked status)",action:"reset-validating"} as ReviewAction,
RequestChanges: {name:"Request Changes",action:"request-changes"} as ReviewAction,
Upload: {name:"Upload",action:"trigger-upload"} as ReviewAction,
ResetUploading: {name:"Reset Uploading (fix softlocked status)",action:"reset-uploading"} as ReviewAction,
}
interface ReviewButton {
name: Review,
action: ApiActions,
action: ReviewAction,
mapfixId: string,
color: ButtonOwnProps["color"]
}
interface ReviewId {
mapfixId: string
mapfixId: string,
mapfixStatus: number,
mapfixSubmitter: number,
}
async function ReviewButtonClicked(action: ApiActions, mapfixId: string) {
async function ReviewButtonClicked(action: string, mapfixId: string) {
try {
const response = await fetch(`/api/mapfixes/${mapfixId}/status/${action}`, {
method: "POST",
@ -41,11 +58,10 @@ function ReviewButton(props: ReviewButton) {
return <Button
color={props.color}
variant="contained"
onClick={() => { ReviewButtonClicked(props.action, props.mapfixId) }}>{props.name}</Button>
onClick={() => { ReviewButtonClicked(props.action.action, props.mapfixId) }}>{props.action.name}</Button>
}
export default function ReviewButtons(props: ReviewId) {
const mapfixId = props.mapfixId
// When is each button visible?
// Multiple buttons can be visible at once.
// Action | Role | When Current Status is One of:
@ -59,16 +75,86 @@ export default function ReviewButtons(props: ReviewId) {
// RequestChanges | Reviewer | Validated, Accepted, Submitted
// Upload | MapAdmin | Validated
// ResetUploading | MapAdmin | Uploading
const { mapfixId, mapfixStatus } = props;
const [user, setUser] = useState<number|null>(null);
const [roles, setRoles] = useState<Roles>(RolesConstants.Empty);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
try {
const [rolesData, userData] = await Promise.all([
fetch("/api/session/roles").then(rolesResponse => rolesResponse.json()),
fetch("/api/session/user").then(userResponse => userResponse.json())
]);
setRoles(rolesData.Roles);
setUser(userData.UserID);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}
}
fetchData();
}, [mapfixId]);
if (loading) return <p>Loading...</p>;
const visibleButtons: ReviewButton[] = [];
const is_submitter = user === props.mapfixSubmitter;
if (is_submitter) {
if ([MapfixStatus.UnderConstruction, MapfixStatus.ChangesRequested].includes(mapfixStatus!)) {
visibleButtons.push({ action: ReviewActions.Submit, color: "info", mapfixId });
}
if ([MapfixStatus.Submitted, MapfixStatus.ChangesRequested].includes(mapfixStatus!)) {
visibleButtons.push({ action: ReviewActions.Revoke, color: "info", mapfixId });
}
}
if (roles&RolesConstants.MapfixReview) {
// you can't review your own mapfix!
// note that this means there needs to be more than one person with MapfixReview
if (!is_submitter && mapfixStatus === MapfixStatus.Submitted) {
visibleButtons.push({ action: ReviewActions.Accept, color: "info", mapfixId });
visibleButtons.push({ action: ReviewActions.Reject, color: "error", mapfixId });
}
if (mapfixStatus === MapfixStatus.AcceptedUnvalidated) {
visibleButtons.push({ action: ReviewActions.Validate, color: "info", mapfixId });
}
if (mapfixStatus === MapfixStatus.Validating) {
visibleButtons.push({ action: ReviewActions.ResetValidating, color: "error", mapfixId });
}
// this button serves the same purpose as Revoke if you are both
// the map submitter and have MapfixReview when status is Submitted
if (
[MapfixStatus.Validated, MapfixStatus.AcceptedUnvalidated].includes(mapfixStatus!)
|| !is_submitter && mapfixStatus == MapfixStatus.Submitted
) {
visibleButtons.push({ action: ReviewActions.RequestChanges, color: "error", mapfixId });
}
}
if (roles&RolesConstants.MapfixUpload) {
if (mapfixStatus === MapfixStatus.Validated) {
visibleButtons.push({ action: ReviewActions.Upload, color: "info", mapfixId });
}
if (mapfixStatus === MapfixStatus.Uploading) {
visibleButtons.push({ action: ReviewActions.ResetUploading, color: "error", mapfixId });
}
}
return (
<section className="review-set">
<ReviewButton color="info" name="Submit" action="submit" mapfixId={mapfixId}/>
<ReviewButton color="info" name="Revoke" action="revoke" mapfixId={mapfixId}/>
<ReviewButton color="info" name="Accept" action="trigger-validate" mapfixId={mapfixId}/>
<ReviewButton color="info" name="Validate" action="retry-validate" mapfixId={mapfixId}/>
<ReviewButton color="error" name="Reject" action="reject" mapfixId={mapfixId}/>
<ReviewButton color="info" name="Upload" action="trigger-upload" mapfixId={mapfixId}/>
<ReviewButton color="error" name="Reset Uploading (fix softlocked status)" action="reset-uploading" mapfixId={mapfixId}/>
<ReviewButton color="error" name="Reset Validating (fix softlocked status)" action="reset-validating" mapfixId={mapfixId}/>
{visibleButtons.length === 0 ? (
<p>No available actions</p>
) : (
visibleButtons.map((btn) => (
<ReviewButton key={btn.action.action} {...btn} />
))
)}
</section>
)
);
}

@ -2,51 +2,38 @@
import { MapfixInfo, MapfixStatusToString } from "@/app/ts/Mapfix";
import type { CreatorAndReviewStatus } from "./_comments";
import { MapImage } from "./_map";
import { MapImage } from "./_mapImage";
import { useParams } from "next/navigation";
import ReviewButtons from "./_reviewButtons";
import { Rating } from "@mui/material";
import Comments from "./_comments";
import Webpage from "@/app/_components/webpage";
import Window from "./_window";
import Link from "next/link";
import { useState, useEffect } from "react";
import "./(styles)/page.scss";
interface ReviewId {
mapfixId: string
}
function Ratings() {
return (
<Window className="rating-window" title="Rating">
<section className="rating-type">
<aside className="rating-left">
<p>Quality</p>
<p>Difficulty</p>
<p>Fun</p>
<p>Length</p>
</aside>
<aside className="rating-right">
<Rating defaultValue={2.5} precision={0.5}/>
<Rating defaultValue={2.5} precision={0.5}/>
<Rating defaultValue={2.5} precision={0.5}/>
<Rating defaultValue={2.5} precision={0.5}/>
</aside>
</section>
</Window>
)
mapfixId: string,
mapfixStatus: number,
mapfixSubmitter: number,
mapfixAssetId: number,
mapfixTargetAssetId: number,
}
function RatingArea(mapfix: ReviewId) {
return (
<aside className="review-area">
<section className="map-image-area">
<MapImage/>
<div>
<p className="this-mapfix">This Mapfix:</p>
<MapImage id={mapfix.mapfixAssetId}/>
</div>
<div>
<p className="target-map">Target Map Being Fixed:</p>
<MapImage id={mapfix.mapfixTargetAssetId}/>
</div>
</section>
<Ratings/>
<ReviewButtons mapfixId={mapfix.mapfixId}/>
<ReviewButtons mapfixId={mapfix.mapfixId} mapfixStatus={mapfix.mapfixStatus} mapfixSubmitter={mapfix.mapfixSubmitter}/>
</aside>
)
}
@ -64,7 +51,9 @@ function TitleAndComments(stats: CreatorAndReviewStatus) {
</aside>
</div>
<p className="by-creator">by <Link href="" target="_blank">{stats.creator}</Link></p>
<p className="submitter">Submitter {stats.submitter}</p>
<p className="asset-id">Model Asset ID {stats.asset_id}</p>
<p className="target-asset-id">Target Asset ID {stats.target_asset_id}</p>
<p className="status-message">Validation Error: {stats.status_message}</p>
<span className="spacer"></span>
<Comments comments_data={stats}/>
@ -96,8 +85,8 @@ export default function MapfixInfoPage() {
<Webpage>
<main className="map-page-main">
<section className="review-section">
<RatingArea mapfixId={dynamicId.mapfixId}/>
<TitleAndComments name={mapfix.DisplayName} creator={mapfix.Creator} review={mapfix.StatusID} status_message={mapfix.StatusMessage} asset_id={mapfix.AssetID} comments={[]}/>
<RatingArea mapfixId={dynamicId.mapfixId} mapfixStatus={mapfix.StatusID} mapfixSubmitter={mapfix.Submitter} mapfixAssetId={mapfix.AssetID} mapfixTargetAssetId={mapfix.TargetAssetID} />
<TitleAndComments name={mapfix.DisplayName} creator={mapfix.Creator} review={mapfix.StatusID} status_message={mapfix.StatusMessage} asset_id={mapfix.AssetID} submitter={mapfix.Submitter} target_asset_id={mapfix.TargetAssetID} comments={[]}/>
</section>
</main>
</Webpage>

@ -1,41 +1,42 @@
'use client'
import React, { useState, useEffect } from "react";
import { MapfixInfo } from "../ts/Mapfix";
import MapfixCard from "../_components/mapCard";
import { MapfixList } from "../ts/Mapfix";
import { MapfixCard } from "../_components/mapCard";
import Webpage from "@/app/_components/webpage";
// TODO: MAKE MAPFIX & SUBMISSIONS USE THE SAME COMPONENTS :angry: (currently too lazy)
import "./(styles)/page.scss";
import { ListSortConstants } from "../ts/Sort";
export default function MapfixInfoPage() {
const [mapfixes, setMapfixes] = useState<MapfixInfo[]>([])
const [currentPage, setCurrentPage] = useState(0);
const [mapfixes, setMapfixes] = useState<MapfixList>({Total:0,Mapfixes:[]})
const [currentPage, setCurrentPage] = useState(1);
const cardsPerPage = 24; // built to fit on a 1920x1080 monitor
const totalPages = Math.ceil(mapfixes.length / cardsPerPage);
const totalPages = Math.ceil(mapfixes.Total / cardsPerPage);
const currentCards = mapfixes.slice(
currentPage * cardsPerPage,
(currentPage + 1) * cardsPerPage
const currentCards = mapfixes.Mapfixes.slice(
(currentPage - 1) * cardsPerPage,
currentPage * cardsPerPage
);
const nextPage = () => {
if (currentPage < totalPages - 1) {
if (currentPage < totalPages) {
setCurrentPage(currentPage + 1);
}
};
const prevPage = () => {
if (currentPage > 0) {
if (currentPage > 1) {
setCurrentPage(currentPage - 1);
}
};
useEffect(() => {
async function fetchMapfixes() {
const res = await fetch('/api/mapfixes?Page=1&Limit=100')
const res = await fetch(`/api/mapfixes?Page=${currentPage}&Limit=${cardsPerPage}&Sort=${ListSortConstants.ListSortDateDescending}`)
if (res.ok) {
setMapfixes(await res.json())
}
@ -44,7 +45,7 @@ export default function MapfixInfoPage() {
setTimeout(() => {
fetchMapfixes()
}, 50);
}, [])
}, [currentPage])
if (!mapfixes) {
return <Webpage>
@ -54,7 +55,7 @@ export default function MapfixInfoPage() {
</Webpage>
}
if (mapfixes && mapfixes.length == 0) {
if (mapfixes && mapfixes.Total == 0) {
return <Webpage>
<main>
Mapfixes list is empty.
@ -82,17 +83,17 @@ export default function MapfixInfoPage() {
{Array.from({ length: totalPages }).map((_, index) => (
<span
key={index}
className={`dot ${index === currentPage ? 'active' : ''}`}
onClick={() => setCurrentPage(index)}
className={`dot ${index+1 === currentPage ? 'active' : ''}`}
onClick={() => setCurrentPage(index+1)}
></span>
))}
</div>
<div className="pagination">
<button onClick={prevPage} disabled={currentPage === 0}>&lt;</button>
<button onClick={prevPage} disabled={currentPage === 1}>&lt;</button>
<span>
Page {currentPage + 1} of {totalPages}
Page {currentPage} of {totalPages}
</span>
<button onClick={nextPage} disabled={currentPage === totalPages - 1}>&gt;</button>
<button onClick={nextPage} disabled={currentPage === totalPages}>&gt;</button>
</div>
<div className="grid">
{currentCards.map((mapfix) => (

@ -0,0 +1,28 @@
import Image from "next/image";
import { MapInfo } from "@/app/ts/Map";
interface AssetID {
id: MapInfo["ID"];
}
function MapImage({ id }: AssetID) {
if (!id) {
return <p>Missing asset ID</p>;
}
const imageUrl = `/thumbnails/asset/${id}`;
return (
<Image
src={imageUrl}
alt="Map Thumbnail"
layout="responsive"
width={512}
height={512}
priority={true}
className="map-image"
/>
);
}
export { type AssetID, MapImage };

@ -13,7 +13,7 @@ interface MapfixPayload {
TargetAssetID: number;
}
interface IdResponse {
ID: number;
OperationID: number;
}
export default function MapfixInfoPage() {
@ -53,7 +53,7 @@ export default function MapfixInfoPage() {
const id_response:IdResponse = await response.json();
// navigate to newly created mapfix
window.location.assign(`/mapfixes/${id_response.ID}`)
window.location.assign(`/operations/${id_response.OperationID}`)
} catch (error) {
console.error("Error submitting data:", error);

@ -1,14 +1,103 @@
"use client"
import { MapInfo } from "@/app/ts/Map";
import { MapImage } from "./_mapImage";
import Webpage from "@/app/_components/webpage";
import { useParams } from "next/navigation";
import { useState, useEffect } from "react";
import Link from "next/link";
// MUI Components
import {
Typography,
Box,
Button as MuiButton,
Card,
CardContent,
Skeleton,
ThemeProvider,
createTheme,
CssBaseline
} from "@mui/material";
interface ButtonProps {
name: string;
href: string;
}
function Button({ name, href }: ButtonProps) {
return (
<Link href={href} passHref>
<MuiButton variant="contained" color="primary" sx={{ mt: 2 }}>
{name}
</MuiButton>
</Link>
);
}
const darkTheme = createTheme({
palette: {
mode: "dark",
},
});
export default function Map() {
const { mapId } = useParams<{mapId: string}>()
const { mapId } = useParams();
const [map, setMap] = useState<MapInfo | null>(null);
return (
<Webpage>
<p>map { mapId }</p>
</Webpage>
);
useEffect(() => {
async function getMap() {
const res = await fetch(`/api/maps/${mapId}`);
if (res.ok) {
setMap(await res.json());
}
}
getMap();
}, [mapId]);
return (
<ThemeProvider theme={darkTheme}>
<CssBaseline />
<Webpage>
{!map ? (
<Card>
<CardContent>
<Skeleton variant="text" width={200} height={40} />
<Skeleton variant="text" width={300} />
<Skeleton variant="rectangular" height={200} />
</CardContent>
</Card>
) : (
<Box display="flex" flexDirection={{ xs: "column", md: "row" }} gap={4}>
<Box flex={1}>
<Card>
<CardContent>
<Typography variant="h5" gutterBottom>
Map Info
</Typography>
<Typography variant="body1"><strong>Map ID:</strong> {mapId}</Typography>
<Typography variant="body1"><strong>Display Name:</strong> {map.DisplayName}</Typography>
<Typography variant="body1"><strong>Creator:</strong> {map.Creator}</Typography>
<Typography variant="body1"><strong>Game ID:</strong> {map.GameID}</Typography>
<Typography variant="body1"><strong>Release Date:</strong> {new Date(map.Date * 1000).toLocaleString()}</Typography>
<Button name="Submit A Mapfix For This Map" href={`/maps/${mapId}/fix`} />
</CardContent>
</Card>
</Box>
<Box flex={1}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Map Preview
</Typography>
<MapImage id={map.ID} />
</CardContent>
</Card>
</Box>
</Box>
)}
</Webpage>
</ThemeProvider>
);
}

@ -1,6 +1,6 @@
"use client";
import { useEffect, useState } from "react";
import { useEffect, useState, useRef } from "react";
import { useParams, useRouter } from "next/navigation";
import { CircularProgress, Typography, Card, CardContent, Button } from "@mui/material";
import Webpage from "@/app/_components/webpage";
@ -17,23 +17,31 @@ interface Operation {
}
export default function OperationStatusPage() {
const { operationId } = useParams();
const router = useRouter();
const [operation, setOperation] = useState<Operation | null>(null);
const { operationId } = useParams();
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [operation, setOperation] = useState<Operation | null>(null);
const intervalRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
if (!operationId) return;
const fetchOperation = async () => {
try {
const response = await fetch(`/api/operations/${operationId}`);
if (!response.ok) throw new Error("Failed to fetch operation");
const data = await response.json();
const data: Operation = await response.json();
setOperation(data);
if (data.Status !== 0 && intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
} catch (err: unknown) {
if (err instanceof Error) {
setError(err.message);
@ -44,39 +52,34 @@ export default function OperationStatusPage() {
setLoading(false);
}
};
fetchOperation();
const interval = setInterval(fetchOperation, 5000);
return () => clearInterval(interval);
if (!intervalRef.current) {
intervalRef.current = setInterval(fetchOperation, 1000);
}
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, [operationId]);
const getStatusClass = (status: number) => {
switch (status) {
case 0:
return "created";
case 1:
return "completed";
case 2:
return "failed";
default:
return "";
}
};
const getStatusText = (status: number) => {
switch (status) {
case 0:
return "Created";
case 1:
return "Completed";
case 2:
return "Failed";
default:
return "Unknown";
case 0:
return "Created";
case 1:
return "Completed";
case 2:
return "Failed";
default:
return "Unknown";
}
};
const getStatusClass = (status: number) => getStatusText(status).toLowerCase();
return (
<Webpage>
<main className="operation-status">
@ -96,13 +99,13 @@ export default function OperationStatusPage() {
<Typography>Owner: {operation.Owner}</Typography>
<Typography>Date: {new Date(operation.Date * 1000).toLocaleString()}</Typography>
<Typography>Path: {operation.Path}</Typography>
{operation.Status === 1 && (
<div className="submission-button">
<Button
variant="contained"
color="success"
onClick={() => router.push(`/submissions/${operation.OperationID}`)}
onClick={() => router.push(operation.Path)}
>
View Submission
</Button>
@ -116,4 +119,4 @@ export default function OperationStatusPage() {
</main>
</Webpage>
);
}
}

@ -13,6 +13,8 @@ interface CreatorAndReviewStatus {
creator: SubmissionInfo["DisplayName"],
review: SubmissionInfo["StatusID"],
status_message: SubmissionInfo["StatusMessage"],
submitter: SubmissionInfo["Submitter"],
uploaded_asset_id: SubmissionInfo["UploadedAssetID"],
comments: Comment[],
name: string
}

@ -1,21 +1,38 @@
import { Roles, RolesConstants } from "@/app/ts/Roles";
import { SubmissionStatus } from "@/app/ts/Submission";
import { Button, ButtonOwnProps } from "@mui/material";
import { useState, useEffect } from "react";
type Actions = "Completed" | "Submit" | "Reject" | "Revoke"
type ApiActions = Lowercase<Actions> | "trigger-validate" | "retry-validate" | "trigger-upload" | "reset-uploading" | "reset-validating"
type Review = Actions | "Accept" | "Validate" | "Upload" | "Reset Uploading (fix softlocked status)" | "Reset Validating (fix softlocked status)" | "Request Changes"
interface ReviewAction {
name: string,
action: string,
}
const ReviewActions = {
Submit: {name:"Submit",action:"trigger-submit"} as ReviewAction,
Revoke: {name:"Revoke",action:"revoke"} as ReviewAction,
Accept: {name:"Accept",action:"trigger-validate"} as ReviewAction,
Reject: {name:"Reject",action:"reject"} as ReviewAction,
Validate: {name:"Validate",action:"retry-validate"} as ReviewAction,
ResetValidating: {name:"Reset Validating (fix softlocked status)",action:"reset-validating"} as ReviewAction,
RequestChanges: {name:"Request Changes",action:"request-changes"} as ReviewAction,
Upload: {name:"Upload",action:"trigger-upload"} as ReviewAction,
ResetUploading: {name:"Reset Uploading (fix softlocked status)",action:"reset-uploading"} as ReviewAction,
}
interface ReviewButton {
name: Review,
action: ApiActions,
action: ReviewAction,
submissionId: string,
color: ButtonOwnProps["color"]
}
interface ReviewId {
submissionId: string
submissionId: string,
submissionStatus: number,
submissionSubmitter: number,
}
async function ReviewButtonClicked(action: ApiActions, submissionId: string) {
async function ReviewButtonClicked(action: string, submissionId: string) {
try {
const response = await fetch(`/api/submissions/${submissionId}/status/${action}`, {
method: "POST",
@ -41,11 +58,10 @@ function ReviewButton(props: ReviewButton) {
return <Button
color={props.color}
variant="contained"
onClick={() => { ReviewButtonClicked(props.action, props.submissionId) }}>{props.name}</Button>
onClick={() => { ReviewButtonClicked(props.action.action, props.submissionId) }}>{props.action.name}</Button>
}
export default function ReviewButtons(props: ReviewId) {
const submissionId = props.submissionId
// When is each button visible?
// Multiple buttons can be visible at once.
// Action | Role | When Current Status is One of:
@ -59,16 +75,87 @@ export default function ReviewButtons(props: ReviewId) {
// RequestChanges | Reviewer | Validated, Accepted, Submitted
// Upload | MapAdmin | Validated
// ResetUploading | MapAdmin | Uploading
const { submissionId, submissionStatus } = props;
const [user, setUser] = useState<number|null>(null);
const [roles, setRoles] = useState<Roles>(RolesConstants.Empty);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
try {
const [rolesData, userData] = await Promise.all([
fetch("/api/session/roles").then(rolesResponse => rolesResponse.json()),
fetch("/api/session/user").then(userResponse => userResponse.json())
]);
setRoles(rolesData.Roles);
setUser(userData.UserID);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}
}
fetchData();
}, [submissionId]);
if (loading) return <p>Loading...</p>;
const visibleButtons: ReviewButton[] = [];
const is_submitter = user === props.submissionSubmitter;
if (is_submitter) {
if ([SubmissionStatus.UnderConstruction, SubmissionStatus.ChangesRequested].includes(submissionStatus!)) {
visibleButtons.push({ action: ReviewActions.Submit, color: "info", submissionId });
}
if ([SubmissionStatus.Submitted, SubmissionStatus.ChangesRequested].includes(submissionStatus!)) {
visibleButtons.push({ action: ReviewActions.Revoke, color: "info", submissionId });
}
}
if (roles&RolesConstants.SubmissionReview) {
// you can't review your own submission!
// note that this means there needs to be more than one person with SubmissionReview
if (!is_submitter && submissionStatus === SubmissionStatus.Submitted) {
visibleButtons.push({ action: ReviewActions.Accept, color: "info", submissionId });
visibleButtons.push({ action: ReviewActions.Reject, color: "error", submissionId });
}
if (submissionStatus === SubmissionStatus.AcceptedUnvalidated) {
visibleButtons.push({ action: ReviewActions.Validate, color: "info", submissionId });
}
if (submissionStatus === SubmissionStatus.Validating) {
visibleButtons.push({ action: ReviewActions.ResetValidating, color: "error", submissionId });
}
// this button serves the same purpose as Revoke if you are both
// the map submitter and have SubmissionReview when status is Submitted
if (
[SubmissionStatus.Validated, SubmissionStatus.AcceptedUnvalidated].includes(submissionStatus!)
|| !is_submitter && submissionStatus == SubmissionStatus.Submitted
) {
visibleButtons.push({ action: ReviewActions.RequestChanges, color: "error", submissionId });
}
}
if (roles&RolesConstants.SubmissionUpload) {
if (submissionStatus === SubmissionStatus.Validated) {
visibleButtons.push({ action: ReviewActions.Upload, color: "info", submissionId });
}
// TODO: hide Reset buttons for 10 seconds
if (submissionStatus === SubmissionStatus.Uploading) {
visibleButtons.push({ action: ReviewActions.ResetUploading, color: "error", submissionId });
}
}
return (
<section className="review-set">
<ReviewButton color="info" name="Submit" action="submit" submissionId={submissionId}/>
<ReviewButton color="info" name="Revoke" action="revoke" submissionId={submissionId}/>
<ReviewButton color="info" name="Accept" action="trigger-validate" submissionId={submissionId}/>
<ReviewButton color="info" name="Validate" action="retry-validate" submissionId={submissionId}/>
<ReviewButton color="error" name="Reject" action="reject" submissionId={submissionId}/>
<ReviewButton color="info" name="Upload" action="trigger-upload" submissionId={submissionId}/>
<ReviewButton color="error" name="Reset Uploading (fix softlocked status)" action="reset-uploading" submissionId={submissionId}/>
<ReviewButton color="error" name="Reset Validating (fix softlocked status)" action="reset-validating" submissionId={submissionId}/>
{visibleButtons.length === 0 ? (
<p>No available actions</p>
) : (
visibleButtons.map((btn) => (
<ReviewButton key={btn.action.action} {...btn} />
))
)}
</section>
)
);
}

@ -5,10 +5,8 @@ import type { CreatorAndReviewStatus } from "./_comments";
import { MapImage } from "./_mapImage";
import { useParams } from "next/navigation";
import ReviewButtons from "./_reviewButtons";
import { Rating } from "@mui/material";
import Comments from "./_comments";
import Webpage from "@/app/_components/webpage";
import Window from "./_window";
import Link from "next/link";
import { useState, useEffect } from "react";
@ -17,27 +15,8 @@ import "./(styles)/page.scss";
interface ReviewId {
submissionId: string;
assetId: number;
}
function Ratings() {
return (
<Window className="rating-window" title="Rating">
<section className="rating-type">
<aside className="rating-left">
<p>Quality</p>
<p>Difficulty</p>
<p>Fun</p>
<p>Length</p>
</aside>
<aside className="rating-right">
<Rating defaultValue={2.5} precision={0.5}/>
<Rating defaultValue={2.5} precision={0.5}/>
<Rating defaultValue={2.5} precision={0.5}/>
<Rating defaultValue={2.5} precision={0.5}/>
</aside>
</section>
</Window>
)
submissionStatus: number;
submissionSubmitter: number,
}
function RatingArea(submission: ReviewId) {
@ -46,8 +25,7 @@ function RatingArea(submission: ReviewId) {
<section className="map-image-area">
<MapImage id={submission.assetId}/>
</section>
<Ratings/>
<ReviewButtons submissionId={submission.submissionId}/>
<ReviewButtons submissionId={submission.submissionId} submissionStatus={submission.submissionStatus} submissionSubmitter={submission.submissionSubmitter}/>
</aside>
)
}
@ -65,7 +43,9 @@ function TitleAndComments(stats: CreatorAndReviewStatus) {
</aside>
</div>
<p className="by-creator">by <Link href="" target="_blank">{stats.creator}</Link></p>
<p className="submitter">Submitter {stats.submitter}</p>
<p className="asset-id">Model Asset ID {stats.asset_id}</p>
<p className="uploaded-asset-id">Uploaded Asset ID {stats.uploaded_asset_id}</p>
<p className="status-message">Validation Error: {stats.status_message}</p>
<span className="spacer"></span>
<Comments comments_data={stats}/>
@ -97,8 +77,8 @@ export default function SubmissionInfoPage() {
<Webpage>
<main className="map-page-main">
<section className="review-section">
<RatingArea assetId={submission.AssetID} submissionId={dynamicId.submissionId}/>
<TitleAndComments name={submission.DisplayName} creator={submission.Creator} review={submission.StatusID} status_message={submission.StatusMessage} asset_id={submission.AssetID} comments={[]}/>
<RatingArea assetId={submission.AssetID} submissionId={dynamicId.submissionId} submissionStatus={submission.StatusID} submissionSubmitter={submission.Submitter}/>
<TitleAndComments name={submission.DisplayName} creator={submission.Creator} review={submission.StatusID} status_message={submission.StatusMessage} asset_id={submission.AssetID} submitter={submission.Submitter} uploaded_asset_id={submission.UploadedAssetID} comments={[]}/>
</section>
</main>
</Webpage>

@ -1,39 +1,40 @@
'use client'
import React, { useState, useEffect } from "react";
import { SubmissionInfo } from "../ts/Submission";
import SubmissionCard from "../_components/mapCard";
import { SubmissionList } from "../ts/Submission";
import { SubmissionCard } from "../_components/mapCard";
import Webpage from "@/app/_components/webpage";
import "./(styles)/page.scss";
import { ListSortConstants } from "../ts/Sort";
export default function SubmissionInfoPage() {
const [submissions, setSubmissions] = useState<SubmissionInfo[]>([])
const [currentPage, setCurrentPage] = useState(0);
const [submissions, setSubmissions] = useState<SubmissionList>({Total:0,Submissions:[]})
const [currentPage, setCurrentPage] = useState(1);
const cardsPerPage = 24; // built to fit on a 1920x1080 monitor
const totalPages = Math.ceil(submissions.length / cardsPerPage);
const totalPages = Math.ceil(submissions.Total / cardsPerPage);
const currentCards = submissions.slice(
currentPage * cardsPerPage,
(currentPage + 1) * cardsPerPage
const currentCards = submissions.Submissions.slice(
(currentPage - 1) * cardsPerPage,
currentPage * cardsPerPage
);
const nextPage = () => {
if (currentPage < totalPages - 1) {
if (currentPage < totalPages) {
setCurrentPage(currentPage + 1);
}
};
const prevPage = () => {
if (currentPage > 0) {
if (currentPage > 1) {
setCurrentPage(currentPage - 1);
}
};
useEffect(() => {
async function fetchSubmissions() {
const res = await fetch('/api/submissions?Page=1&Limit=100')
const res = await fetch(`/api/submissions?Page=${currentPage}&Limit=${cardsPerPage}&Sort=${ListSortConstants.ListSortDateDescending}`)
if (res.ok) {
setSubmissions(await res.json())
}
@ -42,7 +43,7 @@ export default function SubmissionInfoPage() {
setTimeout(() => {
fetchSubmissions()
}, 50);
}, [])
}, [currentPage])
if (!submissions) {
return <Webpage>
@ -52,7 +53,7 @@ export default function SubmissionInfoPage() {
</Webpage>
}
if (submissions && submissions.length == 0) {
if (submissions && submissions.Total == 0) {
return <Webpage>
<main>
Submissions list is empty.
@ -80,17 +81,17 @@ export default function SubmissionInfoPage() {
{Array.from({ length: totalPages }).map((_, index) => (
<span
key={index}
className={`dot ${index === currentPage ? 'active' : ''}`}
onClick={() => setCurrentPage(index)}
className={`dot ${index+1 === currentPage ? 'active' : ''}`}
onClick={() => setCurrentPage(index+1)}
></span>
))}
</div>
<div className="pagination">
<button onClick={prevPage} disabled={currentPage === 0}>&lt;</button>
<button onClick={prevPage} disabled={currentPage === 1}>&lt;</button>
<span>
Page {currentPage + 1} of {totalPages}
Page {currentPage} of {totalPages}
</span>
<button onClick={nextPage} disabled={currentPage === totalPages - 1}>&gt;</button>
<button onClick={nextPage} disabled={currentPage === totalPages}>&gt;</button>
</div>
<div className="grid">
{currentCards.map((submission) => (

11
web/src/app/ts/Map.ts Normal file

@ -0,0 +1,11 @@
interface MapInfo {
readonly ID: number,
readonly DisplayName: string,
readonly Creator: string,
readonly GameID: number,
readonly Date: number,
}
export {
type MapInfo
}

@ -1,13 +1,15 @@
const enum MapfixStatus {
UnderConstruction = 0,
Submitted = 1,
ChangesRequested = 2,
Accepted = 3,
Validating = 4,
Validated = 5,
Uploading = 6,
Uploaded = 7,
Rejected = 8,
UnderConstruction = 0,
ChangesRequested = 1,
Submitting = 2,
Submitted = 3,
AcceptedUnvalidated = 4,
Validating = 5,
Validated = 6,
Uploading = 7,
Uploaded = 8,
Rejected = 9,
// MapfixStatus does not have a Released state
}
interface MapfixInfo {
@ -27,10 +29,15 @@ interface MapfixInfo {
readonly StatusMessage: string,
}
interface MapfixList {
readonly Total: number,
readonly Mapfixes: MapfixInfo[],
}
function MapfixStatusToString(mapfix_status: MapfixStatus): string {
switch (mapfix_status) {
case MapfixStatus.Rejected:
return "REJECTED"
case MapfixStatus.Rejected:
return "REJECTED"
case MapfixStatus.Uploading:
return "UPLOADING"
case MapfixStatus.Uploaded:
@ -39,12 +46,14 @@ function MapfixStatusToString(mapfix_status: MapfixStatus): string {
return "VALIDATED"
case MapfixStatus.Validating:
return "VALIDATING"
case MapfixStatus.Accepted:
return "ACCEPTED"
case MapfixStatus.AcceptedUnvalidated:
return "ACCEPTED, NOT VALIDATED"
case MapfixStatus.ChangesRequested:
return "CHANGES REQUESTED"
case MapfixStatus.Submitted:
return "SUBMITTED"
case MapfixStatus.Submitting:
return "SUBMITTING"
case MapfixStatus.UnderConstruction:
return "UNDER CONSTRUCTION"
default:
@ -55,5 +64,6 @@ function MapfixStatusToString(mapfix_status: MapfixStatus): string {
export {
MapfixStatus,
MapfixStatusToString,
type MapfixInfo
type MapfixInfo,
type MapfixList,
}

25
web/src/app/ts/Roles.ts Normal file

@ -0,0 +1,25 @@
type Roles = number;
// Constants
const RolesConstants = {
All: -1 as Roles,
SubmissionUpload: 1 << 6 as Roles,
SubmissionReview: 1 << 5 as Roles,
SubmissionRelease: 1 << 4 as Roles,
ScriptWrite: 1 << 3 as Roles,
MapfixUpload: 1 << 2 as Roles,
MapfixReview: 1 << 1 as Roles,
MapDownload: 1 << 0 as Roles,
Empty: 0 as Roles,
};
// Operations
function hasRole(flags: Roles, role: Roles): boolean {
return (flags & role) === role;
}
export {
type Roles,
RolesConstants,
hasRole,
};

15
web/src/app/ts/Sort.ts Normal file

@ -0,0 +1,15 @@
type ListSort = number;
// Constants
const ListSortConstants = {
ListSortDisabled: 0,
ListSortDisplayNameAscending: 1,
ListSortDisplayNameDescending: 2,
ListSortDateAscending: 3,
ListSortDateDescending: 4,
};
export {
type ListSort,
ListSortConstants,
};

@ -1,14 +1,15 @@
const enum SubmissionStatus {
UnderConstruction = 0,
Submitted = 1,
ChangesRequested = 2,
Accepted = 3,
Validating = 4,
Validated = 5,
Uploading = 6,
Uploaded = 7,
Rejected = 8,
Released = 9,
UnderConstruction = 0,
ChangesRequested = 1,
Submitting = 2,
Submitted = 3,
AcceptedUnvalidated = 4,
Validating = 5,
Validated = 6,
Uploading = 7,
Uploaded = 8,
Rejected = 9,
Released = 10,
}
interface SubmissionInfo {
@ -28,12 +29,17 @@ interface SubmissionInfo {
readonly StatusMessage: string,
}
interface SubmissionList {
readonly Total: number,
readonly Submissions: SubmissionInfo[],
}
function SubmissionStatusToString(submission_status: SubmissionStatus): string {
switch (submission_status) {
case SubmissionStatus.Released:
return "RELEASED"
case SubmissionStatus.Rejected:
return "REJECTED"
case SubmissionStatus.Released:
return "RELEASED"
case SubmissionStatus.Rejected:
return "REJECTED"
case SubmissionStatus.Uploading:
return "UPLOADING"
case SubmissionStatus.Uploaded:
@ -42,12 +48,14 @@ function SubmissionStatusToString(submission_status: SubmissionStatus): string {
return "VALIDATED"
case SubmissionStatus.Validating:
return "VALIDATING"
case SubmissionStatus.Accepted:
return "ACCEPTED"
case SubmissionStatus.AcceptedUnvalidated:
return "ACCEPTED, NOT VALIDATED"
case SubmissionStatus.ChangesRequested:
return "CHANGES REQUESTED"
case SubmissionStatus.Submitted:
return "SUBMITTED"
case SubmissionStatus.Submitting:
return "SUBMITTING"
case SubmissionStatus.UnderConstruction:
return "UNDER CONSTRUCTION"
default:
@ -58,5 +66,6 @@ function SubmissionStatusToString(submission_status: SubmissionStatus): string {
export {
SubmissionStatus,
SubmissionStatusToString,
type SubmissionInfo
type SubmissionInfo,
type SubmissionList,
}