150 Commits

Author SHA1 Message Date
d66bd9b112 submissions: introduce special roles to create submissions you do not own
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-02 13:06:16 -07:00
3bda4803aa submissions: refine roles
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2025-04-01 16:21:48 -07:00
c401d24366 submissions: fix mapfixes auto migrate
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-04-01 15:27:05 -07:00
a119c4292e web: change submit text to match mapfix submit page
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-04-01 15:07:25 -07:00
4cf7889db9 web: add submit page at /maps/[mapId]/fix 2025-04-01 15:07:25 -07:00
146d627534 web: mapfixes: rename all occurrences of submission with mapfix
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-01 14:45:21 -07:00
97180ab263 web: clone submissions page for mapfixes 2025-04-01 14:44:42 -07:00
37560ac5d2 submissions: reintroduce mapfix fields
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-04-01 14:34:23 -07:00
de8f869b5b openapi: generate 2025-04-01 14:34:23 -07:00
b6ae600a93 openapi: reintroduce mapfix fields 2025-04-01 14:34:20 -07:00
96ace736f4 validator: add mapfix capability
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-04-01 13:51:40 -07:00
9dd7a41d8f submissions-api: add simple mapfixes endpoints 2025-04-01 13:51:40 -07:00
cc7df064be submissions-api: deduplicate simple endpoints with crazy macro 2025-04-01 13:51:37 -07:00
732598266c submissions: mapfixes
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-01 13:44:42 -07:00
6d420c3a82 openapi: generate 2025-04-01 13:44:06 -07:00
2e65d071e0 openapi: mapfixes 2025-04-01 13:43:59 -07:00
e36b49a31e submissions: datastore: duplicate submissions as mapfixes 2025-04-01 13:34:23 -07:00
1d7f6ea79a nats: rename types
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-04-01 13:33:23 -07:00
b0f1e42a06 web: fix SubmissionInfo type
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-31 19:57:48 -07:00
8925d71bcd submissions: fix compile
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-31 19:42:57 -07:00
8de5bcba68 openapi: generate
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2025-03-31 19:34:05 -07:00
a048d713da openapi: missing fields 2025-03-31 19:33:28 -07:00
581c65594d openapi: generate
Some checks failed
continuous-integration/drone/push Build is running
continuous-integration/drone/pr Build is failing
2025-03-31 19:31:05 -07:00
4e22933e34 openapi: fix /scripts endpoint 2025-03-31 19:31:05 -07:00
758c2254eb submissions-api: create new error variant
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-31 19:23:59 -07:00
ade54ee662 validator: refactor + remove mapfix capability 2025-03-31 19:23:59 -07:00
01785bb190 validator: move files 2025-03-31 18:14:08 -07:00
8366b84d90 submissions: tweak script data model
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-31 18:13:31 -07:00
746c7aa9b7 openapi: generate 2025-03-31 18:09:50 -07:00
930eb47096 openapi: tweak script data model 2025-03-31 18:09:25 -07:00
9671c357f4 submissions: ruin script data model 2025-03-31 18:06:39 -07:00
3404251c14 nats: rename events
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-31 16:51:36 -07:00
ffcba57408 validation: tweak validator-uploaded endpoint 2025-03-31 16:27:42 -07:00
a60ccd22f0 submissions: remove prints 2025-03-31 16:27:42 -07:00
f7d7a0891d submissions: tweak validator-uploaded endpoint 2025-03-31 16:27:42 -07:00
e5e85db1fd openapi-internal: generate 2025-03-31 16:27:42 -07:00
0b64440975 openapi-internal: tweak validator-uploaded endpoint 2025-03-31 16:27:42 -07:00
0e29ca98dd web: remove TargetAssetID 2025-03-31 16:27:42 -07:00
9740cbe91a openapi: generate 2025-03-31 16:27:42 -07:00
2d2691b551 openapi: tweak Submission fields 2025-03-31 16:27:42 -07:00
dfc2a605f4 submissions: prepare for separate mapfixes 2025-03-31 16:27:42 -07:00
88c3866654 Revert "submissions: add AcceptedBy, UploadedBy fields to model"
This reverts commit 4c17a3c9e9.
2025-03-31 14:39:06 -07:00
92226e768d submissions: allow map council to upload maps
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-28 23:57:34 -07:00
4515eb6da2 submissions: typo in error variable names
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-28 23:55:07 -07:00
f2d8c49647 submissions: move pipeline restriction to accept rather than create
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-28 19:10:29 -07:00
2c75cfa67f submissions: remove StatusUploaded from ActiveSubmissionStatuses 2025-03-28 19:00:26 -07:00
f3689f4916 rename part 2: rename all occurrences of "publish" to "upload"
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-28 15:56:47 -07:00
e855ace229 rename part 1: move files 2025-03-28 15:56:47 -07:00
6e21447d4b validation: update deps 2025-03-28 15:56:47 -07:00
49fea314ec submissions: log accepter and uploader
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-28 22:34:51 +00:00
4c17a3c9e9 submissions: add AcceptedBy, UploadedBy fields to model 2025-03-28 22:34:51 +00:00
a7784bdaf5 web: fix api types
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is running
2025-03-28 18:26:59 -04:00
f0e18a5963 web: Validate button calls retry-validate endpoint
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2025-03-28 15:07:07 -07:00
661fa17fa7 submissions: implement ActionSubmissionRetryValidate 2025-03-28 15:01:22 -07:00
cc1d5f4bda submissions: remove Accepted from valid src status in ActionSubmissionTriggerValidate 2025-03-28 15:01:09 -07:00
e7a66ebe0d openapi: generate 2025-03-28 14:48:48 -07:00
977f3902b7 openapi: split trigger-validate into two cases 2025-03-28 14:48:27 -07:00
af9f413b49 validation: refactor get_partial_path
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-27 22:53:39 +00:00
b02b3d205e Switch to using /api/session/validate for determining if the user is not logged in (#34)
All checks were successful
continuous-integration/drone/push Build is passing
My apologies for being stupid not knowing the NextJS framework fully, as I have little experience with it and its non intuitive SSR and CSR workflow

Code successfully built locally running `bun run build`

Reviewed-on: #34
Co-authored-by: rhpidfyre <brandon@rhpidfyre.io>
Co-committed-by: rhpidfyre <brandon@rhpidfyre.io>
2025-03-27 21:47:03 +00:00
2f2241612a openapi: generate
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-26 20:53:25 -07:00
a7c72163eb openapi: user session is required for SessionValidate 2025-03-26 20:53:02 -07:00
c8077482f3 submissions: do not validate session in HandleCookieAuth 2025-03-26 20:53:02 -07:00
79c21b62d8 openapi: generate
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-26 20:17:03 -07:00
032f0e8739 openapi: opt out of security for get requests 2025-03-26 20:16:44 -07:00
251a24efae web: Revert auth redirect
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-26 17:23:21 -07:00
a9afdf38cf web: auth redirect fix
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-26 16:27:32 -07:00
d3edb6b3da validation: include script path in ScriptFlaggedIllegalKeyword
Some checks failed
continuous-integration/drone/push Build is running
continuous-integration/drone/pr Build is failing
2025-03-26 16:23:44 -07:00
188fbd2a6d submissions: rename VersionID to ValidatedModelVersion
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2025-03-26 15:41:46 -07:00
1468a9edc2 openapi: generate 2025-03-26 15:41:19 -07:00
1053719eab openapi: rename field 2025-03-26 15:40:57 -07:00
2867da4b21 submissions: detect sentinel value
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2025-03-26 15:33:47 -07:00
85a144e276 submissions-api: v0.6.1
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-26 14:58:28 -07:00
4227f18992 validator: name model correctly
Some checks failed
continuous-integration/drone/push Build is failing
2025-03-26 21:30:12 +00:00
123bc8af47 validator: write and use tragic script name function 2025-03-26 21:30:12 +00:00
cd82954b73 validator: refactor errors to improve information and clarity 2025-03-26 21:30:12 +00:00
ce08b57e18 submissions-api: include get_scripts & get_script_from_hash in internal api 2025-03-26 21:30:12 +00:00
1ca0348924 submissions-api: derive Clone, Debug on many types 2025-03-26 21:30:12 +00:00
936a1f93aa web: use --turbopack for dev
Some checks are pending
continuous-integration/drone/push Build is running
2025-03-26 21:29:07 +00:00
d5d0e5ffc9 web: redirect if the user is not logged in based on session_id cookie's presence 2025-03-26 21:29:07 +00:00
039309c75a submissions: include status message
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-26 13:08:56 -07:00
7cc0b5da7f openapi: generate 2025-03-26 13:08:41 -07:00
f0c44fb4a8 openapi: include status message 2025-03-26 13:08:22 -07:00
4fec1bba47 validation: do not implicitly append url
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-26 12:53:35 -07:00
5ae287f3f2 docker: fix API_HOST
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-26 12:46:54 -07:00
bf6c8af21a docker: add group id env var
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-26 12:34:27 -07:00
65e63431a3 docker: use staging auth image
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-26 12:26:37 -07:00
a8dc6cd35a submissions: introduce new role SubmissionRelease
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-26 12:07:06 -07:00
539e09fe06 validator: correct enum item name 2025-03-26 12:07:06 -07:00
87fd7adb93 submissions: rename SubmissionPublish to SubmissionUpload 2025-03-26 12:07:06 -07:00
7d57d1ac4d submissions: improve error granularity 2025-03-26 12:07:06 -07:00
636bb1fb94 submissions: fix roles bug
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-25 19:42:39 -07:00
295ec3cd8b submissions: refactor UserInfoHandle.GetRoles
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-25 19:32:48 -07:00
6af006f802 fix docker compose
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-25 19:27:26 -07:00
d16bb8ad02 submissions: refactor roles again
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-25 18:07:37 -07:00
1af7d7e941 submissions: implement session endpoints
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-25 17:53:50 -07:00
1feca92f7d submissions: add UserInfoHandle.Validate 2025-03-25 17:44:49 -07:00
7213948a26 submissions: add UserInfoHandle.GetUserInfo function 2025-03-25 17:44:49 -07:00
783d0e843c submissions: refactor roles 2025-03-25 17:44:13 -07:00
977d1d20c2 submissions: rename UserInfo to UserInfoHandle 2025-03-25 17:44:13 -07:00
d7634de9ec openapi: generate 2025-03-25 17:44:13 -07:00
8da1c9346b openapi: add session endpoints 2025-03-25 17:44:08 -07:00
894851c0e8 openapi: fix operation summary
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-03-25 16:35:50 -07:00
3da4023466 web: throw error on failure status (#16)
All checks were successful
continuous-integration/drone/push Build is passing
Thanks to ai for knowing javascript

Co-authored-by: rhpidfyre <brandon@rhpidfyre.io>
Reviewed-on: #16
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-03-24 23:03:53 +00:00
08a4e913a9 Enable auto deploy
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-23 19:13:36 -04:00
6748cb4324 submissions: submitter cannot accept their own submission
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-19 18:12:18 -07:00
73e5c76e75 submissions: reject reset unless validator is stale
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-19 18:05:50 -07:00
b4be174d98 web: implement reset from softlock 2025-03-19 17:49:26 -07:00
f52e0a91a2 openapi: generate 2025-03-19 17:43:17 -07:00
0b1e7085e3 openapi: implement reset from softlock 2025-03-19 17:42:38 -07:00
31f1db6446 submissions: implement reset from softlock 2025-03-19 17:38:40 -07:00
b377405762 web: display validation error
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-18 16:45:41 -07:00
b496f8c0d8 submissions-api: v0.6.0
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-18 16:13:10 -07:00
0c247fbb43 submissions-api: add status message to validation failure 2025-03-18 16:12:42 -07:00
483ffd1d66 submissions: add StatusMessage to submissions 2025-03-18 16:06:47 -07:00
ff01abdd63 openapi: generate 2025-03-18 16:05:02 -07:00
0271ba4d28 openapi: add status message to validation failure 2025-03-18 16:04:28 -07:00
c6b31b7c73 submissions: tweak group roles to allow developers proper staging permissions
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-18 15:12:10 -07:00
80e7d735be openapi: generate
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-18 14:47:19 -07:00
e66513e88d Revert "openapi: no security for get submission requests"
This reverts commit 11e801443f.
2025-03-18 14:47:05 -07:00
355161c3b1 submissions: publish validated model
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-18 14:26:19 -07:00
e5a1dcf144 submissions-api: v0.5.0
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-18 14:20:21 -07:00
99e320d17f submissions-api: validated-model 2025-03-18 14:19:12 -07:00
57d714fdd7 submissions: implement internal validated model 2025-03-18 14:17:18 -07:00
d77bf02185 openapi: generate 2025-03-18 14:16:48 -07:00
47129e2d1f openapi: internal operation updates validated model 2025-03-18 14:16:16 -07:00
b542dba739 submissions: add ValidatedModelID to submissions model 2025-03-18 14:16:16 -07:00
f5c4868dc4 validation: v0.1.1
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-18 13:33:01 -07:00
1341f87bf8 submissions-api: v0.4.0 2025-03-18 13:33:01 -07:00
57544f3f64 submissions-api: external is default usage 2025-03-18 13:32:28 -07:00
ecb88c14a4 validation: explicitly append url (TODO: update deployment env var) 2025-03-18 13:23:48 -07:00
e1645e7c46 validation: use cargo workspace
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-18 12:37:20 -07:00
49e767f027 web: note when submissions list is loaded but empty 2025-03-18 12:32:48 -07:00
91a72ccf8b openapi: generate
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-18 12:08:56 -07:00
11e801443f openapi: no security for get submission requests 2025-03-18 12:08:56 -07:00
8338a71470 submissions: modernize loops 2025-03-18 12:08:56 -07:00
59e5e529c6 Strip /api prefix
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-17 20:25:02 -04:00
a82a78c938 middleware: oops, thats the wrong path
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-16 20:38:49 -04:00
b6c7c76900 document the middleware
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-16 16:42:44 -04:00
75e8d2b7b2 middleware 2025-03-16 16:33:16 -04:00
8dbdfbdb3f document environment variables
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-16 12:11:07 -07:00
28990e2dbe submissions: implement sort functionality for listSubmissions
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2025-01-13 20:34:04 -08:00
a39e2892ef openapi: generate 2025-01-13 05:01:04 -08:00
8e223d432e openapi: add sort parameter to listSubmissions 2025-01-13 05:00:51 -08:00
ic3w0lf
040488d85f small changes
All checks were successful
continuous-integration/drone/push Build is passing
2025-01-13 04:31:33 -07:00
e43f4bd0f0 openapi-internal: remove unused endpoint
All checks were successful
continuous-integration/drone/push Build is passing
2025-01-02 18:33:38 -08:00
ca1e007b07 docker: update compose
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-30 20:11:09 -08:00
952ceab014 submissions: ReleaseSubmissions operation 2024-12-30 20:11:09 -08:00
952b77b3db submissions: connect to maps grpc 2024-12-30 20:11:09 -08:00
0794e7ba46 openapi: generate 2024-12-30 19:16:58 -08:00
bc8b7b68d2 openapi: add release-submissions endpoint 2024-12-30 19:14:49 -08:00
114 changed files with 20861 additions and 4312 deletions

View File

@@ -67,7 +67,10 @@ steps:
- name: deploy
image: argoproj/argocd:latest
commands:
- echo "Deploy!" # Not going to do actually do this until
- argocd login --grpc-web cd.stricity.com --username $USERNAME --password $PASSWORD
- argocd app --grpc-web set ${DRONE_BRANCH}-maps-service --kustomize-image registry.itzana.me/strafesnet/maptest-api:${DRONE_BRANCH}-${DRONE_BUILD_NUMBER}
- argocd app --grpc-web set ${DRONE_BRANCH}-maps-service --kustomize-image registry.itzana.me/strafesnet/maptest-frontend:${DRONE_BRANCH}-${DRONE_BUILD_NUMBER}
- argocd app --grpc-web set ${DRONE_BRANCH}-maps-service --kustomize-image registry.itzana.me/strafesnet/maptest-validator:${DRONE_BRANCH}-${DRONE_BUILD_NUMBER}
environment:
USERNAME:
from_secret: ARGO_USER
@@ -83,6 +86,6 @@ steps:
- staging
---
kind: signature
hmac: 9958fd5b01af1ebcc75f7277fe71eb5336b899445c359cecf1b14e83b3d05059
hmac: 1162b329a9cad12b4c5db0ccf8b8998072b0de9279326f76a493fd0af6794095
...

3
.gitignore vendored
View File

@@ -1 +1,2 @@
.idea
.idea
/target

File diff suppressed because it is too large Load Diff

6
Cargo.toml Normal file
View File

@@ -0,0 +1,6 @@
[workspace]
members = [
"validation",
"validation/api",
]
resolver = "2"

View File

@@ -26,6 +26,12 @@ Prerequisite: golang installed
Prerequisite: bun installed
The environment variable `API_HOST` will need to be set for the middleware.
Example `.env` in web's root:
```
API_HOST="http://localhost:8082/v1/"
```
1. `cd web`
2. `bun install`
@@ -43,6 +49,12 @@ Prerequisite: rust installed
1. `cd validation`
2. `cargo run --release`
Environment Variables:
- ROBLOX_GROUP_ID
- RBXCOOKIE
- API_HOST_INTERNAL
- NATS_HOST
#### License
<sup>

View File

@@ -30,7 +30,8 @@ services:
"--pg-password","happypostgresuser",
# other hosts
"--nats-host","nats:4222",
"--auth-rpc-host","authrpc:8081"
"--auth-rpc-host","authrpc:8081",
"--data-rpc-host","dataservice:9000",
]
depends_on:
- authrpc
@@ -47,6 +48,8 @@ services:
- maps-service-network
ports:
- "3000:3000"
environment:
- API_HOST=http://submissions:8082/v1
validation:
image:
@@ -56,9 +59,8 @@ services:
- ../auth-compose/strafesnet_staging.env
environment:
- ROBLOX_GROUP_ID=17032139 # "None" is special case string value
- API_HOST_INTERNAL=http://submissions:8083
- API_HOST_INTERNAL=http://submissions:8083/v1
- NATS_HOST=nats:4222
- DATA_HOST=http://dataservice:9000
depends_on:
- nats
# note: this races the submissions which creates a nats stream
@@ -91,11 +93,12 @@ services:
- maps-service-network
authrpc:
image: registry.itzana.me/strafesnet/auth-service:master
image: registry.itzana.me/strafesnet/auth-service:staging
container_name: authrpc
command: ["serve", "rpc"]
environment:
- REDIS_ADDR=authredis:6379
- RBX_GROUP_ID=17032139
env_file:
- ../auth-compose/auth-service.env
depends_on:
@@ -106,7 +109,7 @@ services:
driver: "none"
auth-web:
image: registry.itzana.me/strafesnet/auth-service:master
image: registry.itzana.me/strafesnet/auth-service:staging
command: ["serve", "web"]
environment:
- REDIS_ADDR=authredis:6379

View File

@@ -4,24 +4,113 @@ info:
description: Internal operations inaccessible from the public internet.
version: 0.1.0
tags:
- name: Mapfixes
description: Mapfix operations
- name: Submissions
description: Submission operations
paths:
/submissions/{SubmissionID}/model:
/mapfixes/{MapfixID}/validated-model:
post:
summary: Update model following role restrictions
operationId: updateSubmissionModel
summary: Update validated model
operationId: updateMapfixValidatedModel
tags:
- Submissions
- Mapfixes
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: ModelID
- $ref: '#/components/parameters/MapfixID'
- name: ValidatedModelID
in: query
required: true
schema:
type: integer
format: int64
- name: VersionID
- name: ValidatedModelVersion
in: query
required: true
schema:
type: integer
format: int64
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-validated:
post:
summary: (Internal endpoint) Role Validator changes status from Validating -> Validated
operationId: actionMapfixValidated
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-failed:
post:
summary: (Internal endpoint) Role Validator changes status from Validating -> Accepted
operationId: actionMapfixAccepted
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: StatusMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-uploaded:
post:
summary: (Internal endpoint) Role Validator changes status from Uploading -> Uploaded
operationId: actionMapfixUploaded
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/validated-model:
post:
summary: Update validated model
operationId: updateSubmissionValidatedModel
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: ValidatedModelID
in: query
required: true
schema:
type: integer
format: int64
- name: ValidatedModelVersion
in: query
required: true
schema:
@@ -61,6 +150,13 @@ paths:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: StatusMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
@@ -78,8 +174,9 @@ paths:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: TargetAssetID
- name: UploadedAssetID
in: query
required: true
schema:
type: integer
format: int64
@@ -92,23 +189,6 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/releaser-released:
post:
summary: (Internal endpoint) Role Releaser changes status from releasing -> released
operationId: actionSubmissionReleased
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/script-policy:
get:
summary: Get list of script policies
@@ -198,7 +278,12 @@ paths:
schema:
type: string
maxLength: 1048576
- name: SubmissionID
- name: ResourceType
in: query
schema:
type: integer
format: int32
- name: ResourceID
in: query
schema:
type: integer
@@ -265,6 +350,14 @@ paths:
$ref: "#/components/schemas/Error"
components:
parameters:
MapfixID:
name: MapfixID
in: path
required: true
description: The unique identifier for a submission.
schema:
type: integer
format: int64
SubmissionID:
name: SubmissionID
in: path
@@ -313,7 +406,8 @@ components:
- Name
- Hash
- Source
- SubmissionID
- ResourceType
- ResourceID
type: object
properties:
ID:
@@ -329,14 +423,18 @@ components:
Source:
type: string
maxLength: 1048576
SubmissionID:
ResourceType:
type: integer
format: int32
ResourceID:
type: integer
format: int64
ScriptCreate:
required:
- Name
- Source
# - SubmissionID
- ResourceType
# - ResourceID
type: object
properties:
Name:
@@ -345,7 +443,10 @@ components:
Source:
type: string
maxLength: 1048576
SubmissionID:
ResourceType:
type: integer
format: int32
ResourceID:
type: integer
format: int64
ScriptPolicy:

View File

@@ -6,6 +6,10 @@ info:
servers:
- url: https://submissions.strafes.net/v1
tags:
- name: Mapfixes
description: Mapfix operations
- name: Session
description: Session operations
- name: Submissions
description: Submission operations
- name: Scripts
@@ -15,12 +19,70 @@ tags:
security:
- cookieAuth: []
paths:
/submissions:
/session/user:
get:
summary: Get list of submissions
operationId: listSubmissions
summary: Get information about the currently logged in user
operationId: sessionUser
tags:
- Submissions
- Session
responses:
"200":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/User"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/session/roles:
get:
summary: Get list of roles for the current session
operationId: sessionRoles
tags:
- Session
responses:
"200":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/Roles"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/session/validate:
get:
summary: Ask if the current session is valid
operationId: sessionValidate
tags:
- Session
responses:
"200":
description: Successful response
content:
application/json:
schema:
type: boolean
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes:
get:
summary: Get list of mapfixes
operationId: listMapfixes
tags:
- Mapfixes
security: []
parameters:
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/Limit"
@@ -39,6 +101,301 @@ paths:
schema:
type: integer
format: int32
- name: Sort
in: query
schema:
type: integer
format: int32
responses:
"200":
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Mapfix"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create new mapfix
operationId: createMapfix
tags:
- Mapfixes
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MapfixCreate'
responses:
"201":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/Id"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}:
get:
summary: Retrieve map with ID
operationId: getMapfix
tags:
- Mapfixes
security: []
parameters:
- $ref: '#/components/parameters/MapfixID'
responses:
"200":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/Mapfix"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/model:
post:
summary: Update model following role restrictions
operationId: updateMapfixModel
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: ModelID
in: query
required: true
schema:
type: integer
format: int64
- name: VersionID
in: query
required: true
schema:
type: integer
format: int64
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/completed:
post:
summary: Called by maptest when a player completes the map
operationId: setMapfixCompleted
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/submit:
post:
summary: Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted
operationId: actionMapfixSubmit
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/revoke:
post:
summary: Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction
operationId: actionMapfixRevoke
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/trigger-validate:
post:
summary: Role Reviewer triggers validation and changes status from Submitted -> Validating
operationId: actionMapfixTriggerValidate
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/retry-validate:
post:
summary: Role Reviewer re-runs validation and changes status from Accepted -> Validating
operationId: actionMapfixRetryValidate
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-validating:
post:
summary: Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted
operationId: actionMapfixAccepted
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/reject:
post:
summary: Role Reviewer changes status from Submitted -> Rejected
operationId: actionMapfixReject
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/request-changes:
post:
summary: Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested
operationId: actionMapfixRequestChanges
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/trigger-upload:
post:
summary: Role Admin changes status from Validated -> Uploading
operationId: actionMapfixTriggerUpload
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-uploading:
post:
summary: Role Admin manually resets uploading softlock and changes status from Uploading -> Validated
operationId: actionMapfixValidated
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions:
get:
summary: Get list of submissions
operationId: listSubmissions
tags:
- Submissions
security: []
parameters:
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/Limit"
- name: DisplayName
in: query
schema:
type: string
maxLength: 128
- name: Creator
in: query
schema:
type: string
maxLength: 128
- name: GameID
in: query
schema:
type: integer
format: int32
- name: Sort
in: query
schema:
type: integer
format: int32
responses:
"200":
description: Successful response
@@ -84,6 +441,7 @@ paths:
operationId: getSubmission
tags:
- Submissions
security: []
parameters:
- $ref: '#/components/parameters/SubmissionID'
responses:
@@ -130,7 +488,7 @@ paths:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/completed:
post:
summary: Retrieve map with ID
summary: Called by maptest when a player completes the map
operationId: setSubmissionCompleted
tags:
- Submissions
@@ -181,7 +539,7 @@ paths:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/trigger-validate:
post:
summary: Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating
summary: Role Reviewer triggers validation and changes status from Submitted -> Validating
operationId: actionSubmissionTriggerValidate
tags:
- Submissions
@@ -196,6 +554,40 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/retry-validate:
post:
summary: Role Reviewer re-runs validation and changes status from Accepted -> Validating
operationId: actionSubmissionRetryValidate
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-validating:
post:
summary: Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted
operationId: actionSubmissionAccepted
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/reject:
post:
summary: Role Reviewer changes status from Submitted -> Rejected
@@ -247,12 +639,55 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/reset-uploading:
post:
summary: Role Admin manually resets uploading softlock and changes status from Uploading -> Validated
operationId: actionSubmissionValidated
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/release-submissions:
post:
summary: Release a set of uploaded maps
operationId: releaseSubmissions
tags:
- Submissions
requestBody:
required: true
content:
application/json:
schema:
type: array
minItems: 1
maxItems: 255
items:
$ref: '#/components/schemas/ReleaseInfo'
responses:
"201":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/script-policy:
get:
summary: Get list of script policies
operationId: listScriptPolicy
tags:
- ScriptPolicy
security: []
parameters:
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/Limit"
@@ -317,6 +752,7 @@ paths:
operationId: getScriptPolicy
tags:
- ScriptPolicy
security: []
parameters:
- $ref: '#/components/parameters/ScriptPolicyID'
responses:
@@ -376,6 +812,7 @@ paths:
operationId: listScripts
tags:
- Script
security: []
parameters:
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/Limit"
@@ -395,7 +832,12 @@ paths:
schema:
type: string
maxLength: 1048576
- name: SubmissionID
- name: ResourceType
in: query
schema:
type: integer
format: int32
- name: ResourceID
in: query
schema:
type: integer
@@ -445,6 +887,7 @@ paths:
operationId: getScript
tags:
- Scripts
security: []
parameters:
- $ref: '#/components/parameters/ScriptID'
responses:
@@ -505,6 +948,14 @@ components:
in: cookie
name: session_id
parameters:
MapfixID:
name: MapfixID
in: path
required: true
description: The unique identifier for a mapfix.
schema:
type: integer
format: int64
SubmissionID:
name: SubmissionID
in: path
@@ -555,7 +1006,31 @@ components:
ID:
type: integer
format: int64
Submission:
Roles:
required:
- Roles
type: object
properties:
Roles:
type: integer
format: int32
User:
required:
- UserID
- Username
- AvatarURL
type: object
properties:
UserID:
type: integer
format: int64
Username:
type: string
maxLength: 128
AvatarURL:
type: string
maxLength: 256
Mapfix:
required:
- ID
- DisplayName
@@ -567,9 +1042,9 @@ components:
- AssetID
- AssetVersion
- Completed
- SubmissionType
# - TargetAssetID
- TargetAssetID
- StatusID
- StatusMessage
type: object
properties:
ID:
@@ -601,23 +1076,23 @@ components:
format: int64
Completed:
type: boolean
SubmissionType:
type: integer
format: int32
TargetAssetID:
type: integer
format: int64
StatusID:
type: integer
format: int32
SubmissionCreate:
StatusMessage:
type: string
maxLength: 256
MapfixCreate:
required:
- DisplayName
- Creator
- GameID
- AssetID
- AssetVersion
# - TargetAssetID
- TargetAssetID
type: object
properties:
DisplayName:
@@ -638,13 +1113,113 @@ components:
TargetAssetID:
type: integer
format: int64
Submission:
required:
- ID
- DisplayName
- Creator
- GameID
- CreatedAt
- UpdatedAt
- Submitter
- AssetID
- AssetVersion
# - ValidatedAssetID
# - ValidatedAssetVersion
- Completed
# - UploadedAssetID
- StatusID
- StatusMessage
type: object
properties:
ID:
type: integer
format: int64
DisplayName:
type: string
maxLength: 128
Creator:
type: string
maxLength: 128
GameID:
type: integer
format: int32
CreatedAt:
type: integer
format: int64
UpdatedAt:
type: integer
format: int64
Submitter:
type: integer
format: int64
AssetID:
type: integer
format: int64
AssetVersion:
type: integer
format: int64
ValidatedAssetID:
type: integer
format: int64
ValidatedAssetVersion:
type: integer
format: int64
Completed:
type: boolean
UploadedAssetID:
type: integer
format: int64
StatusID:
type: integer
format: int32
StatusMessage:
type: string
maxLength: 256
SubmissionCreate:
required:
- DisplayName
- Creator
- GameID
- AssetID
- AssetVersion
type: object
properties:
DisplayName:
type: string
maxLength: 128
Creator:
type: string
maxLength: 128
GameID:
type: integer
format: int32
AssetID:
type: integer
format: int64
AssetVersion:
type: integer
format: int64
ReleaseInfo:
required:
- SubmissionID
- Date
type: object
properties:
SubmissionID:
type: integer
format: int64
Date:
type: string
format: date-time
Script:
required:
- ID
- Name
- Hash
- Source
- SubmissionID
- ResourceType
- ResourceID
type: object
properties:
ID:
@@ -660,14 +1235,18 @@ components:
Source:
type: string
maxLength: 1048576
SubmissionID:
ResourceType:
type: integer
format: int32
ResourceID:
type: integer
format: int64
ScriptCreate:
required:
- Name
- Source
# - SubmissionID
- ResourceType
# - ResourceID
type: object
properties:
Name:
@@ -676,7 +1255,10 @@ components:
Source:
type: string
maxLength: 1048576
SubmissionID:
ResourceType:
type: integer
format: int32
ResourceID:
type: integer
format: int64
ScriptUpdate:
@@ -693,7 +1275,10 @@ components:
Source:
type: string
maxLength: 1048576
SubmissionID:
ResourceType:
type: integer
format: int32
ResourceID:
type: integer
format: int64
ScriptPolicy:

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

View File

@@ -6,24 +6,45 @@ package api
type OperationName = string
const (
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
ActionMapfixRejectOperation OperationName = "ActionMapfixReject"
ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges"
ActionMapfixRetryValidateOperation OperationName = "ActionMapfixRetryValidate"
ActionMapfixRevokeOperation OperationName = "ActionMapfixRevoke"
ActionMapfixSubmitOperation OperationName = "ActionMapfixSubmit"
ActionMapfixTriggerUploadOperation OperationName = "ActionMapfixTriggerUpload"
ActionMapfixTriggerValidateOperation OperationName = "ActionMapfixTriggerValidate"
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject"
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
ActionSubmissionRetryValidateOperation OperationName = "ActionSubmissionRetryValidate"
ActionSubmissionRevokeOperation OperationName = "ActionSubmissionRevoke"
ActionSubmissionSubmitOperation OperationName = "ActionSubmissionSubmit"
ActionSubmissionTriggerUploadOperation OperationName = "ActionSubmissionTriggerUpload"
ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateMapfixOperation OperationName = "CreateMapfix"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
DeleteScriptOperation OperationName = "DeleteScript"
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"
GetMapfixOperation OperationName = "GetMapfix"
GetScriptOperation OperationName = "GetScript"
GetScriptPolicyOperation OperationName = "GetScriptPolicy"
GetSubmissionOperation OperationName = "GetSubmission"
ListMapfixesOperation OperationName = "ListMapfixes"
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"
ListSubmissionsOperation OperationName = "ListSubmissions"
ReleaseSubmissionsOperation OperationName = "ReleaseSubmissions"
SessionRolesOperation OperationName = "SessionRoles"
SessionUserOperation OperationName = "SessionUser"
SessionValidateOperation OperationName = "SessionValidate"
SetMapfixCompletedOperation OperationName = "SetMapfixCompleted"
SetSubmissionCompletedOperation OperationName = "SetSubmissionCompleted"
UpdateMapfixModelOperation OperationName = "UpdateMapfixModel"
UpdateScriptOperation OperationName = "UpdateScript"
UpdateScriptPolicyOperation OperationName = "UpdateScriptPolicy"
UpdateSubmissionModelOperation OperationName = "UpdateSubmissionModel"

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,77 @@ import (
"github.com/ogen-go/ogen/validate"
)
func (s *Server) decodeCreateMapfixRequest(r *http.Request) (
req *MapfixCreate,
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 == "application/json":
if r.ContentLength == 0 {
return req, close, validate.ErrBodyRequired
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return req, close, err
}
if len(buf) == 0 {
return req, close, validate.ErrBodyRequired
}
d := jx.DecodeBytes(buf)
var request MapfixCreate
if err := func() error {
if err := request.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeCreateScriptRequest(r *http.Request) (
req *ScriptCreate,
close func() error,
@@ -220,6 +291,93 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
}
}
func (s *Server) decodeReleaseSubmissionsRequest(r *http.Request) (
req []ReleaseInfo,
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 == "application/json":
if r.ContentLength == 0 {
return req, close, validate.ErrBodyRequired
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return req, close, err
}
if len(buf) == 0 {
return req, close, validate.ErrBodyRequired
}
d := jx.DecodeBytes(buf)
var request []ReleaseInfo
if err := func() error {
request = make([]ReleaseInfo, 0)
if err := d.Arr(func(d *jx.Decoder) error {
var elem ReleaseInfo
if err := elem.Decode(d); err != nil {
return err
}
request = append(request, elem)
return nil
}); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
if err := func() error {
if request == nil {
return errors.New("nil is invalid value")
}
if err := (validate.Array{
MinLength: 1,
MinLengthSet: true,
MaxLength: 255,
MaxLengthSet: true,
}).ValidateLength(len(request)); err != nil {
return errors.Wrap(err, "array")
}
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) decodeUpdateScriptRequest(r *http.Request) (
req *ScriptUpdate,
close func() error,

View File

@@ -11,6 +11,20 @@ import (
ht "github.com/ogen-go/ogen/http"
)
func encodeCreateMapfixRequest(
req *MapfixCreate,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeCreateScriptRequest(
req *ScriptCreate,
r *http.Request,
@@ -53,6 +67,24 @@ func encodeCreateSubmissionRequest(
return nil
}
func encodeReleaseSubmissionsRequest(
req []ReleaseInfo,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
e.ArrStart()
for _, elem := range req {
elem.Encode(e)
}
e.ArrEnd()
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeUpdateScriptRequest(
req *ScriptUpdate,
r *http.Request,

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,76 @@ import (
ht "github.com/ogen-go/ogen/http"
)
func encodeActionMapfixAcceptedResponse(response *ActionMapfixAcceptedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixRejectResponse(response *ActionMapfixRejectNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixRequestChangesResponse(response *ActionMapfixRequestChangesNoContent, 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))
return nil
}
func encodeActionMapfixRevokeResponse(response *ActionMapfixRevokeNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixSubmitResponse(response *ActionMapfixSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixTriggerUploadResponse(response *ActionMapfixTriggerUploadNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixTriggerValidateResponse(response *ActionMapfixTriggerValidateNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixValidatedResponse(response *ActionMapfixValidatedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionRejectResponse(response *ActionSubmissionRejectNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@@ -27,6 +97,13 @@ func encodeActionSubmissionRequestChangesResponse(response *ActionSubmissionRequ
return nil
}
func encodeActionSubmissionRetryValidateResponse(response *ActionSubmissionRetryValidateNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionRevokeResponse(response *ActionSubmissionRevokeNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@@ -55,6 +132,27 @@ func encodeActionSubmissionTriggerValidateResponse(response *ActionSubmissionTri
return nil
}
func encodeActionSubmissionValidatedResponse(response *ActionSubmissionValidatedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeCreateMapfixResponse(response *ID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeCreateScriptResponse(response *ID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
@@ -111,6 +209,20 @@ func encodeDeleteScriptPolicyResponse(response *DeleteScriptPolicyNoContent, w h
return nil
}
func encodeGetMapfixResponse(response *Mapfix, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
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 encodeGetScriptResponse(response *Script, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
@@ -153,6 +265,24 @@ func encodeGetSubmissionResponse(response *Submission, w http.ResponseWriter, sp
return nil
}
func encodeListMapfixesResponse(response []Mapfix, 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)
e.ArrStart()
for _, elem := range response {
elem.Encode(e)
}
e.ArrEnd()
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeListScriptPolicyResponse(response []ScriptPolicy, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
@@ -207,6 +337,62 @@ func encodeListSubmissionsResponse(response []Submission, w http.ResponseWriter,
return nil
}
func encodeReleaseSubmissionsResponse(response *ReleaseSubmissionsCreated, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
return nil
}
func encodeSessionRolesResponse(response *Roles, 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 encodeSessionUserResponse(response *User, 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 encodeSessionValidateResponse(response bool, 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)
e.Bool(response)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeSetMapfixCompletedResponse(response *SetMapfixCompletedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeSetSubmissionCompletedResponse(response *SetSubmissionCompletedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@@ -214,6 +400,13 @@ func encodeSetSubmissionCompletedResponse(response *SetSubmissionCompletedNoCont
return nil
}
func encodeUpdateMapfixModelResponse(response *UpdateMapfixModelNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeUpdateScriptResponse(response *UpdateScriptNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))

File diff suppressed because it is too large Load Diff

View File

@@ -4,18 +4,52 @@ package api
import (
"fmt"
"time"
)
func (s *ErrorStatusCode) Error() string {
return fmt.Sprintf("code %d: %+v", s.StatusCode, s.Response)
}
// ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation.
type ActionMapfixAcceptedNoContent struct{}
// ActionMapfixRejectNoContent is response for ActionMapfixReject operation.
type ActionMapfixRejectNoContent struct{}
// ActionMapfixRequestChangesNoContent is response for ActionMapfixRequestChanges operation.
type ActionMapfixRequestChangesNoContent struct{}
// ActionMapfixRetryValidateNoContent is response for ActionMapfixRetryValidate operation.
type ActionMapfixRetryValidateNoContent struct{}
// ActionMapfixRevokeNoContent is response for ActionMapfixRevoke operation.
type ActionMapfixRevokeNoContent struct{}
// ActionMapfixSubmitNoContent is response for ActionMapfixSubmit operation.
type ActionMapfixSubmitNoContent struct{}
// ActionMapfixTriggerUploadNoContent is response for ActionMapfixTriggerUpload operation.
type ActionMapfixTriggerUploadNoContent struct{}
// ActionMapfixTriggerValidateNoContent is response for ActionMapfixTriggerValidate operation.
type ActionMapfixTriggerValidateNoContent struct{}
// ActionMapfixValidatedNoContent is response for ActionMapfixValidated operation.
type ActionMapfixValidatedNoContent struct{}
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
type ActionSubmissionAcceptedNoContent struct{}
// ActionSubmissionRejectNoContent is response for ActionSubmissionReject operation.
type ActionSubmissionRejectNoContent struct{}
// ActionSubmissionRequestChangesNoContent is response for ActionSubmissionRequestChanges operation.
type ActionSubmissionRequestChangesNoContent struct{}
// ActionSubmissionRetryValidateNoContent is response for ActionSubmissionRetryValidate operation.
type ActionSubmissionRetryValidateNoContent struct{}
// ActionSubmissionRevokeNoContent is response for ActionSubmissionRevoke operation.
type ActionSubmissionRevokeNoContent struct{}
@@ -28,6 +62,9 @@ type ActionSubmissionTriggerUploadNoContent struct{}
// ActionSubmissionTriggerValidateNoContent is response for ActionSubmissionTriggerValidate operation.
type ActionSubmissionTriggerValidateNoContent struct{}
// ActionSubmissionValidatedNoContent is response for ActionSubmissionValidated operation.
type ActionSubmissionValidatedNoContent struct{}
type CookieAuth struct {
APIKey string
}
@@ -116,6 +153,223 @@ func (s *ID) SetID(val int64) {
s.ID = val
}
// Ref: #/components/schemas/Mapfix
type Mapfix struct {
ID int64 `json:"ID"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
CreatedAt int64 `json:"CreatedAt"`
UpdatedAt int64 `json:"UpdatedAt"`
Submitter int64 `json:"Submitter"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
Completed bool `json:"Completed"`
TargetAssetID int64 `json:"TargetAssetID"`
StatusID int32 `json:"StatusID"`
StatusMessage string `json:"StatusMessage"`
}
// GetID returns the value of ID.
func (s *Mapfix) GetID() int64 {
return s.ID
}
// GetDisplayName returns the value of DisplayName.
func (s *Mapfix) GetDisplayName() string {
return s.DisplayName
}
// GetCreator returns the value of Creator.
func (s *Mapfix) GetCreator() string {
return s.Creator
}
// GetGameID returns the value of GameID.
func (s *Mapfix) GetGameID() int32 {
return s.GameID
}
// GetCreatedAt returns the value of CreatedAt.
func (s *Mapfix) GetCreatedAt() int64 {
return s.CreatedAt
}
// GetUpdatedAt returns the value of UpdatedAt.
func (s *Mapfix) GetUpdatedAt() int64 {
return s.UpdatedAt
}
// GetSubmitter returns the value of Submitter.
func (s *Mapfix) GetSubmitter() int64 {
return s.Submitter
}
// GetAssetID returns the value of AssetID.
func (s *Mapfix) GetAssetID() int64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *Mapfix) GetAssetVersion() int64 {
return s.AssetVersion
}
// GetCompleted returns the value of Completed.
func (s *Mapfix) GetCompleted() bool {
return s.Completed
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *Mapfix) GetTargetAssetID() int64 {
return s.TargetAssetID
}
// GetStatusID returns the value of StatusID.
func (s *Mapfix) GetStatusID() int32 {
return s.StatusID
}
// GetStatusMessage returns the value of StatusMessage.
func (s *Mapfix) GetStatusMessage() string {
return s.StatusMessage
}
// SetID sets the value of ID.
func (s *Mapfix) SetID(val int64) {
s.ID = val
}
// SetDisplayName sets the value of DisplayName.
func (s *Mapfix) SetDisplayName(val string) {
s.DisplayName = val
}
// SetCreator sets the value of Creator.
func (s *Mapfix) SetCreator(val string) {
s.Creator = val
}
// SetGameID sets the value of GameID.
func (s *Mapfix) SetGameID(val int32) {
s.GameID = val
}
// SetCreatedAt sets the value of CreatedAt.
func (s *Mapfix) SetCreatedAt(val int64) {
s.CreatedAt = val
}
// SetUpdatedAt sets the value of UpdatedAt.
func (s *Mapfix) SetUpdatedAt(val int64) {
s.UpdatedAt = val
}
// SetSubmitter sets the value of Submitter.
func (s *Mapfix) SetSubmitter(val int64) {
s.Submitter = val
}
// SetAssetID sets the value of AssetID.
func (s *Mapfix) SetAssetID(val int64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *Mapfix) SetAssetVersion(val int64) {
s.AssetVersion = val
}
// SetCompleted sets the value of Completed.
func (s *Mapfix) SetCompleted(val bool) {
s.Completed = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *Mapfix) SetTargetAssetID(val int64) {
s.TargetAssetID = val
}
// SetStatusID sets the value of StatusID.
func (s *Mapfix) SetStatusID(val int32) {
s.StatusID = val
}
// SetStatusMessage sets the value of StatusMessage.
func (s *Mapfix) SetStatusMessage(val string) {
s.StatusMessage = val
}
// Ref: #/components/schemas/MapfixCreate
type MapfixCreate struct {
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
TargetAssetID int64 `json:"TargetAssetID"`
}
// GetDisplayName returns the value of DisplayName.
func (s *MapfixCreate) GetDisplayName() string {
return s.DisplayName
}
// GetCreator returns the value of Creator.
func (s *MapfixCreate) GetCreator() string {
return s.Creator
}
// GetGameID returns the value of GameID.
func (s *MapfixCreate) GetGameID() int32 {
return s.GameID
}
// GetAssetID returns the value of AssetID.
func (s *MapfixCreate) GetAssetID() int64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *MapfixCreate) GetAssetVersion() int64 {
return s.AssetVersion
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *MapfixCreate) GetTargetAssetID() int64 {
return s.TargetAssetID
}
// SetDisplayName sets the value of DisplayName.
func (s *MapfixCreate) SetDisplayName(val string) {
s.DisplayName = val
}
// SetCreator sets the value of Creator.
func (s *MapfixCreate) SetCreator(val string) {
s.Creator = val
}
// SetGameID sets the value of GameID.
func (s *MapfixCreate) SetGameID(val int32) {
s.GameID = val
}
// SetAssetID sets the value of AssetID.
func (s *MapfixCreate) SetAssetID(val int64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *MapfixCreate) SetAssetVersion(val int64) {
s.AssetVersion = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *MapfixCreate) SetTargetAssetID(val int64) {
s.TargetAssetID = val
}
// NewOptInt32 returns new OptInt32 with value set to v.
func NewOptInt32(v int32) OptInt32 {
return OptInt32{
@@ -254,13 +508,58 @@ func (o OptString) Or(d string) string {
return d
}
// Ref: #/components/schemas/ReleaseInfo
type ReleaseInfo struct {
SubmissionID int64 `json:"SubmissionID"`
Date time.Time `json:"Date"`
}
// GetSubmissionID returns the value of SubmissionID.
func (s *ReleaseInfo) GetSubmissionID() int64 {
return s.SubmissionID
}
// GetDate returns the value of Date.
func (s *ReleaseInfo) GetDate() time.Time {
return s.Date
}
// SetSubmissionID sets the value of SubmissionID.
func (s *ReleaseInfo) SetSubmissionID(val int64) {
s.SubmissionID = val
}
// SetDate sets the value of Date.
func (s *ReleaseInfo) SetDate(val time.Time) {
s.Date = val
}
// ReleaseSubmissionsCreated is response for ReleaseSubmissions operation.
type ReleaseSubmissionsCreated struct{}
// Ref: #/components/schemas/Roles
type Roles struct {
Roles int32 `json:"Roles"`
}
// GetRoles returns the value of Roles.
func (s *Roles) GetRoles() int32 {
return s.Roles
}
// SetRoles sets the value of Roles.
func (s *Roles) SetRoles(val int32) {
s.Roles = val
}
// Ref: #/components/schemas/Script
type Script struct {
ID int64 `json:"ID"`
Name string `json:"Name"`
Hash string `json:"Hash"`
Source string `json:"Source"`
SubmissionID int64 `json:"SubmissionID"`
ResourceType int32 `json:"ResourceType"`
ResourceID int64 `json:"ResourceID"`
}
// GetID returns the value of ID.
@@ -283,9 +582,14 @@ func (s *Script) GetSource() string {
return s.Source
}
// GetSubmissionID returns the value of SubmissionID.
func (s *Script) GetSubmissionID() int64 {
return s.SubmissionID
// GetResourceType returns the value of ResourceType.
func (s *Script) GetResourceType() int32 {
return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *Script) GetResourceID() int64 {
return s.ResourceID
}
// SetID sets the value of ID.
@@ -308,16 +612,22 @@ func (s *Script) SetSource(val string) {
s.Source = val
}
// SetSubmissionID sets the value of SubmissionID.
func (s *Script) SetSubmissionID(val int64) {
s.SubmissionID = val
// SetResourceType sets the value of ResourceType.
func (s *Script) SetResourceType(val int32) {
s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *Script) SetResourceID(val int64) {
s.ResourceID = val
}
// Ref: #/components/schemas/ScriptCreate
type ScriptCreate struct {
Name string `json:"Name"`
Source string `json:"Source"`
SubmissionID OptInt64 `json:"SubmissionID"`
ResourceType int32 `json:"ResourceType"`
ResourceID OptInt64 `json:"ResourceID"`
}
// GetName returns the value of Name.
@@ -330,9 +640,14 @@ func (s *ScriptCreate) GetSource() string {
return s.Source
}
// GetSubmissionID returns the value of SubmissionID.
func (s *ScriptCreate) GetSubmissionID() OptInt64 {
return s.SubmissionID
// GetResourceType returns the value of ResourceType.
func (s *ScriptCreate) GetResourceType() int32 {
return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *ScriptCreate) GetResourceID() OptInt64 {
return s.ResourceID
}
// SetName sets the value of Name.
@@ -345,9 +660,14 @@ func (s *ScriptCreate) SetSource(val string) {
s.Source = val
}
// SetSubmissionID sets the value of SubmissionID.
func (s *ScriptCreate) SetSubmissionID(val OptInt64) {
s.SubmissionID = val
// SetResourceType sets the value of ResourceType.
func (s *ScriptCreate) SetResourceType(val int32) {
s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *ScriptCreate) SetResourceID(val OptInt64) {
s.ResourceID = val
}
// Ref: #/components/schemas/ScriptPolicy
@@ -488,7 +808,8 @@ type ScriptUpdate struct {
ID int64 `json:"ID"`
Name OptString `json:"Name"`
Source OptString `json:"Source"`
SubmissionID OptInt64 `json:"SubmissionID"`
ResourceType OptInt32 `json:"ResourceType"`
ResourceID OptInt64 `json:"ResourceID"`
}
// GetID returns the value of ID.
@@ -506,9 +827,14 @@ func (s *ScriptUpdate) GetSource() OptString {
return s.Source
}
// GetSubmissionID returns the value of SubmissionID.
func (s *ScriptUpdate) GetSubmissionID() OptInt64 {
return s.SubmissionID
// GetResourceType returns the value of ResourceType.
func (s *ScriptUpdate) GetResourceType() OptInt32 {
return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *ScriptUpdate) GetResourceID() OptInt64 {
return s.ResourceID
}
// SetID sets the value of ID.
@@ -526,29 +852,39 @@ func (s *ScriptUpdate) SetSource(val OptString) {
s.Source = val
}
// SetSubmissionID sets the value of SubmissionID.
func (s *ScriptUpdate) SetSubmissionID(val OptInt64) {
s.SubmissionID = val
// SetResourceType sets the value of ResourceType.
func (s *ScriptUpdate) SetResourceType(val OptInt32) {
s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *ScriptUpdate) SetResourceID(val OptInt64) {
s.ResourceID = val
}
// SetMapfixCompletedNoContent is response for SetMapfixCompleted operation.
type SetMapfixCompletedNoContent struct{}
// SetSubmissionCompletedNoContent is response for SetSubmissionCompleted operation.
type SetSubmissionCompletedNoContent struct{}
// Ref: #/components/schemas/Submission
type Submission struct {
ID int64 `json:"ID"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
CreatedAt int64 `json:"CreatedAt"`
UpdatedAt int64 `json:"UpdatedAt"`
Submitter int64 `json:"Submitter"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
Completed bool `json:"Completed"`
SubmissionType int32 `json:"SubmissionType"`
TargetAssetID OptInt64 `json:"TargetAssetID"`
StatusID int32 `json:"StatusID"`
ID int64 `json:"ID"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
CreatedAt int64 `json:"CreatedAt"`
UpdatedAt int64 `json:"UpdatedAt"`
Submitter int64 `json:"Submitter"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
ValidatedAssetID OptInt64 `json:"ValidatedAssetID"`
ValidatedAssetVersion OptInt64 `json:"ValidatedAssetVersion"`
Completed bool `json:"Completed"`
UploadedAssetID OptInt64 `json:"UploadedAssetID"`
StatusID int32 `json:"StatusID"`
StatusMessage string `json:"StatusMessage"`
}
// GetID returns the value of ID.
@@ -596,19 +932,24 @@ func (s *Submission) GetAssetVersion() int64 {
return s.AssetVersion
}
// GetValidatedAssetID returns the value of ValidatedAssetID.
func (s *Submission) GetValidatedAssetID() OptInt64 {
return s.ValidatedAssetID
}
// GetValidatedAssetVersion returns the value of ValidatedAssetVersion.
func (s *Submission) GetValidatedAssetVersion() OptInt64 {
return s.ValidatedAssetVersion
}
// GetCompleted returns the value of Completed.
func (s *Submission) GetCompleted() bool {
return s.Completed
}
// GetSubmissionType returns the value of SubmissionType.
func (s *Submission) GetSubmissionType() int32 {
return s.SubmissionType
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *Submission) GetTargetAssetID() OptInt64 {
return s.TargetAssetID
// GetUploadedAssetID returns the value of UploadedAssetID.
func (s *Submission) GetUploadedAssetID() OptInt64 {
return s.UploadedAssetID
}
// GetStatusID returns the value of StatusID.
@@ -616,6 +957,11 @@ func (s *Submission) GetStatusID() int32 {
return s.StatusID
}
// GetStatusMessage returns the value of StatusMessage.
func (s *Submission) GetStatusMessage() string {
return s.StatusMessage
}
// SetID sets the value of ID.
func (s *Submission) SetID(val int64) {
s.ID = val
@@ -661,19 +1007,24 @@ func (s *Submission) SetAssetVersion(val int64) {
s.AssetVersion = val
}
// SetValidatedAssetID sets the value of ValidatedAssetID.
func (s *Submission) SetValidatedAssetID(val OptInt64) {
s.ValidatedAssetID = val
}
// SetValidatedAssetVersion sets the value of ValidatedAssetVersion.
func (s *Submission) SetValidatedAssetVersion(val OptInt64) {
s.ValidatedAssetVersion = val
}
// SetCompleted sets the value of Completed.
func (s *Submission) SetCompleted(val bool) {
s.Completed = val
}
// SetSubmissionType sets the value of SubmissionType.
func (s *Submission) SetSubmissionType(val int32) {
s.SubmissionType = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *Submission) SetTargetAssetID(val OptInt64) {
s.TargetAssetID = val
// SetUploadedAssetID sets the value of UploadedAssetID.
func (s *Submission) SetUploadedAssetID(val OptInt64) {
s.UploadedAssetID = val
}
// SetStatusID sets the value of StatusID.
@@ -681,14 +1032,18 @@ func (s *Submission) SetStatusID(val int32) {
s.StatusID = val
}
// SetStatusMessage sets the value of StatusMessage.
func (s *Submission) SetStatusMessage(val string) {
s.StatusMessage = val
}
// Ref: #/components/schemas/SubmissionCreate
type SubmissionCreate struct {
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
TargetAssetID OptInt64 `json:"TargetAssetID"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
}
// GetDisplayName returns the value of DisplayName.
@@ -716,11 +1071,6 @@ func (s *SubmissionCreate) GetAssetVersion() int64 {
return s.AssetVersion
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *SubmissionCreate) GetTargetAssetID() OptInt64 {
return s.TargetAssetID
}
// SetDisplayName sets the value of DisplayName.
func (s *SubmissionCreate) SetDisplayName(val string) {
s.DisplayName = val
@@ -746,10 +1096,8 @@ func (s *SubmissionCreate) SetAssetVersion(val int64) {
s.AssetVersion = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *SubmissionCreate) SetTargetAssetID(val OptInt64) {
s.TargetAssetID = val
}
// UpdateMapfixModelNoContent is response for UpdateMapfixModel operation.
type UpdateMapfixModelNoContent struct{}
// UpdateScriptNoContent is response for UpdateScript operation.
type UpdateScriptNoContent struct{}
@@ -759,3 +1107,40 @@ type UpdateScriptPolicyNoContent struct{}
// UpdateSubmissionModelNoContent is response for UpdateSubmissionModel operation.
type UpdateSubmissionModelNoContent struct{}
// Ref: #/components/schemas/User
type User struct {
UserID int64 `json:"UserID"`
Username string `json:"Username"`
AvatarURL string `json:"AvatarURL"`
}
// GetUserID returns the value of UserID.
func (s *User) GetUserID() int64 {
return s.UserID
}
// GetUsername returns the value of Username.
func (s *User) GetUsername() string {
return s.Username
}
// GetAvatarURL returns the value of AvatarURL.
func (s *User) GetAvatarURL() string {
return s.AvatarURL
}
// SetUserID sets the value of UserID.
func (s *User) SetUserID(val int64) {
s.UserID = val
}
// SetUsername sets the value of Username.
func (s *User) SetUsername(val string) {
s.Username = val
}
// SetAvatarURL sets the value of AvatarURL.
func (s *User) SetAvatarURL(val string) {
s.AvatarURL = val
}

View File

@@ -8,6 +8,66 @@ import (
// Handler handles operations described by OpenAPI v3 specification.
type Handler interface {
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/reset-validating
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixReject implements actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
//
// POST /mapfixes/{MapfixID}/status/reject
ActionMapfixReject(ctx context.Context, params ActionMapfixRejectParams) error
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
//
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
//
// POST /mapfixes/{MapfixID}/status/request-changes
ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error
// ActionMapfixRetryValidate implements actionMapfixRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
//
// POST /mapfixes/{MapfixID}/status/retry-validate
ActionMapfixRetryValidate(ctx context.Context, params ActionMapfixRetryValidateParams) error
// ActionMapfixRevoke implements actionMapfixRevoke operation.
//
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
//
// POST /mapfixes/{MapfixID}/status/revoke
ActionMapfixRevoke(ctx context.Context, params ActionMapfixRevokeParams) error
// ActionMapfixSubmit implements actionMapfixSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/submit
ActionMapfixSubmit(ctx context.Context, params ActionMapfixSubmitParams) error
// ActionMapfixTriggerUpload implements actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
//
// POST /mapfixes/{MapfixID}/status/trigger-upload
ActionMapfixTriggerUpload(ctx context.Context, params ActionMapfixTriggerUploadParams) error
// ActionMapfixTriggerValidate implements actionMapfixTriggerValidate operation.
//
// Role Reviewer triggers validation and changes status from Submitted -> Validating.
//
// POST /mapfixes/{MapfixID}/status/trigger-validate
ActionMapfixTriggerValidate(ctx context.Context, params ActionMapfixTriggerValidateParams) error
// ActionMapfixValidated implements actionMapfixValidated operation.
//
// Role Admin manually resets uploading softlock and changes status from Uploading -> Validated.
//
// POST /mapfixes/{MapfixID}/status/reset-uploading
ActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) error
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/reset-validating
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionReject implements actionSubmissionReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@@ -20,6 +80,12 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/request-changes
ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error
// ActionSubmissionRetryValidate implements actionSubmissionRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
//
// POST /submissions/{SubmissionID}/status/retry-validate
ActionSubmissionRetryValidate(ctx context.Context, params ActionSubmissionRetryValidateParams) error
// ActionSubmissionRevoke implements actionSubmissionRevoke operation.
//
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
@@ -40,10 +106,22 @@ type Handler interface {
ActionSubmissionTriggerUpload(ctx context.Context, params ActionSubmissionTriggerUploadParams) error
// ActionSubmissionTriggerValidate implements actionSubmissionTriggerValidate operation.
//
// Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
// Role Reviewer triggers validation and changes status from Submitted -> Validating.
//
// POST /submissions/{SubmissionID}/status/trigger-validate
ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error
// ActionSubmissionValidated implements actionSubmissionValidated operation.
//
// Role Admin manually resets uploading softlock and changes status from Uploading -> Validated.
//
// POST /submissions/{SubmissionID}/status/reset-uploading
ActionSubmissionValidated(ctx context.Context, params ActionSubmissionValidatedParams) error
// CreateMapfix implements createMapfix operation.
//
// Create new mapfix.
//
// POST /mapfixes
CreateMapfix(ctx context.Context, req *MapfixCreate) (*ID, error)
// CreateScript implements createScript operation.
//
// Create a new script.
@@ -74,6 +152,12 @@ type Handler interface {
//
// DELETE /script-policy/{ScriptPolicyID}
DeleteScriptPolicy(ctx context.Context, params DeleteScriptPolicyParams) error
// GetMapfix implements getMapfix operation.
//
// Retrieve map with ID.
//
// GET /mapfixes/{MapfixID}
GetMapfix(ctx context.Context, params GetMapfixParams) (*Mapfix, error)
// GetScript implements getScript operation.
//
// Get the specified script by ID.
@@ -92,6 +176,12 @@ type Handler interface {
//
// GET /submissions/{SubmissionID}
GetSubmission(ctx context.Context, params GetSubmissionParams) (*Submission, error)
// ListMapfixes implements listMapfixes operation.
//
// Get list of mapfixes.
//
// GET /mapfixes
ListMapfixes(ctx context.Context, params ListMapfixesParams) ([]Mapfix, error)
// ListScriptPolicy implements listScriptPolicy operation.
//
// Get list of script policies.
@@ -110,12 +200,48 @@ type Handler interface {
//
// GET /submissions
ListSubmissions(ctx context.Context, params ListSubmissionsParams) ([]Submission, error)
// ReleaseSubmissions implements releaseSubmissions operation.
//
// Release a set of uploaded maps.
//
// POST /release-submissions
ReleaseSubmissions(ctx context.Context, req []ReleaseInfo) error
// SessionRoles implements sessionRoles operation.
//
// Get list of roles for the current session.
//
// GET /session/roles
SessionRoles(ctx context.Context) (*Roles, error)
// SessionUser implements sessionUser operation.
//
// Get information about the currently logged in user.
//
// GET /session/user
SessionUser(ctx context.Context) (*User, error)
// SessionValidate implements sessionValidate operation.
//
// Ask if the current session is valid.
//
// GET /session/validate
SessionValidate(ctx context.Context) (bool, error)
// SetMapfixCompleted implements setMapfixCompleted operation.
//
// Called by maptest when a player completes the map.
//
// POST /mapfixes/{MapfixID}/completed
SetMapfixCompleted(ctx context.Context, params SetMapfixCompletedParams) error
// SetSubmissionCompleted implements setSubmissionCompleted operation.
//
// Retrieve map with ID.
// Called by maptest when a player completes the map.
//
// POST /submissions/{SubmissionID}/completed
SetSubmissionCompleted(ctx context.Context, params SetSubmissionCompletedParams) error
// UpdateMapfixModel implements updateMapfixModel operation.
//
// Update model following role restrictions.
//
// POST /mapfixes/{MapfixID}/model
UpdateMapfixModel(ctx context.Context, params UpdateMapfixModelParams) error
// UpdateScript implements updateScript operation.
//
// Update the specified script by ID.

View File

@@ -13,6 +13,96 @@ type UnimplementedHandler struct{}
var _ Handler = UnimplementedHandler{}
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/reset-validating
func (UnimplementedHandler) ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixReject implements actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
//
// POST /mapfixes/{MapfixID}/status/reject
func (UnimplementedHandler) ActionMapfixReject(ctx context.Context, params ActionMapfixRejectParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
//
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
//
// POST /mapfixes/{MapfixID}/status/request-changes
func (UnimplementedHandler) ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixRetryValidate implements actionMapfixRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
//
// POST /mapfixes/{MapfixID}/status/retry-validate
func (UnimplementedHandler) ActionMapfixRetryValidate(ctx context.Context, params ActionMapfixRetryValidateParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixRevoke implements actionMapfixRevoke operation.
//
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
//
// POST /mapfixes/{MapfixID}/status/revoke
func (UnimplementedHandler) ActionMapfixRevoke(ctx context.Context, params ActionMapfixRevokeParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixSubmit implements actionMapfixSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/submit
func (UnimplementedHandler) ActionMapfixSubmit(ctx context.Context, params ActionMapfixSubmitParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixTriggerUpload implements actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
//
// POST /mapfixes/{MapfixID}/status/trigger-upload
func (UnimplementedHandler) ActionMapfixTriggerUpload(ctx context.Context, params ActionMapfixTriggerUploadParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixTriggerValidate implements actionMapfixTriggerValidate operation.
//
// Role Reviewer triggers validation and changes status from Submitted -> Validating.
//
// POST /mapfixes/{MapfixID}/status/trigger-validate
func (UnimplementedHandler) ActionMapfixTriggerValidate(ctx context.Context, params ActionMapfixTriggerValidateParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixValidated implements actionMapfixValidated operation.
//
// Role Admin manually resets uploading softlock and changes status from Uploading -> Validated.
//
// POST /mapfixes/{MapfixID}/status/reset-uploading
func (UnimplementedHandler) ActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/reset-validating
func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionReject implements actionSubmissionReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@@ -31,6 +121,15 @@ func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context,
return ht.ErrNotImplemented
}
// ActionSubmissionRetryValidate implements actionSubmissionRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
//
// POST /submissions/{SubmissionID}/status/retry-validate
func (UnimplementedHandler) ActionSubmissionRetryValidate(ctx context.Context, params ActionSubmissionRetryValidateParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionRevoke implements actionSubmissionRevoke operation.
//
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
@@ -60,13 +159,31 @@ func (UnimplementedHandler) ActionSubmissionTriggerUpload(ctx context.Context, p
// ActionSubmissionTriggerValidate implements actionSubmissionTriggerValidate operation.
//
// Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
// Role Reviewer triggers validation and changes status from Submitted -> Validating.
//
// POST /submissions/{SubmissionID}/status/trigger-validate
func (UnimplementedHandler) ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionValidated implements actionSubmissionValidated operation.
//
// Role Admin manually resets uploading softlock and changes status from Uploading -> Validated.
//
// POST /submissions/{SubmissionID}/status/reset-uploading
func (UnimplementedHandler) ActionSubmissionValidated(ctx context.Context, params ActionSubmissionValidatedParams) error {
return ht.ErrNotImplemented
}
// CreateMapfix implements createMapfix operation.
//
// Create new mapfix.
//
// POST /mapfixes
func (UnimplementedHandler) CreateMapfix(ctx context.Context, req *MapfixCreate) (r *ID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateScript implements createScript operation.
//
// Create a new script.
@@ -112,6 +229,15 @@ func (UnimplementedHandler) DeleteScriptPolicy(ctx context.Context, params Delet
return ht.ErrNotImplemented
}
// GetMapfix implements getMapfix operation.
//
// Retrieve map with ID.
//
// GET /mapfixes/{MapfixID}
func (UnimplementedHandler) GetMapfix(ctx context.Context, params GetMapfixParams) (r *Mapfix, _ error) {
return r, ht.ErrNotImplemented
}
// GetScript implements getScript operation.
//
// Get the specified script by ID.
@@ -139,6 +265,15 @@ func (UnimplementedHandler) GetSubmission(ctx context.Context, params GetSubmiss
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) {
return r, ht.ErrNotImplemented
}
// ListScriptPolicy implements listScriptPolicy operation.
//
// Get list of script policies.
@@ -166,15 +301,69 @@ func (UnimplementedHandler) ListSubmissions(ctx context.Context, params ListSubm
return r, ht.ErrNotImplemented
}
// ReleaseSubmissions implements releaseSubmissions operation.
//
// Release a set of uploaded maps.
//
// POST /release-submissions
func (UnimplementedHandler) ReleaseSubmissions(ctx context.Context, req []ReleaseInfo) error {
return ht.ErrNotImplemented
}
// SessionRoles implements sessionRoles operation.
//
// Get list of roles for the current session.
//
// GET /session/roles
func (UnimplementedHandler) SessionRoles(ctx context.Context) (r *Roles, _ error) {
return r, ht.ErrNotImplemented
}
// SessionUser implements sessionUser operation.
//
// Get information about the currently logged in user.
//
// GET /session/user
func (UnimplementedHandler) SessionUser(ctx context.Context) (r *User, _ error) {
return r, ht.ErrNotImplemented
}
// SessionValidate implements sessionValidate operation.
//
// Ask if the current session is valid.
//
// GET /session/validate
func (UnimplementedHandler) SessionValidate(ctx context.Context) (r bool, _ error) {
return r, ht.ErrNotImplemented
}
// SetMapfixCompleted implements setMapfixCompleted operation.
//
// Called by maptest when a player completes the map.
//
// POST /mapfixes/{MapfixID}/completed
func (UnimplementedHandler) SetMapfixCompleted(ctx context.Context, params SetMapfixCompletedParams) error {
return ht.ErrNotImplemented
}
// SetSubmissionCompleted implements setSubmissionCompleted operation.
//
// Retrieve map with ID.
// Called by maptest when a player completes the map.
//
// POST /submissions/{SubmissionID}/completed
func (UnimplementedHandler) SetSubmissionCompleted(ctx context.Context, params SetSubmissionCompletedParams) error {
return ht.ErrNotImplemented
}
// UpdateMapfixModel implements updateMapfixModel operation.
//
// Update model following role restrictions.
//
// POST /mapfixes/{MapfixID}/model
func (UnimplementedHandler) UpdateMapfixModel(ctx context.Context, params UpdateMapfixModelParams) error {
return ht.ErrNotImplemented
}
// UpdateScript implements updateScript operation.
//
// Update the specified script by ID.

View File

@@ -8,6 +8,125 @@ import (
"github.com/ogen-go/ogen/validate"
)
func (s *Mapfix) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.DisplayName)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "DisplayName",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Creator)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Creator",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 256,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.StatusMessage)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "StatusMessage",
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.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.DisplayName)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "DisplayName",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Creator)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Creator",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *Script) Validate() error {
if s == nil {
return validate.ErrNilPointer
@@ -266,6 +385,25 @@ func (s *Submission) Validate() error {
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 256,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.StatusMessage)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "StatusMessage",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
@@ -321,3 +459,53 @@ func (s *SubmissionCreate) Validate() error {
}
return nil
}
func (s *User) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Username)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Username",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 256,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.AvatarURL)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AvatarURL",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}

View File

@@ -5,6 +5,7 @@ import (
"net/http"
"git.itzana.me/strafesnet/go-grpc/auth"
"git.itzana.me/strafesnet/go-grpc/maps"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
@@ -77,6 +78,12 @@ func NewServeCommand() *cli.Command {
EnvVars: []string{"AUTH_RPC_HOST"},
Value: "auth-service:8090",
},
&cli.StringFlag{
Name: "data-rpc-host",
Usage: "Host of data rpc",
EnvVars: []string{"DATA_RPC_HOST"},
Value: "data-service:9000",
},
&cli.StringFlag{
Name: "nats-host",
Usage: "Host of nats",
@@ -110,12 +117,18 @@ func serve(ctx *cli.Context) error {
log.WithError(err).Fatal("failed to add stream")
}
// connect to main game database
conn, err := grpc.Dial(ctx.String("data-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
svc := &service.Service{
DB: db,
Nats: js,
Client: maps.NewMapsServiceClient(conn),
}
conn, err := grpc.Dial(ctx.String("auth-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials()))
conn, err = grpc.Dial(ctx.String("auth-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}

View File

@@ -9,23 +9,45 @@ import (
var (
ErrNotExist = errors.New("resource does not exist")
ErroNoRowsAffected = errors.New("query did not affect any rows")
ErrInvalidListSort = errors.New("invalid list sort parameter [1,2,3,4]")
)
type ListSort uint32
const (
ListSortDisabled ListSort = 0
ListSortDisplayNameAscending ListSort = 1
ListSortDisplayNameDescending ListSort = 2
ListSortDateAscending ListSort = 3
ListSortDateDescending ListSort = 4
)
type Datastore interface {
Mapfixes() Mapfixes
Submissions() Submissions
Scripts() Scripts
ScriptPolicy() ScriptPolicy
}
type Mapfixes interface {
Get(ctx context.Context, id int64) (model.Mapfix, error)
GetList(ctx context.Context, id []int64) ([]model.Mapfix, error)
Create(ctx context.Context, smap model.Mapfix) (model.Mapfix, error)
Update(ctx context.Context, id int64, values OptionalMap) error
IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.MapfixStatus, values OptionalMap) error
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)
}
type Submissions interface {
Get(ctx context.Context, id int64) (model.Submission, error)
GetList(ctx context.Context, id []int64) ([]model.Submission, error)
Create(ctx context.Context, smap model.Submission) (model.Submission, error)
Update(ctx context.Context, id int64, values OptionalMap) error
IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values OptionalMap) error
IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.Status, values OptionalMap) (model.Submission, error)
IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.SubmissionStatus, values OptionalMap) error
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) ([]model.Submission, error)
List(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) ([]model.Submission, error)
}
type Scripts interface {

View File

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

View File

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

View File

@@ -0,0 +1,132 @@
package gormstore
import (
"context"
"errors"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type Mapfixes struct {
db *gorm.DB
}
func (env *Mapfixes) Get(ctx context.Context, id int64) (model.Mapfix, error) {
var mapfix model.Mapfix
if err := env.db.First(&mapfix, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return mapfix, datastore.ErrNotExist
}
return mapfix, err
}
return mapfix, nil
}
func (env *Mapfixes) GetList(ctx context.Context, id []int64) ([]model.Mapfix, error) {
var mapList []model.Mapfix
if err := env.db.Find(&mapList, "id IN ?", id).Error; err != nil {
return mapList, err
}
return mapList, nil
}
func (env *Mapfixes) Create(ctx context.Context, smap model.Mapfix) (model.Mapfix, error) {
if err := env.db.Create(&smap).Error; err != nil {
return smap, err
}
return smap, nil
}
func (env *Mapfixes) Update(ctx context.Context, id int64, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Mapfix{}).Where("id = ?", id).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
// the update can only occur if the status matches one of the provided values.
func (env *Mapfixes) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.MapfixStatus, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Mapfix{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
// the update can only occur if the status matches one of the provided values.
// returns the updated value
func (env *Mapfixes) IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.MapfixStatus, values datastore.OptionalMap) (model.Mapfix, error) {
var mapfix model.Mapfix
result := env.db.Model(&mapfix).
Clauses(clause.Returning{}).
Where("id = ?", id).
Where("status_id IN ?", statuses).
Updates(values.Map())
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return mapfix, datastore.ErrNotExist
}
return mapfix, result.Error
}
if result.RowsAffected == 0 {
return mapfix, datastore.ErroNoRowsAffected
}
return mapfix, nil
}
func (env *Mapfixes) Delete(ctx context.Context, id int64) error {
if err := env.db.Delete(&model.Mapfix{}, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *Mapfixes) List(ctx context.Context, filters datastore.OptionalMap, page model.Page, sort datastore.ListSort) ([]model.Mapfix, error) {
var maps []model.Mapfix
db := env.db
switch sort {
case datastore.ListSortDisabled:
// No sort
break
case datastore.ListSortDisplayNameAscending:
db=db.Order("display_name ASC")
break
case datastore.ListSortDisplayNameDescending:
db=db.Order("display_name DESC")
break
case datastore.ListSortDateAscending:
db=db.Order("created_at ASC")
break
case datastore.ListSortDateDescending:
db=db.Order("created_at DESC")
break
default:
return nil, datastore.ErrInvalidListSort
}
if err := db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&maps).Error; err != nil {
return nil, err
}
return maps, nil
}

View File

@@ -53,7 +53,7 @@ func (env *Scripts) Update(ctx context.Context, id int64, values datastore.Optio
}
// the update can only occur if the status matches one of the provided values.
func (env *Scripts) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) error {
func (env *Scripts) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.SubmissionStatus, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Script{}).Where("id = ?", id).Where("status IN ?", statuses).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist

View File

@@ -54,7 +54,7 @@ func (env *Submissions) Update(ctx context.Context, id int64, values datastore.O
}
// the update can only occur if the status matches one of the provided values.
func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) error {
func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.SubmissionStatus, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Submission{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
@@ -67,7 +67,7 @@ func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, status
// the update can only occur if the status matches one of the provided values.
// returns the updated value
func (env *Submissions) IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) (model.Submission, error) {
func (env *Submissions) IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.SubmissionStatus, values datastore.OptionalMap) (model.Submission, error) {
var submission model.Submission
result := env.db.Model(&submission).
Clauses(clause.Returning{}).
@@ -99,9 +99,32 @@ func (env *Submissions) Delete(ctx context.Context, id int64) error {
return nil
}
func (env *Submissions) List(ctx context.Context, filters datastore.OptionalMap, page model.Page) ([]model.Submission, error) {
func (env *Submissions) List(ctx context.Context, filters datastore.OptionalMap, page model.Page, sort datastore.ListSort) ([]model.Submission, error) {
var maps []model.Submission
if err := env.db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&maps).Error; err != nil {
db := env.db
switch sort {
case datastore.ListSortDisabled:
// No sort
break
case datastore.ListSortDisplayNameAscending:
db=db.Order("display_name ASC")
break
case datastore.ListSortDisplayNameDescending:
db=db.Order("display_name DESC")
break
case datastore.ListSortDateAscending:
db=db.Order("created_at ASC")
break
case datastore.ListSortDateDescending:
db=db.Order("created_at DESC")
break
default:
return nil, datastore.ErrInvalidListSort
}
if err := db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&maps).Error; err != nil {
return nil, err
}

View File

@@ -21,20 +21,37 @@ import (
"github.com/ogen-go/ogen/uri"
)
func trimTrailingSlashes(u *url.URL) {
u.Path = strings.TrimRight(u.Path, "/")
u.RawPath = strings.TrimRight(u.RawPath, "/")
}
// Invoker invokes operations described by OpenAPI v3 specification.
type Invoker interface {
// ActionMapfixAccepted invokes actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixUploaded invokes actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
ActionMapfixUploaded(ctx context.Context, params ActionMapfixUploadedParams) error
// ActionMapfixValidated invokes actionMapfixValidated operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
ActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) error
// ActionSubmissionAccepted invokes actionSubmissionAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/validator-failed
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionReleased invokes actionSubmissionReleased operation.
//
// (Internal endpoint) Role Releaser changes status from releasing -> released.
//
// POST /submissions/{SubmissionID}/status/releaser-released
ActionSubmissionReleased(ctx context.Context, params ActionSubmissionReleasedParams) error
// ActionSubmissionUploaded invokes actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@@ -77,12 +94,18 @@ type Invoker interface {
//
// GET /scripts
ListScripts(ctx context.Context, params ListScriptsParams) ([]Script, error)
// UpdateSubmissionModel invokes updateSubmissionModel operation.
// UpdateMapfixValidatedModel invokes updateMapfixValidatedModel operation.
//
// Update model following role restrictions.
// Update validated model.
//
// POST /submissions/{SubmissionID}/model
UpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) error
// POST /mapfixes/{MapfixID}/validated-model
UpdateMapfixValidatedModel(ctx context.Context, params UpdateMapfixValidatedModelParams) error
// UpdateSubmissionValidatedModel invokes updateSubmissionValidatedModel operation.
//
// Update validated model.
//
// POST /submissions/{SubmissionID}/validated-model
UpdateSubmissionValidatedModel(ctx context.Context, params UpdateSubmissionValidatedModelParams) error
}
// Client implements OAS client.
@@ -99,11 +122,6 @@ var _ Handler = struct {
*Client
}{}
func trimTrailingSlashes(u *url.URL) {
u.Path = strings.TrimRight(u.Path, "/")
u.RawPath = strings.TrimRight(u.RawPath, "/")
}
// NewClient initializes new Client defined by OAS.
func NewClient(serverURL string, opts ...ClientOption) (*Client, error) {
u, err := url.Parse(serverURL)
@@ -137,6 +155,297 @@ func (c *Client) requestURL(ctx context.Context) *url.URL {
return u
}
// ActionMapfixAccepted invokes actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
func (c *Client) ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error {
_, err := c.sendActionMapfixAccepted(ctx, params)
return err
}
func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) (res *ActionMapfixAcceptedNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixAccepted"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-failed"),
}
// 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, ActionMapfixAcceptedOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/mapfixes/"
{
// Encode "MapfixID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "MapfixID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/status/validator-failed"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "StatusMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "StatusMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.StatusMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeActionMapfixAcceptedResponse(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.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
func (c *Client) ActionMapfixUploaded(ctx context.Context, params ActionMapfixUploadedParams) error {
_, err := c.sendActionMapfixUploaded(ctx, params)
return err
}
func (c *Client) sendActionMapfixUploaded(ctx context.Context, params ActionMapfixUploadedParams) (res *ActionMapfixUploadedNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixUploaded"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-uploaded"),
}
// 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, ActionMapfixUploadedOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/mapfixes/"
{
// Encode "MapfixID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "MapfixID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/status/validator-uploaded"
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 := decodeActionMapfixUploadedResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionMapfixValidated invokes actionMapfixValidated operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
func (c *Client) ActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) error {
_, err := c.sendActionMapfixValidated(ctx, params)
return err
}
func (c *Client) sendActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) (res *ActionMapfixValidatedNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixValidated"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-validated"),
}
// 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, ActionMapfixValidatedOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/mapfixes/"
{
// Encode "MapfixID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "MapfixID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/status/validator-validated"
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 := decodeActionMapfixValidatedResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionSubmissionAccepted invokes actionSubmissionAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
@@ -206,6 +515,24 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
pathParts[2] = "/status/validator-failed"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "StatusMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "StatusMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.StatusMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
@@ -228,97 +555,6 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
return result, nil
}
// ActionSubmissionReleased invokes actionSubmissionReleased operation.
//
// (Internal endpoint) Role Releaser changes status from releasing -> released.
//
// POST /submissions/{SubmissionID}/status/releaser-released
func (c *Client) ActionSubmissionReleased(ctx context.Context, params ActionSubmissionReleasedParams) error {
_, err := c.sendActionSubmissionReleased(ctx, params)
return err
}
func (c *Client) sendActionSubmissionReleased(ctx context.Context, params ActionSubmissionReleasedParams) (res *ActionSubmissionReleasedNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionSubmissionReleased"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/releaser-released"),
}
// 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, ActionSubmissionReleasedOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/submissions/"
{
// Encode "SubmissionID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "SubmissionID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/status/releaser-released"
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 := decodeActionSubmissionReleasedResponse(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.
@@ -391,18 +627,15 @@ func (c *Client) sendActionSubmissionUploaded(ctx context.Context, params Action
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "TargetAssetID" parameter.
// Encode "UploadedAssetID" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "TargetAssetID",
Name: "UploadedAssetID",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.TargetAssetID.Get(); ok {
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
return e.EncodeValue(conv.Int64ToString(params.UploadedAssetID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -1049,15 +1282,32 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
}
}
{
// Encode "SubmissionID" parameter.
// Encode "ResourceType" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "SubmissionID",
Name: "ResourceType",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.SubmissionID.Get(); ok {
if val, ok := params.ResourceType.Get(); ok {
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
{
// Encode "ResourceID" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ResourceID",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.ResourceID.Get(); ok {
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
@@ -1089,21 +1339,21 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
return result, nil
}
// UpdateSubmissionModel invokes updateSubmissionModel operation.
// UpdateMapfixValidatedModel invokes updateMapfixValidatedModel operation.
//
// Update model following role restrictions.
// Update validated model.
//
// POST /submissions/{SubmissionID}/model
func (c *Client) UpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) error {
_, err := c.sendUpdateSubmissionModel(ctx, params)
// POST /mapfixes/{MapfixID}/validated-model
func (c *Client) UpdateMapfixValidatedModel(ctx context.Context, params UpdateMapfixValidatedModelParams) error {
_, err := c.sendUpdateMapfixValidatedModel(ctx, params)
return err
}
func (c *Client) sendUpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) (res *UpdateSubmissionModelNoContent, err error) {
func (c *Client) sendUpdateMapfixValidatedModel(ctx context.Context, params UpdateMapfixValidatedModelParams) (res *UpdateMapfixValidatedModelNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("updateSubmissionModel"),
otelogen.OperationID("updateMapfixValidatedModel"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/model"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/validated-model"),
}
// Run stopwatch.
@@ -1118,7 +1368,130 @@ func (c *Client) sendUpdateSubmissionModel(ctx context.Context, params UpdateSub
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, UpdateSubmissionModelOperation,
ctx, span := c.cfg.Tracer.Start(ctx, UpdateMapfixValidatedModelOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/mapfixes/"
{
// Encode "MapfixID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "MapfixID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/validated-model"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "ValidatedModelID" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ValidatedModelID",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
{
// Encode "ValidatedModelVersion" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ValidatedModelVersion",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelVersion))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeUpdateMapfixValidatedModelResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// UpdateSubmissionValidatedModel invokes updateSubmissionValidatedModel operation.
//
// Update validated model.
//
// POST /submissions/{SubmissionID}/validated-model
func (c *Client) UpdateSubmissionValidatedModel(ctx context.Context, params UpdateSubmissionValidatedModelParams) error {
_, err := c.sendUpdateSubmissionValidatedModel(ctx, params)
return err
}
func (c *Client) sendUpdateSubmissionValidatedModel(ctx context.Context, params UpdateSubmissionValidatedModelParams) (res *UpdateSubmissionValidatedModelNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("updateSubmissionValidatedModel"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/validated-model"),
}
// 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, UpdateSubmissionValidatedModelOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
@@ -1155,35 +1528,35 @@ func (c *Client) sendUpdateSubmissionModel(ctx context.Context, params UpdateSub
}
pathParts[1] = encoded
}
pathParts[2] = "/model"
pathParts[2] = "/validated-model"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "ModelID" parameter.
// Encode "ValidatedModelID" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ModelID",
Name: "ValidatedModelID",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int64ToString(params.ModelID))
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
{
// Encode "VersionID" parameter.
// Encode "ValidatedModelVersion" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "VersionID",
Name: "ValidatedModelVersion",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int64ToString(params.VersionID))
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelVersion))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -1204,7 +1577,7 @@ func (c *Client) sendUpdateSubmissionModel(ctx context.Context, params UpdateSub
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeUpdateSubmissionModelResponse(resp)
result, err := decodeUpdateSubmissionValidatedModelResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}

View File

@@ -30,6 +30,457 @@ func (c *codeRecorder) WriteHeader(status int) {
c.ResponseWriter.WriteHeader(status)
}
// handleActionMapfixAcceptedRequest handles actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
func (s *Server) handleActionMapfixAcceptedRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixAccepted"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-failed"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixAcceptedOperation,
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: ActionMapfixAcceptedOperation,
ID: "actionMapfixAccepted",
}
)
params, err := decodeActionMapfixAcceptedParams(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 *ActionMapfixAcceptedNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: ActionMapfixAcceptedOperation,
OperationSummary: "(Internal endpoint) Role Validator changes status from Validating -> Accepted",
OperationID: "actionMapfixAccepted",
Body: nil,
Params: middleware.Parameters{
{
Name: "MapfixID",
In: "path",
}: params.MapfixID,
{
Name: "StatusMessage",
In: "query",
}: params.StatusMessage,
},
Raw: r,
}
type (
Request = struct{}
Params = ActionMapfixAcceptedParams
Response = *ActionMapfixAcceptedNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackActionMapfixAcceptedParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.ActionMapfixAccepted(ctx, params)
return response, err
},
)
} else {
err = s.h.ActionMapfixAccepted(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 := encodeActionMapfixAcceptedResponse(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.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
func (s *Server) handleActionMapfixUploadedRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixUploaded"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-uploaded"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixUploadedOperation,
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: ActionMapfixUploadedOperation,
ID: "actionMapfixUploaded",
}
)
params, err := decodeActionMapfixUploadedParams(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 *ActionMapfixUploadedNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: ActionMapfixUploadedOperation,
OperationSummary: "(Internal endpoint) Role Validator changes status from Uploading -> Uploaded",
OperationID: "actionMapfixUploaded",
Body: nil,
Params: middleware.Parameters{
{
Name: "MapfixID",
In: "path",
}: params.MapfixID,
},
Raw: r,
}
type (
Request = struct{}
Params = ActionMapfixUploadedParams
Response = *ActionMapfixUploadedNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackActionMapfixUploadedParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.ActionMapfixUploaded(ctx, params)
return response, err
},
)
} else {
err = s.h.ActionMapfixUploaded(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 := encodeActionMapfixUploadedResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleActionMapfixValidatedRequest handles actionMapfixValidated operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
func (s *Server) handleActionMapfixValidatedRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixValidated"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-validated"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixValidatedOperation,
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: ActionMapfixValidatedOperation,
ID: "actionMapfixValidated",
}
)
params, err := decodeActionMapfixValidatedParams(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 *ActionMapfixValidatedNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: ActionMapfixValidatedOperation,
OperationSummary: "(Internal endpoint) Role Validator changes status from Validating -> Validated",
OperationID: "actionMapfixValidated",
Body: nil,
Params: middleware.Parameters{
{
Name: "MapfixID",
In: "path",
}: params.MapfixID,
},
Raw: r,
}
type (
Request = struct{}
Params = ActionMapfixValidatedParams
Response = *ActionMapfixValidatedNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackActionMapfixValidatedParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.ActionMapfixValidated(ctx, params)
return response, err
},
)
} else {
err = s.h.ActionMapfixValidated(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 := encodeActionMapfixValidatedResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleActionSubmissionAcceptedRequest handles actionSubmissionAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
@@ -128,6 +579,10 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
Name: "SubmissionID",
In: "path",
}: params.SubmissionID,
{
Name: "StatusMessage",
In: "query",
}: params.StatusMessage,
},
Raw: r,
}
@@ -179,155 +634,6 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
}
}
// handleActionSubmissionReleasedRequest handles actionSubmissionReleased operation.
//
// (Internal endpoint) Role Releaser changes status from releasing -> released.
//
// POST /submissions/{SubmissionID}/status/releaser-released
func (s *Server) handleActionSubmissionReleasedRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionSubmissionReleased"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/releaser-released"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionReleasedOperation,
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: ActionSubmissionReleasedOperation,
ID: "actionSubmissionReleased",
}
)
params, err := decodeActionSubmissionReleasedParams(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 *ActionSubmissionReleasedNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: ActionSubmissionReleasedOperation,
OperationSummary: "(Internal endpoint) Role Releaser changes status from releasing -> released",
OperationID: "actionSubmissionReleased",
Body: nil,
Params: middleware.Parameters{
{
Name: "SubmissionID",
In: "path",
}: params.SubmissionID,
},
Raw: r,
}
type (
Request = struct{}
Params = ActionSubmissionReleasedParams
Response = *ActionSubmissionReleasedNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackActionSubmissionReleasedParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.ActionSubmissionReleased(ctx, params)
return response, err
},
)
} else {
err = s.h.ActionSubmissionReleased(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 := encodeActionSubmissionReleasedResponse(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.
@@ -427,9 +733,9 @@ func (s *Server) handleActionSubmissionUploadedRequest(args [1]string, argsEscap
In: "path",
}: params.SubmissionID,
{
Name: "TargetAssetID",
Name: "UploadedAssetID",
In: "query",
}: params.TargetAssetID,
}: params.UploadedAssetID,
},
Raw: r,
}
@@ -1357,9 +1663,13 @@ func (s *Server) handleListScriptsRequest(args [0]string, argsEscaped bool, w ht
In: "query",
}: params.Source,
{
Name: "SubmissionID",
Name: "ResourceType",
In: "query",
}: params.SubmissionID,
}: params.ResourceType,
{
Name: "ResourceID",
In: "query",
}: params.ResourceID,
},
Raw: r,
}
@@ -1411,22 +1721,22 @@ func (s *Server) handleListScriptsRequest(args [0]string, argsEscaped bool, w ht
}
}
// handleUpdateSubmissionModelRequest handles updateSubmissionModel operation.
// handleUpdateMapfixValidatedModelRequest handles updateMapfixValidatedModel operation.
//
// Update model following role restrictions.
// Update validated model.
//
// POST /submissions/{SubmissionID}/model
func (s *Server) handleUpdateSubmissionModelRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
// POST /mapfixes/{MapfixID}/validated-model
func (s *Server) handleUpdateMapfixValidatedModelRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("updateSubmissionModel"),
otelogen.OperationID("updateMapfixValidatedModel"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/model"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/validated-model"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), UpdateSubmissionModelOperation,
ctx, span := s.cfg.Tracer.Start(r.Context(), UpdateMapfixValidatedModelOperation,
trace.WithAttributes(otelAttrs...),
serverSpanKind,
)
@@ -1481,11 +1791,11 @@ func (s *Server) handleUpdateSubmissionModelRequest(args [1]string, argsEscaped
}
err error
opErrContext = ogenerrors.OperationContext{
Name: UpdateSubmissionModelOperation,
ID: "updateSubmissionModel",
Name: UpdateMapfixValidatedModelOperation,
ID: "updateMapfixValidatedModel",
}
)
params, err := decodeUpdateSubmissionModelParams(args, argsEscaped, r)
params, err := decodeUpdateMapfixValidatedModelParams(args, argsEscaped, r)
if err != nil {
err = &ogenerrors.DecodeParamsError{
OperationContext: opErrContext,
@@ -1496,35 +1806,35 @@ func (s *Server) handleUpdateSubmissionModelRequest(args [1]string, argsEscaped
return
}
var response *UpdateSubmissionModelNoContent
var response *UpdateMapfixValidatedModelNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: UpdateSubmissionModelOperation,
OperationSummary: "Update model following role restrictions",
OperationID: "updateSubmissionModel",
OperationName: UpdateMapfixValidatedModelOperation,
OperationSummary: "Update validated model",
OperationID: "updateMapfixValidatedModel",
Body: nil,
Params: middleware.Parameters{
{
Name: "SubmissionID",
Name: "MapfixID",
In: "path",
}: params.SubmissionID,
}: params.MapfixID,
{
Name: "ModelID",
Name: "ValidatedModelID",
In: "query",
}: params.ModelID,
}: params.ValidatedModelID,
{
Name: "VersionID",
Name: "ValidatedModelVersion",
In: "query",
}: params.VersionID,
}: params.ValidatedModelVersion,
},
Raw: r,
}
type (
Request = struct{}
Params = UpdateSubmissionModelParams
Response = *UpdateSubmissionModelNoContent
Params = UpdateMapfixValidatedModelParams
Response = *UpdateMapfixValidatedModelNoContent
)
response, err = middleware.HookMiddleware[
Request,
@@ -1533,14 +1843,14 @@ func (s *Server) handleUpdateSubmissionModelRequest(args [1]string, argsEscaped
](
m,
mreq,
unpackUpdateSubmissionModelParams,
unpackUpdateMapfixValidatedModelParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.UpdateSubmissionModel(ctx, params)
err = s.h.UpdateMapfixValidatedModel(ctx, params)
return response, err
},
)
} else {
err = s.h.UpdateSubmissionModel(ctx, params)
err = s.h.UpdateMapfixValidatedModel(ctx, params)
}
if err != nil {
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {
@@ -1559,7 +1869,164 @@ func (s *Server) handleUpdateSubmissionModelRequest(args [1]string, argsEscaped
return
}
if err := encodeUpdateSubmissionModelResponse(response, w, span); err != nil {
if err := encodeUpdateMapfixValidatedModelResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleUpdateSubmissionValidatedModelRequest handles updateSubmissionValidatedModel operation.
//
// Update validated model.
//
// POST /submissions/{SubmissionID}/validated-model
func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("updateSubmissionValidatedModel"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/validated-model"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), UpdateSubmissionValidatedModelOperation,
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: UpdateSubmissionValidatedModelOperation,
ID: "updateSubmissionValidatedModel",
}
)
params, err := decodeUpdateSubmissionValidatedModelParams(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 *UpdateSubmissionValidatedModelNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: UpdateSubmissionValidatedModelOperation,
OperationSummary: "Update validated model",
OperationID: "updateSubmissionValidatedModel",
Body: nil,
Params: middleware.Parameters{
{
Name: "SubmissionID",
In: "path",
}: params.SubmissionID,
{
Name: "ValidatedModelID",
In: "query",
}: params.ValidatedModelID,
{
Name: "ValidatedModelVersion",
In: "query",
}: params.ValidatedModelVersion,
},
Raw: r,
}
type (
Request = struct{}
Params = UpdateSubmissionValidatedModelParams
Response = *UpdateSubmissionValidatedModelNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackUpdateSubmissionValidatedModelParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.UpdateSubmissionValidatedModel(ctx, params)
return response, err
},
)
} else {
err = s.h.UpdateSubmissionValidatedModel(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 := encodeUpdateSubmissionValidatedModelResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)

View File

@@ -282,17 +282,22 @@ func (s *Script) encodeFields(e *jx.Encoder) {
e.Str(s.Source)
}
{
e.FieldStart("SubmissionID")
e.Int64(s.SubmissionID)
e.FieldStart("ResourceType")
e.Int32(s.ResourceType)
}
{
e.FieldStart("ResourceID")
e.Int64(s.ResourceID)
}
}
var jsonFieldsNameOfScript = [5]string{
var jsonFieldsNameOfScript = [6]string{
0: "ID",
1: "Name",
2: "Hash",
3: "Source",
4: "SubmissionID",
4: "ResourceType",
5: "ResourceID",
}
// Decode decodes Script from json.
@@ -352,17 +357,29 @@ func (s *Script) Decode(d *jx.Decoder) error {
}(); err != nil {
return errors.Wrap(err, "decode field \"Source\"")
}
case "SubmissionID":
case "ResourceType":
requiredBitSet[0] |= 1 << 4
if err := func() error {
v, err := d.Int64()
s.SubmissionID = int64(v)
v, err := d.Int32()
s.ResourceType = int32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"SubmissionID\"")
return errors.Wrap(err, "decode field \"ResourceType\"")
}
case "ResourceID":
requiredBitSet[0] |= 1 << 5
if err := func() error {
v, err := d.Int64()
s.ResourceID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
}
default:
return d.Skip()
@@ -374,7 +391,7 @@ func (s *Script) Decode(d *jx.Decoder) error {
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00011111,
0b00111111,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
@@ -438,17 +455,22 @@ func (s *ScriptCreate) encodeFields(e *jx.Encoder) {
e.Str(s.Source)
}
{
if s.SubmissionID.Set {
e.FieldStart("SubmissionID")
s.SubmissionID.Encode(e)
e.FieldStart("ResourceType")
e.Int32(s.ResourceType)
}
{
if s.ResourceID.Set {
e.FieldStart("ResourceID")
s.ResourceID.Encode(e)
}
}
}
var jsonFieldsNameOfScriptCreate = [3]string{
var jsonFieldsNameOfScriptCreate = [4]string{
0: "Name",
1: "Source",
2: "SubmissionID",
2: "ResourceType",
3: "ResourceID",
}
// Decode decodes ScriptCreate from json.
@@ -484,15 +506,27 @@ func (s *ScriptCreate) Decode(d *jx.Decoder) error {
}(); err != nil {
return errors.Wrap(err, "decode field \"Source\"")
}
case "SubmissionID":
case "ResourceType":
requiredBitSet[0] |= 1 << 2
if err := func() error {
s.SubmissionID.Reset()
if err := s.SubmissionID.Decode(d); err != nil {
v, err := d.Int32()
s.ResourceType = int32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"SubmissionID\"")
return errors.Wrap(err, "decode field \"ResourceType\"")
}
case "ResourceID":
if err := func() error {
s.ResourceID.Reset()
if err := s.ResourceID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
}
default:
return d.Skip()
@@ -504,7 +538,7 @@ func (s *ScriptCreate) Decode(d *jx.Decoder) error {
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00000011,
0b00000111,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.

View File

@@ -6,14 +6,17 @@ package api
type OperationName = string
const (
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionReleasedOperation OperationName = "ActionSubmissionReleased"
ActionSubmissionUploadedOperation OperationName = "ActionSubmissionUploaded"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
GetScriptOperation OperationName = "GetScript"
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"
UpdateSubmissionModelOperation OperationName = "UpdateSubmissionModel"
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
ActionMapfixUploadedOperation OperationName = "ActionMapfixUploaded"
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionUploadedOperation OperationName = "ActionSubmissionUploaded"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
GetScriptOperation OperationName = "GetScript"
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"
UpdateMapfixValidatedModelOperation OperationName = "UpdateMapfixValidatedModel"
UpdateSubmissionValidatedModelOperation OperationName = "UpdateSubmissionValidatedModel"
)

View File

@@ -15,10 +15,270 @@ import (
"github.com/ogen-go/ogen/validate"
)
// ActionMapfixAcceptedParams is parameters of actionMapfixAccepted operation.
type ActionMapfixAcceptedParams struct {
// The unique identifier for a submission.
MapfixID int64
StatusMessage string
}
func unpackActionMapfixAcceptedParams(packed middleware.Parameters) (params ActionMapfixAcceptedParams) {
{
key := middleware.ParameterKey{
Name: "MapfixID",
In: "path",
}
params.MapfixID = packed[key].(int64)
}
{
key := middleware.ParameterKey{
Name: "StatusMessage",
In: "query",
}
params.StatusMessage = packed[key].(string)
}
return params
}
func decodeActionMapfixAcceptedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixAcceptedParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query())
// Decode path: MapfixID.
if err := func() error {
param := args[0]
if argsEscaped {
unescaped, err := url.PathUnescape(args[0])
if err != nil {
return errors.Wrap(err, "unescape path")
}
param = unescaped
}
if len(param) > 0 {
d := uri.NewPathDecoder(uri.PathDecoderConfig{
Param: "MapfixID",
Value: param,
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.MapfixID = c
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "MapfixID",
In: "path",
Err: err,
}
}
// Decode query: StatusMessage.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "StatusMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToString(val)
if err != nil {
return err
}
params.StatusMessage = c
return nil
}); err != nil {
return err
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: true,
MaxLength: 4096,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(params.StatusMessage)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
return err
}
} else {
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "StatusMessage",
In: "query",
Err: err,
}
}
return params, nil
}
// ActionMapfixUploadedParams is parameters of actionMapfixUploaded operation.
type ActionMapfixUploadedParams struct {
// The unique identifier for a submission.
MapfixID int64
}
func unpackActionMapfixUploadedParams(packed middleware.Parameters) (params ActionMapfixUploadedParams) {
{
key := middleware.ParameterKey{
Name: "MapfixID",
In: "path",
}
params.MapfixID = packed[key].(int64)
}
return params
}
func decodeActionMapfixUploadedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixUploadedParams, _ error) {
// Decode path: MapfixID.
if err := func() error {
param := args[0]
if argsEscaped {
unescaped, err := url.PathUnescape(args[0])
if err != nil {
return errors.Wrap(err, "unescape path")
}
param = unescaped
}
if len(param) > 0 {
d := uri.NewPathDecoder(uri.PathDecoderConfig{
Param: "MapfixID",
Value: param,
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.MapfixID = c
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "MapfixID",
In: "path",
Err: err,
}
}
return params, nil
}
// ActionMapfixValidatedParams is parameters of actionMapfixValidated operation.
type ActionMapfixValidatedParams struct {
// The unique identifier for a submission.
MapfixID int64
}
func unpackActionMapfixValidatedParams(packed middleware.Parameters) (params ActionMapfixValidatedParams) {
{
key := middleware.ParameterKey{
Name: "MapfixID",
In: "path",
}
params.MapfixID = packed[key].(int64)
}
return params
}
func decodeActionMapfixValidatedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixValidatedParams, _ error) {
// Decode path: MapfixID.
if err := func() error {
param := args[0]
if argsEscaped {
unescaped, err := url.PathUnescape(args[0])
if err != nil {
return errors.Wrap(err, "unescape path")
}
param = unescaped
}
if len(param) > 0 {
d := uri.NewPathDecoder(uri.PathDecoderConfig{
Param: "MapfixID",
Value: param,
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.MapfixID = c
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "MapfixID",
In: "path",
Err: err,
}
}
return params, nil
}
// ActionSubmissionAcceptedParams is parameters of actionSubmissionAccepted operation.
type ActionSubmissionAcceptedParams struct {
// The unique identifier for a submission.
SubmissionID int64
SubmissionID int64
StatusMessage string
}
func unpackActionSubmissionAcceptedParams(packed middleware.Parameters) (params ActionSubmissionAcceptedParams) {
@@ -29,10 +289,18 @@ func unpackActionSubmissionAcceptedParams(packed middleware.Parameters) (params
}
params.SubmissionID = packed[key].(int64)
}
{
key := middleware.ParameterKey{
Name: "StatusMessage",
In: "query",
}
params.StatusMessage = packed[key].(string)
}
return params
}
func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionAcceptedParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query())
// Decode path: SubmissionID.
if err := func() error {
param := args[0]
@@ -78,69 +346,55 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
Err: err,
}
}
return params, nil
}
// ActionSubmissionReleasedParams is parameters of actionSubmissionReleased operation.
type ActionSubmissionReleasedParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
func unpackActionSubmissionReleasedParams(packed middleware.Parameters) (params ActionSubmissionReleasedParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
In: "path",
}
params.SubmissionID = packed[key].(int64)
}
return params
}
func decodeActionSubmissionReleasedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionReleasedParams, _ error) {
// Decode path: SubmissionID.
// Decode query: StatusMessage.
if err := func() error {
param := args[0]
if argsEscaped {
unescaped, err := url.PathUnescape(args[0])
if err != nil {
return errors.Wrap(err, "unescape path")
}
param = unescaped
cfg := uri.QueryParameterDecodingConfig{
Name: "StatusMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if len(param) > 0 {
d := uri.NewPathDecoder(uri.PathDecoderConfig{
Param: "SubmissionID",
Value: param,
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
c, err := conv.ToString(val)
if err != nil {
return err
}
params.SubmissionID = c
params.StatusMessage = c
return nil
}); err != nil {
return err
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: true,
MaxLength: 4096,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(params.StatusMessage)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "SubmissionID",
In: "path",
Name: "StatusMessage",
In: "query",
Err: err,
}
}
@@ -150,8 +404,8 @@ func decodeActionSubmissionReleasedParams(args [1]string, argsEscaped bool, r *h
// ActionSubmissionUploadedParams is parameters of actionSubmissionUploaded operation.
type ActionSubmissionUploadedParams struct {
// The unique identifier for a submission.
SubmissionID int64
TargetAssetID OptInt64
SubmissionID int64
UploadedAssetID int64
}
func unpackActionSubmissionUploadedParams(packed middleware.Parameters) (params ActionSubmissionUploadedParams) {
@@ -164,12 +418,10 @@ func unpackActionSubmissionUploadedParams(packed middleware.Parameters) (params
}
{
key := middleware.ParameterKey{
Name: "TargetAssetID",
Name: "UploadedAssetID",
In: "query",
}
if v, ok := packed[key]; ok {
params.TargetAssetID = v.(OptInt64)
}
params.UploadedAssetID = packed[key].(int64)
}
return params
}
@@ -221,43 +473,38 @@ func decodeActionSubmissionUploadedParams(args [1]string, argsEscaped bool, r *h
Err: err,
}
}
// Decode query: TargetAssetID.
// Decode query: UploadedAssetID.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "TargetAssetID",
Name: "UploadedAssetID",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
var paramsDotTargetAssetIDVal int64
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
paramsDotTargetAssetIDVal = c
return nil
}(); err != nil {
val, err := d.DecodeValue()
if err != nil {
return err
}
params.TargetAssetID.SetTo(paramsDotTargetAssetIDVal)
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.UploadedAssetID = c
return nil
}); err != nil {
return err
}
} else {
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "TargetAssetID",
Name: "UploadedAssetID",
In: "query",
Err: err,
}
@@ -496,7 +743,7 @@ func decodeListScriptPolicyParams(args [0]string, argsEscaped bool, r *http.Requ
return err
}
} else {
return validate.ErrFieldRequired
return err
}
return nil
}(); err != nil {
@@ -549,7 +796,7 @@ func decodeListScriptPolicyParams(args [0]string, argsEscaped bool, r *http.Requ
return err
}
} else {
return validate.ErrFieldRequired
return err
}
return nil
}(); err != nil {
@@ -715,7 +962,8 @@ type ListScriptsParams struct {
Hash OptString
Name OptString
Source OptString
SubmissionID OptInt64
ResourceType OptInt32
ResourceID OptInt64
}
func unpackListScriptsParams(packed middleware.Parameters) (params ListScriptsParams) {
@@ -762,11 +1010,20 @@ func unpackListScriptsParams(packed middleware.Parameters) (params ListScriptsPa
}
{
key := middleware.ParameterKey{
Name: "SubmissionID",
Name: "ResourceType",
In: "query",
}
if v, ok := packed[key]; ok {
params.SubmissionID = v.(OptInt64)
params.ResourceType = v.(OptInt32)
}
}
{
key := middleware.ParameterKey{
Name: "ResourceID",
In: "query",
}
if v, ok := packed[key]; ok {
params.ResourceID = v.(OptInt64)
}
}
return params
@@ -817,7 +1074,7 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
return err
}
} else {
return validate.ErrFieldRequired
return err
}
return nil
}(); err != nil {
@@ -870,7 +1127,7 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
return err
}
} else {
return validate.ErrFieldRequired
return err
}
return nil
}(); err != nil {
@@ -1072,17 +1329,58 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
Err: err,
}
}
// Decode query: SubmissionID.
// Decode query: ResourceType.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "SubmissionID",
Name: "ResourceType",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
var paramsDotSubmissionIDVal int64
var paramsDotResourceTypeVal int32
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt32(val)
if err != nil {
return err
}
paramsDotResourceTypeVal = c
return nil
}(); err != nil {
return err
}
params.ResourceType.SetTo(paramsDotResourceTypeVal)
return nil
}); err != nil {
return err
}
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ResourceType",
In: "query",
Err: err,
}
}
// Decode query: ResourceID.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "ResourceID",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
var paramsDotResourceIDVal int64
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
@@ -1094,12 +1392,12 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
return err
}
paramsDotSubmissionIDVal = c
paramsDotResourceIDVal = c
return nil
}(); err != nil {
return err
}
params.SubmissionID.SetTo(paramsDotSubmissionIDVal)
params.ResourceID.SetTo(paramsDotResourceIDVal)
return nil
}); err != nil {
return err
@@ -1108,7 +1406,7 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "SubmissionID",
Name: "ResourceID",
In: "query",
Err: err,
}
@@ -1116,15 +1414,170 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
return params, nil
}
// UpdateSubmissionModelParams is parameters of updateSubmissionModel operation.
type UpdateSubmissionModelParams struct {
// UpdateMapfixValidatedModelParams is parameters of updateMapfixValidatedModel operation.
type UpdateMapfixValidatedModelParams struct {
// The unique identifier for a submission.
SubmissionID int64
ModelID int64
VersionID int64
MapfixID int64
ValidatedModelID int64
ValidatedModelVersion int64
}
func unpackUpdateSubmissionModelParams(packed middleware.Parameters) (params UpdateSubmissionModelParams) {
func unpackUpdateMapfixValidatedModelParams(packed middleware.Parameters) (params UpdateMapfixValidatedModelParams) {
{
key := middleware.ParameterKey{
Name: "MapfixID",
In: "path",
}
params.MapfixID = packed[key].(int64)
}
{
key := middleware.ParameterKey{
Name: "ValidatedModelID",
In: "query",
}
params.ValidatedModelID = packed[key].(int64)
}
{
key := middleware.ParameterKey{
Name: "ValidatedModelVersion",
In: "query",
}
params.ValidatedModelVersion = packed[key].(int64)
}
return params
}
func decodeUpdateMapfixValidatedModelParams(args [1]string, argsEscaped bool, r *http.Request) (params UpdateMapfixValidatedModelParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query())
// Decode path: MapfixID.
if err := func() error {
param := args[0]
if argsEscaped {
unescaped, err := url.PathUnescape(args[0])
if err != nil {
return errors.Wrap(err, "unescape path")
}
param = unescaped
}
if len(param) > 0 {
d := uri.NewPathDecoder(uri.PathDecoderConfig{
Param: "MapfixID",
Value: param,
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.MapfixID = c
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "MapfixID",
In: "path",
Err: err,
}
}
// Decode query: ValidatedModelID.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "ValidatedModelID",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.ValidatedModelID = c
return nil
}); err != nil {
return err
}
} else {
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ValidatedModelID",
In: "query",
Err: err,
}
}
// Decode query: ValidatedModelVersion.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "ValidatedModelVersion",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.ValidatedModelVersion = c
return nil
}); err != nil {
return err
}
} else {
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ValidatedModelVersion",
In: "query",
Err: err,
}
}
return params, nil
}
// UpdateSubmissionValidatedModelParams is parameters of updateSubmissionValidatedModel operation.
type UpdateSubmissionValidatedModelParams struct {
// The unique identifier for a submission.
SubmissionID int64
ValidatedModelID int64
ValidatedModelVersion int64
}
func unpackUpdateSubmissionValidatedModelParams(packed middleware.Parameters) (params UpdateSubmissionValidatedModelParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
@@ -1134,22 +1587,22 @@ func unpackUpdateSubmissionModelParams(packed middleware.Parameters) (params Upd
}
{
key := middleware.ParameterKey{
Name: "ModelID",
Name: "ValidatedModelID",
In: "query",
}
params.ModelID = packed[key].(int64)
params.ValidatedModelID = packed[key].(int64)
}
{
key := middleware.ParameterKey{
Name: "VersionID",
Name: "ValidatedModelVersion",
In: "query",
}
params.VersionID = packed[key].(int64)
params.ValidatedModelVersion = packed[key].(int64)
}
return params
}
func decodeUpdateSubmissionModelParams(args [1]string, argsEscaped bool, r *http.Request) (params UpdateSubmissionModelParams, _ error) {
func decodeUpdateSubmissionValidatedModelParams(args [1]string, argsEscaped bool, r *http.Request) (params UpdateSubmissionValidatedModelParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query())
// Decode path: SubmissionID.
if err := func() error {
@@ -1196,10 +1649,10 @@ func decodeUpdateSubmissionModelParams(args [1]string, argsEscaped bool, r *http
Err: err,
}
}
// Decode query: ModelID.
// Decode query: ValidatedModelID.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "ModelID",
Name: "ValidatedModelID",
Style: uri.QueryStyleForm,
Explode: true,
}
@@ -1216,26 +1669,26 @@ func decodeUpdateSubmissionModelParams(args [1]string, argsEscaped bool, r *http
return err
}
params.ModelID = c
params.ValidatedModelID = c
return nil
}); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ModelID",
Name: "ValidatedModelID",
In: "query",
Err: err,
}
}
// Decode query: VersionID.
// Decode query: ValidatedModelVersion.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "VersionID",
Name: "ValidatedModelVersion",
Style: uri.QueryStyleForm,
Explode: true,
}
@@ -1252,18 +1705,18 @@ func decodeUpdateSubmissionModelParams(args [1]string, argsEscaped bool, r *http
return err
}
params.VersionID = c
params.ValidatedModelVersion = c
return nil
}); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
return err
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "VersionID",
Name: "ValidatedModelVersion",
In: "query",
Err: err,
}

View File

@@ -15,11 +15,11 @@ import (
"github.com/ogen-go/ogen/validate"
)
func decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSubmissionAcceptedNoContent, _ error) {
func decodeActionMapfixAcceptedResponse(resp *http.Response) (res *ActionMapfixAcceptedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionAcceptedNoContent{}, nil
return &ActionMapfixAcceptedNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
@@ -66,11 +66,113 @@ func decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSub
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionReleasedResponse(resp *http.Response) (res *ActionSubmissionReleasedNoContent, _ error) {
func decodeActionMapfixUploadedResponse(resp *http.Response) (res *ActionMapfixUploadedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionReleasedNoContent{}, nil
return &ActionMapfixUploadedNoContent{}, 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
}
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 decodeActionMapfixValidatedResponse(resp *http.Response) (res *ActionMapfixValidatedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionMapfixValidatedNoContent{}, 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
}
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 decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSubmissionAcceptedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionAcceptedNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
@@ -711,11 +813,62 @@ func decodeListScriptsResponse(resp *http.Response) (res []Script, _ error) {
return res, errors.Wrap(defRes, "error")
}
func decodeUpdateSubmissionModelResponse(resp *http.Response) (res *UpdateSubmissionModelNoContent, _ error) {
func decodeUpdateMapfixValidatedModelResponse(resp *http.Response) (res *UpdateMapfixValidatedModelNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &UpdateSubmissionModelNoContent{}, nil
return &UpdateMapfixValidatedModelNoContent{}, 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
}
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 decodeUpdateSubmissionValidatedModelResponse(resp *http.Response) (res *UpdateSubmissionValidatedModelNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &UpdateSubmissionValidatedModelNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {

View File

@@ -13,14 +13,28 @@ import (
ht "github.com/ogen-go/ogen/http"
)
func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNoContent, w http.ResponseWriter, span trace.Span) error {
func encodeActionMapfixAcceptedResponse(response *ActionMapfixAcceptedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionReleasedResponse(response *ActionSubmissionReleasedNoContent, w http.ResponseWriter, span trace.Span) error {
func encodeActionMapfixUploadedResponse(response *ActionMapfixUploadedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixValidatedResponse(response *ActionMapfixValidatedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@@ -119,7 +133,14 @@ func encodeListScriptsResponse(response []Script, w http.ResponseWriter, span tr
return nil
}
func encodeUpdateSubmissionModelResponse(response *UpdateSubmissionModelNoContent, w http.ResponseWriter, span trace.Span) error {
func encodeUpdateMapfixValidatedModelResponse(response *UpdateMapfixValidatedModelNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeUpdateSubmissionValidatedModelResponse(response *UpdateSubmissionValidatedModelNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))

File diff suppressed because it is too large Load Diff

View File

@@ -10,12 +10,18 @@ func (s *ErrorStatusCode) Error() string {
return fmt.Sprintf("code %d: %+v", s.StatusCode, s.Response)
}
// ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation.
type ActionMapfixAcceptedNoContent struct{}
// ActionMapfixUploadedNoContent is response for ActionMapfixUploaded operation.
type ActionMapfixUploadedNoContent struct{}
// ActionMapfixValidatedNoContent is response for ActionMapfixValidated operation.
type ActionMapfixValidatedNoContent struct{}
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
type ActionSubmissionAcceptedNoContent struct{}
// ActionSubmissionReleasedNoContent is response for ActionSubmissionReleased operation.
type ActionSubmissionReleasedNoContent struct{}
// ActionSubmissionUploadedNoContent is response for ActionSubmissionUploaded operation.
type ActionSubmissionUploadedNoContent struct{}
@@ -234,7 +240,8 @@ type Script struct {
Name string `json:"Name"`
Hash string `json:"Hash"`
Source string `json:"Source"`
SubmissionID int64 `json:"SubmissionID"`
ResourceType int32 `json:"ResourceType"`
ResourceID int64 `json:"ResourceID"`
}
// GetID returns the value of ID.
@@ -257,9 +264,14 @@ func (s *Script) GetSource() string {
return s.Source
}
// GetSubmissionID returns the value of SubmissionID.
func (s *Script) GetSubmissionID() int64 {
return s.SubmissionID
// GetResourceType returns the value of ResourceType.
func (s *Script) GetResourceType() int32 {
return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *Script) GetResourceID() int64 {
return s.ResourceID
}
// SetID sets the value of ID.
@@ -282,16 +294,22 @@ func (s *Script) SetSource(val string) {
s.Source = val
}
// SetSubmissionID sets the value of SubmissionID.
func (s *Script) SetSubmissionID(val int64) {
s.SubmissionID = val
// SetResourceType sets the value of ResourceType.
func (s *Script) SetResourceType(val int32) {
s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *Script) SetResourceID(val int64) {
s.ResourceID = val
}
// Ref: #/components/schemas/ScriptCreate
type ScriptCreate struct {
Name string `json:"Name"`
Source string `json:"Source"`
SubmissionID OptInt64 `json:"SubmissionID"`
ResourceType int32 `json:"ResourceType"`
ResourceID OptInt64 `json:"ResourceID"`
}
// GetName returns the value of Name.
@@ -304,9 +322,14 @@ func (s *ScriptCreate) GetSource() string {
return s.Source
}
// GetSubmissionID returns the value of SubmissionID.
func (s *ScriptCreate) GetSubmissionID() OptInt64 {
return s.SubmissionID
// GetResourceType returns the value of ResourceType.
func (s *ScriptCreate) GetResourceType() int32 {
return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *ScriptCreate) GetResourceID() OptInt64 {
return s.ResourceID
}
// SetName sets the value of Name.
@@ -319,9 +342,14 @@ func (s *ScriptCreate) SetSource(val string) {
s.Source = val
}
// SetSubmissionID sets the value of SubmissionID.
func (s *ScriptCreate) SetSubmissionID(val OptInt64) {
s.SubmissionID = val
// SetResourceType sets the value of ResourceType.
func (s *ScriptCreate) SetResourceType(val int32) {
s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *ScriptCreate) SetResourceID(val OptInt64) {
s.ResourceID = val
}
// Ref: #/components/schemas/ScriptPolicy
@@ -409,5 +437,8 @@ func (s *ScriptPolicyCreate) SetPolicy(val int32) {
s.Policy = val
}
// UpdateSubmissionModelNoContent is response for UpdateSubmissionModel operation.
type UpdateSubmissionModelNoContent struct{}
// UpdateMapfixValidatedModelNoContent is response for UpdateMapfixValidatedModel operation.
type UpdateMapfixValidatedModelNoContent struct{}
// UpdateSubmissionValidatedModelNoContent is response for UpdateSubmissionValidatedModel operation.
type UpdateSubmissionValidatedModelNoContent struct{}

View File

@@ -8,18 +8,30 @@ import (
// Handler handles operations described by OpenAPI v3 specification.
type Handler interface {
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
ActionMapfixUploaded(ctx context.Context, params ActionMapfixUploadedParams) error
// ActionMapfixValidated implements actionMapfixValidated operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
ActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) error
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/validator-failed
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionReleased implements actionSubmissionReleased operation.
//
// (Internal endpoint) Role Releaser changes status from releasing -> released.
//
// POST /submissions/{SubmissionID}/status/releaser-released
ActionSubmissionReleased(ctx context.Context, params ActionSubmissionReleasedParams) error
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@@ -62,12 +74,18 @@ type Handler interface {
//
// GET /scripts
ListScripts(ctx context.Context, params ListScriptsParams) ([]Script, error)
// UpdateSubmissionModel implements updateSubmissionModel operation.
// UpdateMapfixValidatedModel implements updateMapfixValidatedModel operation.
//
// Update model following role restrictions.
// Update validated model.
//
// POST /submissions/{SubmissionID}/model
UpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) error
// POST /mapfixes/{MapfixID}/validated-model
UpdateMapfixValidatedModel(ctx context.Context, params UpdateMapfixValidatedModelParams) error
// UpdateSubmissionValidatedModel implements updateSubmissionValidatedModel operation.
//
// Update validated model.
//
// POST /submissions/{SubmissionID}/validated-model
UpdateSubmissionValidatedModel(ctx context.Context, params UpdateSubmissionValidatedModelParams) error
// NewError creates *ErrorStatusCode from error returned by handler.
//
// Used for common default response.

View File

@@ -13,6 +13,33 @@ type UnimplementedHandler struct{}
var _ Handler = UnimplementedHandler{}
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
func (UnimplementedHandler) ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
func (UnimplementedHandler) ActionMapfixUploaded(ctx context.Context, params ActionMapfixUploadedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixValidated implements actionMapfixValidated operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
func (UnimplementedHandler) ActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
@@ -22,15 +49,6 @@ func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params
return ht.ErrNotImplemented
}
// ActionSubmissionReleased implements actionSubmissionReleased operation.
//
// (Internal endpoint) Role Releaser changes status from releasing -> released.
//
// POST /submissions/{SubmissionID}/status/releaser-released
func (UnimplementedHandler) ActionSubmissionReleased(ctx context.Context, params ActionSubmissionReleasedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@@ -94,12 +112,21 @@ func (UnimplementedHandler) ListScripts(ctx context.Context, params ListScriptsP
return r, ht.ErrNotImplemented
}
// UpdateSubmissionModel implements updateSubmissionModel operation.
// UpdateMapfixValidatedModel implements updateMapfixValidatedModel operation.
//
// Update model following role restrictions.
// Update validated model.
//
// POST /submissions/{SubmissionID}/model
func (UnimplementedHandler) UpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) error {
// POST /mapfixes/{MapfixID}/validated-model
func (UnimplementedHandler) UpdateMapfixValidatedModel(ctx context.Context, params UpdateMapfixValidatedModelParams) error {
return ht.ErrNotImplemented
}
// UpdateSubmissionValidatedModel implements updateSubmissionValidatedModel operation.
//
// Update validated model.
//
// POST /submissions/{SubmissionID}/validated-model
func (UnimplementedHandler) UpdateSubmissionValidatedModel(ctx context.Context, params UpdateSubmissionValidatedModelParams) error {
return ht.ErrNotImplemented
}

40
pkg/model/mapfix.go Normal file
View File

@@ -0,0 +1,40 @@
package model
import "time"
type MapfixStatus int32
const (
// Phase: Final MapfixStatus
MapfixStatusRejected MapfixStatus = 8
MapfixStatusUploaded MapfixStatus = 7 // uploaded to the group, final status for mapfixes
// Phase: Testing
MapfixStatusUploading MapfixStatus = 6
MapfixStatusValidated MapfixStatus = 5
MapfixStatusValidating MapfixStatus = 4
MapfixStatusAccepted MapfixStatus = 3 // pending script review, can re-trigger validation
// Phase: Creation
MapfixStatusChangesRequested MapfixStatus = 2
MapfixStatusSubmitted MapfixStatus = 1
MapfixStatusUnderConstruction MapfixStatus = 0
)
type Mapfix struct {
ID int64 `gorm:"primaryKey"`
DisplayName string
Creator string
GameID int32
CreatedAt time.Time
UpdatedAt time.Time
Submitter int64 // UserID
AssetID int64
AssetVersion int64
ValidatedAssetID int64
ValidatedAssetVersion int64
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.
StatusID MapfixStatus
StatusMessage string
}

View File

@@ -5,24 +5,31 @@ package model
// Requests are sent from maps-service to validator
type ValidateRequest struct {
type ValidateSubmissionRequest struct {
// submission_id is passed back in the response message
SubmissionID int64
ModelID int64
ModelVersion int64
ValidatedModelID int64 // optional value
ValidatedModelID *int64 // optional value
}
type ValidateMapfixRequest struct {
MapfixID int64
ModelID int64
ModelVersion int64
ValidatedModelID *int64 // optional value
}
// Create a new map
type PublishNewRequest struct {
type UploadSubmissionRequest struct {
SubmissionID int64
ModelID int64
ModelVersion int64
ModelName string
}
type PublishFixRequest struct {
SubmissionID int64
type UploadMapfixRequest struct {
MapfixID int64
ModelID int64
ModelVersion int64
TargetAssetID int64

View File

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

View File

@@ -2,24 +2,24 @@ package model
import "time"
type Status int32
type SubmissionStatus int32
const (
// Phase: Final Status
StatusReleased Status = 9
StatusRejected Status = 8
// Phase: Final SubmissionStatus
SubmissionStatusReleased SubmissionStatus = 9
SubmissionStatusRejected SubmissionStatus = 8
// Phase: Testing
StatusUploaded Status = 7 // uploaded to the group, but pending release
StatusUploading Status = 6
StatusValidated Status = 5
StatusValidating Status = 4
StatusAccepted Status = 3 // pending script review, can re-trigger validation
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
// Phase: Creation
StatusChangesRequested Status = 2
StatusSubmitted Status = 1
StatusUnderConstruction Status = 0
SubmissionStatusChangesRequested SubmissionStatus = 2
SubmissionStatusSubmitted SubmissionStatus = 1
SubmissionStatusUnderConstruction SubmissionStatus = 0
)
type Submission struct {
@@ -32,7 +32,10 @@ type Submission struct {
Submitter int64 // UserID
AssetID int64
AssetVersion int64
ValidatedAssetID int64
ValidatedAssetVersion int64
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.
StatusID Status
UploadedAssetID int64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
StatusID SubmissionStatus
StatusMessage string
}

617
pkg/service/mapfixes.go Normal file
View File

@@ -0,0 +1,617 @@
package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
var(
CreationPhaseMapfixesLimit = 20
CreationPhaseMapfixStatuses = []model.MapfixStatus{
model.MapfixStatusChangesRequested,
model.MapfixStatusSubmitted,
model.MapfixStatusUnderConstruction,
}
// prevent two mapfixes with same asset id
ActiveMapfixStatuses = []model.MapfixStatus{
model.MapfixStatusUploading,
model.MapfixStatusValidated,
model.MapfixStatusValidating,
model.MapfixStatusAccepted,
model.MapfixStatusChangesRequested,
model.MapfixStatusSubmitted,
model.MapfixStatusUnderConstruction,
}
// limit mapfixes in the pipeline to one per target map
ActiveAcceptedMapfixStatuses = []model.MapfixStatus{
model.MapfixStatusUploading,
model.MapfixStatusValidated,
model.MapfixStatusValidating,
model.MapfixStatusAccepted,
}
)
var (
ErrCreationPhaseMapfixesLimit = errors.New("Active mapfixes limited to 20")
ErrActiveMapfixSameAssetID = errors.New("There is an active mapfix with the same AssetID")
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)
)
// POST /mapfixes
func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
userId, err := userInfo.GetUserID()
if err != nil {
return nil, err
}
// Check if user's mapfixes in the creation phase exceeds the limit
{
filter := datastore.Optional()
filter.Add("submitter", int64(userId))
filter.Add("status_id", CreationPhaseMapfixStatuses)
creation_mapfixes, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
Number: 1,
Size: int32(CreationPhaseMapfixesLimit),
},datastore.ListSortDisabled)
if err != nil {
return nil, err
}
if CreationPhaseMapfixesLimit <= len(creation_mapfixes) {
return nil, ErrCreationPhaseMapfixesLimit
}
}
// Check if an active mapfix with the same asset id exists
{
filter := datastore.Optional()
filter.Add("asset_id", request.AssetID)
filter.Add("asset_version", request.AssetVersion)
filter.Add("status_id", ActiveMapfixStatuses)
active_mapfixes, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
Number: 1,
Size: 1,
},datastore.ListSortDisabled)
if err != nil {
return nil, err
}
if len(active_mapfixes) != 0{
return nil, ErrActiveMapfixSameAssetID
}
}
mapfix, err := svc.DB.Mapfixes().Create(ctx, model.Mapfix{
ID: 0,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: request.GameID,
Submitter: int64(userId),
AssetID: request.AssetID,
AssetVersion: request.AssetVersion,
Completed: false,
TargetAssetID: request.TargetAssetID,
StatusID: model.MapfixStatusUnderConstruction,
})
if err != nil {
return nil, err
}
return &api.ID{
ID: mapfix.ID,
}, nil
}
// GetMapfix implements getMapfix operation.
//
// Retrieve map with ID.
//
// 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)
if err != nil {
return nil, err
}
return &api.Mapfix{
ID: mapfix.ID,
DisplayName: mapfix.DisplayName,
Creator: mapfix.Creator,
GameID: mapfix.GameID,
CreatedAt: mapfix.CreatedAt.Unix(),
UpdatedAt: mapfix.UpdatedAt.Unix(),
Submitter: int64(mapfix.Submitter),
AssetID: int64(mapfix.AssetID),
AssetVersion: int64(mapfix.AssetVersion),
Completed: mapfix.Completed,
TargetAssetID: int64(mapfix.TargetAssetID),
StatusID: int32(mapfix.StatusID),
StatusMessage: mapfix.StatusMessage,
}, nil
}
// ListMapfixes implements listMapfixes operation.
//
// Get list of mapfixes.
//
// GET /mapfixes
func (svc *Service) ListMapfixes(ctx context.Context, params api.ListMapfixesParams) ([]api.Mapfix, error) {
filter := datastore.Optional()
if params.DisplayName.IsSet(){
filter.Add("display_name", params.DisplayName.Value)
}
if params.Creator.IsSet(){
filter.Add("creator", params.Creator.Value)
}
if params.GameID.IsSet(){
filter.Add("game_id", params.GameID.Value)
}
sort := datastore.ListSort(params.Sort.Or(int32(datastore.ListSortDisabled)))
items, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
},sort)
if err != nil {
return nil, err
}
var resp []api.Mapfix
for _, item := range items {
resp = append(resp, 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),
Completed: item.Completed,
TargetAssetID: int64(item.TargetAssetID),
StatusID: int32(item.StatusID),
})
}
return resp, nil
}
// PatchMapfixCompleted implements patchMapfixCompleted operation.
//
// Retrieve map with ID.
//
// POST /mapfixes/{MapfixID}/completed
func (svc *Service) SetMapfixCompleted(ctx context.Context, params api.SetMapfixCompletedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleMaptest()
if err != nil {
return err
}
// check if caller has MaptestGame role (request must originate from a maptest roblox game)
if !has_role {
return ErrPermissionDeniedNeedRoleMaptest
}
pmap := datastore.Optional()
pmap.Add("completed", true)
return svc.DB.Mapfixes().Update(ctx, params.MapfixID, pmap)
}
// UpdateMapfixModel implements patchMapfixModel operation.
//
// Update model following role restrictions.
//
// POST /mapfixes/{MapfixID}/model
func (svc *Service) UpdateMapfixModel(ctx context.Context, params api.UpdateMapfixModelParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
// read mapfix (this could be done with a transaction WHERE clause)
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
if err != nil {
return err
}
has_role, err := userInfo.IsSubmitter(uint64(mapfix.Submitter))
if err != nil {
return err
}
// check if caller is the submitter
if !has_role {
return ErrPermissionDeniedNotSubmitter
}
// check if Status is ChangesRequested|Submitted|UnderConstruction
pmap := datastore.Optional()
pmap.AddNotNil("asset_id", params.ModelID)
pmap.AddNotNil("asset_version", params.VersionID)
//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)
}
// ActionMapfixReject invokes actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
//
// POST /mapfixes/{MapfixID}/status/reject
func (svc *Service) ActionMapfixReject(ctx context.Context, params api.ActionMapfixRejectParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleMapfixReview()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusRejected)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitted}, smap)
}
// ActionMapfixRequestChanges invokes actionMapfixRequestChanges operation.
//
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
//
// POST /mapfixes/{MapfixID}/status/request-changes
func (svc *Service) ActionMapfixRequestChanges(ctx context.Context, params api.ActionMapfixRequestChangesParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleMapfixReview()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
}
// 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)
}
// ActionMapfixRevoke invokes actionMapfixRevoke operation.
//
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
//
// POST /mapfixes/{MapfixID}/status/revoke
func (svc *Service) ActionMapfixRevoke(ctx context.Context, params api.ActionMapfixRevokeParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
// read mapfix (this could be done with a transaction WHERE clause)
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
if err != nil {
return err
}
has_role, err := userInfo.IsSubmitter(uint64(mapfix.Submitter))
if err != nil {
return err
}
// check if caller is the submitter
if !has_role {
return ErrPermissionDeniedNotSubmitter
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusUnderConstruction)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitted, model.MapfixStatusChangesRequested}, smap)
}
// ActionMapfixSubmit invokes actionMapfixSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/submit
func (svc *Service) ActionMapfixSubmit(ctx context.Context, params api.ActionMapfixSubmitParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
// read mapfix (this could be done with a transaction WHERE clause)
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
if err != nil {
return err
}
has_role, err := userInfo.IsSubmitter(uint64(mapfix.Submitter))
if err != nil {
return err
}
// check if caller is the submitter
if !has_role {
return ErrPermissionDeniedNotSubmitter
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusSubmitted)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUnderConstruction, model.MapfixStatusChangesRequested}, smap)
}
// ActionMapfixTriggerUpload invokes actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
//
// POST /mapfixes/{MapfixID}/status/trigger-upload
func (svc *Service) ActionMapfixTriggerUpload(ctx context.Context, params api.ActionMapfixTriggerUploadParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleMapfixUpload()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapUpload
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusUploading)
mapfix, err := svc.DB.Mapfixes().IfStatusThenUpdateAndGet(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidated}, smap)
if err != nil {
return err
}
// this is a map fix
upload_fix_request := model.UploadMapfixRequest{
MapfixID: mapfix.ID,
ModelID: mapfix.ValidatedAssetID,
ModelVersion: mapfix.ValidatedAssetVersion,
TargetAssetID: mapfix.TargetAssetID,
}
j, err := json.Marshal(upload_fix_request)
if err != nil {
return err
}
svc.Nats.Publish("maptest.mapfixes.uploadfix", []byte(j))
return nil
}
// ActionMapfixValidate invokes actionMapfixValidate operation.
//
// Role MapfixRelease changes status from Uploading -> Validated.
//
// POST /mapfixes/{MapfixID}/status/reset-uploading
func (svc *Service) ActionMapfixValidated(ctx context.Context, params api.ActionMapfixValidatedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleMapfixUpload()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapUpload
}
// 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
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusValidated)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUploading}, smap)
}
// ActionMapfixTriggerValidate invokes actionMapfixTriggerValidate operation.
//
// Role Reviewer triggers validation and changes status from Submitted -> Validating.
//
// POST /mapfixes/{MapfixID}/status/trigger-validate
func (svc *Service) ActionMapfixTriggerValidate(ctx context.Context, params api.ActionMapfixTriggerValidateParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleMapfixReview()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
}
// read mapfix (this could be done with a transaction WHERE clause)
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
if err != nil {
return err
}
has_role, err = userInfo.IsSubmitter(uint64(mapfix.Submitter))
if err != nil {
return err
}
// check if caller is NOT the submitter
if has_role {
return ErrAcceptOwnMapfix
}
// Check if an active mapfix with the same target asset id exists
if mapfix.TargetAssetID != 0 {
filter := datastore.Optional()
filter.Add("target_asset_id", mapfix.TargetAssetID)
filter.Add("status_id", ActiveAcceptedMapfixStatuses)
active_mapfixes, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
Number: 1,
Size: 1,
},datastore.ListSortDisabled)
if err != nil {
return err
}
if len(active_mapfixes) != 0{
return ErrActiveMapfixSameTargetAssetID
}
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusValidating)
mapfix, err = svc.DB.Mapfixes().IfStatusThenUpdateAndGet(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitted}, smap)
if err != nil {
return err
}
validate_request := model.ValidateMapfixRequest{
MapfixID: mapfix.ID,
ModelID: mapfix.AssetID,
ModelVersion: mapfix.AssetVersion,
ValidatedModelID: nil,
}
// sentinel values because we're not using rust
if mapfix.ValidatedAssetID != 0 {
validate_request.ValidatedModelID = &mapfix.ValidatedAssetID
}
j, err := json.Marshal(validate_request)
if err != nil {
return err
}
svc.Nats.Publish("maptest.mapfixes.validate", []byte(j))
return nil
}
// ActionMapfixRetryValidate invokes actionMapfixRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
//
// POST /mapfixes/{MapfixID}/status/retry-validate
func (svc *Service) ActionMapfixRetryValidate(ctx context.Context, params api.ActionMapfixRetryValidateParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleMapfixReview()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusValidating)
mapfix, err := svc.DB.Mapfixes().IfStatusThenUpdateAndGet(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusAccepted}, smap)
if err != nil {
return err
}
validate_request := model.ValidateMapfixRequest{
MapfixID: mapfix.ID,
ModelID: mapfix.AssetID,
ModelVersion: mapfix.AssetVersion,
ValidatedModelID: nil,
}
// sentinel values because we're not using rust
if mapfix.ValidatedAssetID != 0 {
validate_request.ValidatedModelID = &mapfix.ValidatedAssetID
}
j, err := json.Marshal(validate_request)
if err != nil {
return err
}
svc.Nats.Publish("maptest.mapfixes.validate", []byte(j))
return nil
}
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// Role MapfixReview changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/reset-validating
func (svc *Service) ActionMapfixAccepted(ctx context.Context, params api.ActionMapfixAcceptedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleMapfixReview()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
}
// 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
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusAccepted)
smap.Add("status_message", "Manually forced reset")
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
}

View File

@@ -14,7 +14,7 @@ import (
//
// POST /script-policy
func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolicyCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
@@ -81,12 +81,12 @@ func (svc *Service) ListScriptPolicy(ctx context.Context, params api.ListScriptP
}
var resp []api.ScriptPolicy
for i := 0; i < len(items); i++ {
for _, item := range items {
resp = append(resp, api.ScriptPolicy{
ID: items[i].ID,
FromScriptHash: model.HashFormat(uint64(items[i].FromScriptHash)),
ToScriptID: items[i].ToScriptID,
Policy: int32(items[i].Policy),
ID: item.ID,
FromScriptHash: model.HashFormat(uint64(item.FromScriptHash)),
ToScriptID: item.ToScriptID,
Policy: int32(item.Policy),
})
}
@@ -99,7 +99,7 @@ func (svc *Service) ListScriptPolicy(ctx context.Context, params api.ListScriptP
//
// DELETE /script-policy/{ScriptPolicyID}
func (svc *Service) DeleteScriptPolicy(ctx context.Context, params api.DeleteScriptPolicyParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
@@ -121,7 +121,7 @@ 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").(UserInfo)
_, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
@@ -147,7 +147,7 @@ func (svc *Service) GetScriptPolicy(ctx context.Context, params api.GetScriptPol
//
// POST /script-policy/{ScriptPolicyID}
func (svc *Service) UpdateScriptPolicy(ctx context.Context, req *api.ScriptPolicyUpdate, params api.UpdateScriptPolicyParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}

View File

@@ -14,7 +14,7 @@ import (
//
// POST /scripts
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
@@ -32,7 +32,8 @@ func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*a
Name: req.Name,
Hash: int64(model.HashSource(req.Source)),
Source: req.Source,
SubmissionID: req.SubmissionID.Or(0),
ResourceType: model.ResourceType(req.ResourceType),
ResourceID: req.ResourceID.Or(0),
})
if err != nil {
return nil, err
@@ -64,8 +65,11 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
if params.Source.IsSet(){
filter.AddNotNil("source", params.Source.Value)
}
if params.SubmissionID.IsSet(){
filter.AddNotNil("submission_id", params.SubmissionID.Value)
if params.ResourceType.IsSet(){
filter.AddNotNil("resource_type", params.ResourceType.Value)
}
if params.ResourceID.IsSet(){
filter.AddNotNil("resource_id", params.ResourceID.Value)
}
items, err := svc.DB.Scripts().List(ctx, filter, model.Page{
@@ -77,12 +81,13 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
}
var resp []api.Script
for i := 0; i < len(items); i++ {
for _, item := range items {
resp = append(resp, api.Script{
ID: items[i].ID,
Hash: model.HashFormat(uint64(items[i].Hash)),
Source: items[i].Source,
SubmissionID: items[i].SubmissionID,
ID: item.ID,
Hash: model.HashFormat(uint64(item.Hash)),
Source: item.Source,
ResourceType: int32(item.ResourceType),
ResourceID: item.ResourceID,
})
}
@@ -95,7 +100,7 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
//
// DELETE /scripts/{ScriptID}
func (svc *Service) DeleteScript(ctx context.Context, params api.DeleteScriptParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
@@ -117,7 +122,7 @@ 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").(UserInfo)
_, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
@@ -134,7 +139,8 @@ func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (
Name: script.Name,
Hash: model.HashFormat(uint64(script.Hash)),
Source: script.Source,
SubmissionID: script.SubmissionID,
ResourceType: int32(script.ResourceType),
ResourceID: script.ResourceID,
}, nil
}
@@ -144,7 +150,7 @@ func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (
//
// PATCH /scripts/{ScriptID}
func (svc *Service) UpdateScript(ctx context.Context, req *api.ScriptUpdate, params api.UpdateScriptParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
@@ -165,8 +171,11 @@ func (svc *Service) UpdateScript(ctx context.Context, req *api.ScriptUpdate, par
pmap.Add("source", source)
pmap.Add("hash", int64(model.HashSource(source))) // No type safety!
}
if SubmissionID, ok := req.SubmissionID.Get(); ok {
pmap.Add("submission_id", SubmissionID)
if ResourceType, ok := req.ResourceType.Get(); ok {
pmap.Add("resource_type", ResourceType)
}
if ResourceID, ok := req.ResourceID.Get(); ok {
pmap.Add("resource_id", ResourceID)
}
return svc.DB.Scripts().Update(ctx, req.ID, pmap)
}

View File

@@ -14,24 +14,66 @@ var (
ErrInvalidSession = errors.New("Session invalid")
)
type Role int32
// Submissions roles bitflag
type Roles int32
var (
// has ScriptWrite
RoleQuat Role = 255
// has SubmissionPublish
RoleMapAdmin Role = 128
// has SubmissionReview
RoleMapCouncil Role = 64
// Only users with this role are allowed to submit models they don't own
RolesSubmissionCreateNotModelOwner Roles = 1<<8
RolesMapfixCreateNotModelOwner Roles = 1<<7
RolesSubmissionUpload Roles = 1<<6
RolesSubmissionReview Roles = 1<<5
RolesSubmissionRelease Roles = 1<<4
RolesScriptWrite Roles = 1<<3
RolesMapfixUpload Roles = 1<<2
RolesMapfixReview Roles = 1<<1
RolesMapDownload Roles = 1<<0
RolesEmpty Roles = 0
)
type UserInfo struct {
// StrafesNET group roles
type GroupRole int32
var (
// has ScriptWrite
RoleQuat GroupRole = 255
RoleItzaname GroupRole = 254
RoleStagingDeveloper GroupRole = 240
RolesAll Roles = ^RolesEmpty
// has SubmissionUpload
RoleMapAdmin GroupRole = 128
RolesMapAdmin Roles = RolesSubmissionRelease|RolesSubmissionUpload|RolesSubmissionReview|RolesSubmissionCreateNotModelOwner|RolesMapCouncil
// has MapfixReview
RoleMapCouncil GroupRole = 64
RolesMapCouncil Roles = RolesMapfixReview|RolesMapfixUpload|RolesMapfixCreateNotModelOwner|RolesMapAccess
// access to downloading maps
RoleMapAccess GroupRole = 32
RolesMapAccess Roles = RolesMapDownload
)
type UserInfoHandle struct {
// Would love to know a better way to do this
svc *SecurityHandler
ctx *context.Context
sessionId string
}
type UserInfo struct {
UserID uint64
Username string
AvatarURL string
}
func (usr UserInfo) GetUserID() (uint64, error) {
func (usr UserInfoHandle) GetUserInfo() (userInfo UserInfo, err error) {
session, err := usr.svc.Client.GetSessionUser(*usr.ctx, &auth.IdMessage{
SessionID: usr.sessionId,
})
if err != nil {
return userInfo, err
}
userInfo.UserID = session.UserID
userInfo.Username = session.Username
userInfo.AvatarURL = session.AvatarURL
return userInfo, nil
}
func (usr UserInfoHandle) GetUserID() (uint64, error) {
session, err := usr.svc.Client.GetSessionUser(*usr.ctx, &auth.IdMessage{
SessionID: usr.sessionId,
})
@@ -40,43 +82,86 @@ func (usr UserInfo) GetUserID() (uint64, error) {
}
return session.UserID, nil
}
func (usr UserInfo) IsSubmitter(submitter uint64) (bool, error) {
func (usr UserInfoHandle) Validate() (bool, error) {
validate, err := usr.svc.Client.ValidateSession(*usr.ctx, &auth.IdMessage{
SessionID: usr.sessionId,
})
if err != nil {
return false, err
}
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 UserInfo) hasRole(role Role) (bool, error) {
roles, err := usr.svc.Client.GetGroupRole(*usr.ctx, &auth.IdMessage{
SessionID: usr.sessionId,
})
func (usr UserInfoHandle) hasRoles(wantRoles Roles) (bool, error) {
haveroles, err := usr.GetRoles()
if err != nil {
return false, err
}
return haveroles & wantRoles == wantRoles, nil
}
func (usr UserInfoHandle) GetRoles() (Roles, error) {
roles, err := usr.svc.Client.GetGroupRole(*usr.ctx, &auth.IdMessage{
SessionID: usr.sessionId,
})
if err != nil {
return RolesEmpty, err
}
// map roles into bitflag
rolesBitflag := RolesEmpty;
for _, r := range roles.Roles {
if int32(role) <= r.Rank {
return true, nil
switch GroupRole(r.Rank){
case RoleQuat, RoleItzaname, RoleStagingDeveloper:
rolesBitflag|=RolesAll
case RoleMapAdmin:
rolesBitflag|=RolesMapAdmin
case RoleMapCouncil:
rolesBitflag|=RolesMapCouncil
case RoleMapAccess:
rolesBitflag|=RolesMapAccess
}
}
return false, nil
return rolesBitflag, nil
}
// RoleThumbnail
// RoleMapDownload
func (usr UserInfo) HasRoleSubmissionRelease() (bool, error) {
return usr.hasRole(RoleMapAdmin)
func (usr UserInfoHandle) HasRoleMapfixCreateNotModelOwner() (bool, error) {
return usr.hasRoles(RolesMapfixCreateNotModelOwner)
}
func (usr UserInfo) HasRoleSubmissionReview() (bool, error) {
return usr.hasRole(RoleMapCouncil)
func (usr UserInfoHandle) HasRoleSubmissionCreateNotModelOwner() (bool, error) {
return usr.hasRoles(RolesSubmissionCreateNotModelOwner)
}
func (usr UserInfo) HasRoleScriptWrite() (bool, error) {
return usr.hasRole(RoleQuat)
func (usr UserInfoHandle) HasRoleMapfixUpload() (bool, error) {
return usr.hasRoles(RolesMapfixUpload)
}
func (usr UserInfoHandle) HasRoleMapfixReview() (bool, error) {
return usr.hasRoles(RolesMapfixReview)
}
func (usr UserInfoHandle) HasRoleMapDownload() (bool, error) {
return usr.hasRoles(RolesMapDownload)
}
func (usr UserInfoHandle) HasRoleSubmissionRelease() (bool, error) {
return usr.hasRoles(RolesSubmissionRelease)
}
func (usr UserInfoHandle) HasRoleSubmissionUpload() (bool, error) {
return usr.hasRoles(RolesSubmissionUpload)
}
func (usr UserInfoHandle) HasRoleSubmissionReview() (bool, error) {
return usr.hasRoles(RolesSubmissionReview)
}
func (usr UserInfoHandle) HasRoleScriptWrite() (bool, error) {
return usr.hasRoles(RolesScriptWrite)
}
/// Not implemented
func (usr UserInfo) HasRoleMaptest() (bool, error) {
func (usr UserInfoHandle) HasRoleMaptest() (bool, error) {
println("HasRoleMaptest is not implemented!")
return false, nil
}
@@ -91,17 +176,7 @@ func (svc SecurityHandler) HandleCookieAuth(ctx context.Context, operationName a
return nil, ErrMissingSessionID
}
validate, err := svc.Client.ValidateSession(ctx, &auth.IdMessage{
SessionID: sessionId,
})
if err != nil {
return nil, err
}
if !validate.Valid {
return nil, ErrInvalidSession
}
newCtx := context.WithValue(ctx, "UserInfo", UserInfo{
newCtx := context.WithValue(ctx, "UserInfo", UserInfoHandle{
svc: &svc,
ctx: &ctx,
sessionId: sessionId,

View File

@@ -3,6 +3,9 @@ package service
import (
"context"
"errors"
"fmt"
"git.itzana.me/strafesnet/go-grpc/maps"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"github.com/nats-io/nats.go"
@@ -13,11 +16,20 @@ var (
ErrPermissionDenied = errors.New("Permission denied")
// ErrUserInfo user info is missing for some reason
ErrUserInfo = errors.New("Missing user info")
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)
ErrPermissionDeniedNeedRoleMapDownload = fmt.Errorf("%w: Need Role MapDownload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleScriptWrite = fmt.Errorf("%w: Need Role ScriptWrite", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMaptest = fmt.Errorf("%w: Need Role Maptest", ErrPermissionDenied)
)
type Service struct {
DB datastore.Datastore
Nats nats.JetStreamContext
Client maps.MapsServiceClient
}
// NewError creates *ErrorStatusCode from error returned by handler.

68
pkg/service/session.go Normal file
View File

@@ -0,0 +1,68 @@
package service
import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/api"
)
// SessionRoles implements getSessionRoles operation.
//
// Get bitflags of permissions the currently logged in user has.
//
// GET /session/roles
func (svc *Service) SessionRoles(ctx context.Context) (*api.Roles, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
roles, err := userInfo.GetRoles();
if err != nil {
return nil, err
}
return &api.Roles{Roles: int32(roles)}, nil
}
// SessionUser implements sessionUser operation.
//
// Get information about the currently logged in user.
//
// GET /session/roles
func (svc *Service) SessionUser(ctx context.Context) (*api.User, error) {
userInfoHandle, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
userInfo, err := userInfoHandle.GetUserInfo();
if err != nil {
return nil, err
}
return &api.User{
UserID:int64(userInfo.UserID),
Username:userInfo.Username,
AvatarURL:userInfo.AvatarURL,
}, nil
}
// SessionUser implements sessionUser operation.
//
// Get information about the currently logged in user.
//
// GET /session/roles
func (svc *Service) SessionValidate(ctx context.Context) (bool, error) {
userInfoHandle, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return false, ErrUserInfo
}
valid, err := userInfoHandle.Validate();
if err != nil {
return false, err
}
return valid, nil
}

View File

@@ -4,7 +4,10 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"git.itzana.me/strafesnet/go-grpc/maps"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
@@ -12,32 +15,42 @@ import (
var(
CreationPhaseSubmissionsLimit = 20
CreationPhaseSubmissionStatuses = []model.Status{
model.StatusChangesRequested,
model.StatusSubmitted,
model.StatusUnderConstruction,
CreationPhaseSubmissionStatuses = []model.SubmissionStatus{
model.SubmissionStatusChangesRequested,
model.SubmissionStatusSubmitted,
model.SubmissionStatusUnderConstruction,
}
ActiveSubmissionStatuses = []model.Status{
model.StatusUploaded,
model.StatusUploading,
model.StatusValidated,
model.StatusValidating,
model.StatusAccepted,
model.StatusChangesRequested,
model.StatusSubmitted,
model.StatusUnderConstruction,
// prevent two mapfixes with same asset id
ActiveSubmissionStatuses = []model.SubmissionStatus{
model.SubmissionStatusUploading,
model.SubmissionStatusValidated,
model.SubmissionStatusValidating,
model.SubmissionStatusAccepted,
model.SubmissionStatusChangesRequested,
model.SubmissionStatusSubmitted,
model.SubmissionStatusUnderConstruction,
}
// limit mapfixes in the pipeline to one per target map
ActiveAcceptedSubmissionStatuses = []model.SubmissionStatus{
model.SubmissionStatusUploading,
model.SubmissionStatusValidated,
model.SubmissionStatusValidating,
model.SubmissionStatusAccepted,
}
)
var (
ErrCreationPhaseSubmissionsLimit = errors.New("Active submissions limited to 20")
ErrActiveSubmissionSameAssetID = errors.New("There is an active submission with the same AssetID")
ErrActiveSubmissionSameTargetAssetID = errors.New("There is an active submission with the same TargetAssetID")
ErrUploadedAssetIDAlreadyExists = errors.New("The submission UploadedAssetID is already set")
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)
)
// POST /submissions
func (svc *Service) CreateSubmission(ctx context.Context, request *api.SubmissionCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
@@ -55,7 +68,7 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
creation_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: 1,
Size: int32(CreationPhaseSubmissionsLimit),
})
},datastore.ListSortDisabled)
if err != nil {
return nil, err
}
@@ -74,7 +87,7 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
active_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: 1,
Size: 1,
})
},datastore.ListSortDisabled)
if err != nil {
return nil, err
}
@@ -83,23 +96,6 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
}
}
// Check if an active submission with the same target asset id exists
if request.TargetAssetID.IsSet() && request.TargetAssetID.Value != 0{
filter := datastore.Optional()
filter.Add("target_asset_id", request.TargetAssetID.Value)
filter.Add("status_id", ActiveSubmissionStatuses)
active_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: 1,
Size: 1,
})
if err != nil {
return nil, err
}
if len(active_submissions) != 0{
return nil, ErrActiveSubmissionSameTargetAssetID
}
}
submission, err := svc.DB.Submissions().Create(ctx, model.Submission{
ID: 0,
DisplayName: request.DisplayName,
@@ -109,8 +105,7 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
AssetID: request.AssetID,
AssetVersion: request.AssetVersion,
Completed: false,
TargetAssetID: request.TargetAssetID.Value,
StatusID: model.StatusUnderConstruction,
StatusID: model.SubmissionStatusUnderConstruction,
})
if err != nil {
return nil, err
@@ -141,8 +136,9 @@ func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionP
AssetID: int64(submission.AssetID),
AssetVersion: int64(submission.AssetVersion),
Completed: submission.Completed,
TargetAssetID: api.NewOptInt64(int64(submission.TargetAssetID)),
UploadedAssetID: api.NewOptInt64(int64(submission.UploadedAssetID)),
StatusID: int32(submission.StatusID),
StatusMessage: submission.StatusMessage,
}, nil
}
@@ -164,29 +160,31 @@ func (svc *Service) ListSubmissions(ctx context.Context, params api.ListSubmissi
filter.Add("game_id", params.GameID.Value)
}
sort := datastore.ListSort(params.Sort.Or(int32(datastore.ListSortDisabled)))
items, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
})
},sort)
if err != nil {
return nil, err
}
var resp []api.Submission
for i := 0; i < len(items); i++ {
for _, item := range items {
resp = append(resp, api.Submission{
ID: items[i].ID,
DisplayName: items[i].DisplayName,
Creator: items[i].Creator,
GameID: items[i].GameID,
CreatedAt: items[i].CreatedAt.Unix(),
UpdatedAt: items[i].UpdatedAt.Unix(),
Submitter: int64(items[i].Submitter),
AssetID: int64(items[i].AssetID),
AssetVersion: int64(items[i].AssetVersion),
Completed: items[i].Completed,
TargetAssetID: api.NewOptInt64(int64(items[i].TargetAssetID)),
StatusID: int32(items[i].StatusID),
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),
Completed: item.Completed,
UploadedAssetID: api.NewOptInt64(int64(item.UploadedAssetID)),
StatusID: int32(item.StatusID),
})
}
@@ -199,7 +197,7 @@ func (svc *Service) ListSubmissions(ctx context.Context, params api.ListSubmissi
//
// POST /submissions/{SubmissionID}/completed
func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSubmissionCompletedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
@@ -210,7 +208,7 @@ func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSu
}
// check if caller has MaptestGame role (request must originate from a maptest roblox game)
if !has_role {
return ErrPermissionDenied
return ErrPermissionDeniedNeedRoleMaptest
}
pmap := datastore.Optional()
@@ -224,7 +222,7 @@ func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSu
//
// POST /submissions/{SubmissionID}/model
func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.UpdateSubmissionModelParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
@@ -241,7 +239,7 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
}
// check if caller is the submitter
if !has_role {
return ErrPermissionDenied
return ErrPermissionDeniedNotSubmitter
}
// check if Status is ChangesRequested|Submitted|UnderConstruction
@@ -250,7 +248,7 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
pmap.AddNotNil("asset_version", params.VersionID)
//always reset completed when model changes
pmap.Add("completed", false)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusChangesRequested, model.StatusSubmitted, model.StatusUnderConstruction}, pmap)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusChangesRequested, model.SubmissionStatusSubmitted, model.SubmissionStatusUnderConstruction}, pmap)
}
// ActionSubmissionReject invokes actionSubmissionReject operation.
@@ -259,7 +257,7 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
//
// POST /submissions/{SubmissionID}/status/reject
func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.ActionSubmissionRejectParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
@@ -270,13 +268,13 @@ func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.Actio
}
// check if caller has required role
if !has_role {
return ErrPermissionDenied
return ErrPermissionDeniedNeedRoleMapReview
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusRejected)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted}, smap)
smap.Add("status_id", model.SubmissionStatusRejected)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted}, smap)
}
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
@@ -285,7 +283,7 @@ func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.Actio
//
// POST /submissions/{SubmissionID}/status/request-changes
func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params api.ActionSubmissionRequestChangesParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
@@ -296,13 +294,13 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params a
}
// check if caller has required role
if !has_role {
return ErrPermissionDenied
return ErrPermissionDeniedNeedRoleMapReview
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusChangesRequested)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidated, model.StatusAccepted, model.StatusSubmitted}, smap)
smap.Add("status_id", model.SubmissionStatusChangesRequested)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidated, model.SubmissionStatusAccepted, model.SubmissionStatusSubmitted}, smap)
}
// ActionSubmissionRevoke invokes actionSubmissionRevoke operation.
@@ -311,7 +309,7 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params a
//
// POST /submissions/{SubmissionID}/status/revoke
func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.ActionSubmissionRevokeParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
@@ -328,13 +326,13 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio
}
// check if caller is the submitter
if !has_role {
return ErrPermissionDenied
return ErrPermissionDeniedNotSubmitter
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusUnderConstruction)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted, model.StatusChangesRequested}, smap)
smap.Add("status_id", model.SubmissionStatusUnderConstruction)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted, model.SubmissionStatusChangesRequested}, smap)
}
// ActionSubmissionSubmit invokes actionSubmissionSubmit operation.
@@ -343,7 +341,7 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio
//
// POST /submissions/{SubmissionID}/status/submit
func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.ActionSubmissionSubmitParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
@@ -360,13 +358,13 @@ func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.Actio
}
// check if caller is the submitter
if !has_role {
return ErrPermissionDenied
return ErrPermissionDeniedNotSubmitter
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusSubmitted)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUnderConstruction, model.StatusChangesRequested}, smap)
smap.Add("status_id", model.SubmissionStatusSubmitted)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUnderConstruction, model.SubmissionStatusChangesRequested}, smap)
}
// ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation.
@@ -375,72 +373,96 @@ func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.Actio
//
// POST /submissions/{SubmissionID}/status/trigger-upload
func (svc *Service) ActionSubmissionTriggerUpload(ctx context.Context, params api.ActionSubmissionTriggerUploadParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionRelease()
has_role, err := userInfo.HasRoleSubmissionUpload()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDenied
return ErrPermissionDeniedNeedRoleMapUpload
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusUploading)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusValidated}, smap)
smap.Add("status_id", model.SubmissionStatusUploading)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidated}, smap)
if err != nil {
return err
}
// sentinel value because we are not using rust
if submission.TargetAssetID == 0 {
if submission.UploadedAssetID == 0 {
// this is a new map
publish_new_request := model.PublishNewRequest{
upload_new_request := model.UploadSubmissionRequest{
SubmissionID: submission.ID,
ModelID: submission.AssetID,
ModelVersion: submission.AssetVersion,
// publish as displayname, whatever
ModelID: submission.ValidatedAssetID,
ModelVersion: submission.ValidatedAssetVersion,
// upload as displayname, whatever
ModelName: submission.DisplayName,
}
j, err := json.Marshal(publish_new_request)
j, err := json.Marshal(upload_new_request)
if err != nil {
return err
}
svc.Nats.Publish("maptest.submissions.publishnew", []byte(j))
svc.Nats.Publish("maptest.submissions.upload", []byte(j))
} else {
// this is a map fix
publish_fix_request := model.PublishFixRequest{
SubmissionID: submission.ID,
ModelID: submission.AssetID,
ModelVersion: submission.AssetVersion,
TargetAssetID: submission.TargetAssetID,
}
j, err := json.Marshal(publish_fix_request)
if err != nil {
return err
}
svc.Nats.Publish("maptest.submissions.publishfix", []byte(j))
// refuse to operate
return ErrUploadedAssetIDAlreadyExists
}
return nil
}
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
//
// Role SubmissionRelease changes status from Uploading -> Validated.
//
// POST /submissions/{SubmissionID}/status/reset-uploading
func (svc *Service) ActionSubmissionValidated(ctx context.Context, params api.ActionSubmissionValidatedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionUpload()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapUpload
}
// 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
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusValidated)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
}
// ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation.
//
// Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
// Role Reviewer triggers validation and changes status from Submitted -> Validating.
//
// POST /submissions/{SubmissionID}/status/trigger-validate
func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params api.ActionSubmissionTriggerValidateParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
@@ -451,22 +473,42 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
}
// check if caller has required role
if !has_role {
return ErrPermissionDenied
return ErrPermissionDeniedNeedRoleMapReview
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusValidating)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted, model.StatusAccepted}, smap)
// read submission (this could be done with a transaction WHERE clause)
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil {
return err
}
validate_request := model.ValidateRequest{
has_role, err = userInfo.IsSubmitter(uint64(submission.Submitter))
if err != nil {
return err
}
// check if caller is NOT the submitter
if has_role {
return ErrAcceptOwnSubmission
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusValidating)
submission, err = svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted}, smap)
if err != nil {
return err
}
validate_request := model.ValidateSubmissionRequest{
SubmissionID: submission.ID,
ModelID: submission.AssetID,
ModelVersion: submission.AssetVersion,
ValidatedModelID: 0, //TODO: reuse velidation models
ValidatedModelID: nil,
}
// sentinel values because we're not using rust
if submission.ValidatedAssetID != 0 {
validate_request.ValidatedModelID = &submission.ValidatedAssetID
}
j, err := json.Marshal(validate_request)
@@ -478,3 +520,157 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
return nil
}
// ActionSubmissionRetryValidate invokes actionSubmissionRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
//
// POST /submissions/{SubmissionID}/status/retry-validate
func (svc *Service) ActionSubmissionRetryValidate(ctx context.Context, params api.ActionSubmissionRetryValidateParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionReview()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusValidating)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusAccepted}, smap)
if err != nil {
return err
}
validate_request := model.ValidateSubmissionRequest{
SubmissionID: submission.ID,
ModelID: submission.AssetID,
ModelVersion: submission.AssetVersion,
ValidatedModelID: nil,
}
// sentinel values because we're not using rust
if submission.ValidatedAssetID != 0 {
validate_request.ValidatedModelID = &submission.ValidatedAssetID
}
j, err := json.Marshal(validate_request)
if err != nil {
return err
}
svc.Nats.Publish("maptest.submissions.validate", []byte(j))
return nil
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// Role SubmissionReview changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/reset-validating
func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params api.ActionSubmissionAcceptedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionReview()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
}
// 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
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusAccepted)
smap.Add("status_message", "Manually forced reset")
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
}
// ReleaseSubmissions invokes releaseSubmissions operation.
//
// Release a set of uploaded maps.
//
// POST /release-submissions
func (svc *Service) ReleaseSubmissions(ctx context.Context, request []api.ReleaseInfo) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionRelease()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleSubmissionRelease
}
idList := make([]int64, len(request))
for i, releaseInfo := range request {
idList[i] = releaseInfo.SubmissionID
}
// fetch submissions
submissions, err := svc.DB.Submissions().GetList(ctx, idList)
if err != nil {
return err
}
// check each submission to make sure it is ready to release
for _,submission := range submissions{
if submission.StatusID != model.SubmissionStatusUploaded{
return ErrReleaseInvalidStatus
}
if submission.UploadedAssetID == 0{
return ErrReleaseNoUploadedAssetID
}
}
for i,submission := range submissions{
date := request[i].Date.Unix()
// create each map with go-grpc
_, err := svc.Client.Create(ctx, &maps.MapRequest{
ID: submission.UploadedAssetID,
DisplayName: &submission.DisplayName,
Creator: &submission.Creator,
GameID: &submission.GameID,
Date: &date,
})
if err != nil {
return err
}
// update each status to Released
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusReleased)
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, submission.ID, []model.SubmissionStatus{model.SubmissionStatusUploaded}, smap)
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,61 @@
package service_internal
import (
"context"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
// UpdateMapfixValidatedModel implements patchMapfixModel operation.
//
// Update model following role restrictions.
//
// POST /mapfixes/{MapfixID}/validated-model
func (svc *Service) UpdateMapfixValidatedModel(ctx context.Context, params internal.UpdateMapfixValidatedModelParams) error {
// check if Status is ChangesRequested|Submitted|UnderConstruction
pmap := datastore.Optional()
pmap.AddNotNil("validated_asset_id", params.ValidatedModelID)
pmap.AddNotNil("validated_asset_version", params.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)
}
// ActionMapfixValidate invokes actionMapfixValidate operation.
//
// Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
func (svc *Service) ActionMapfixValidated(ctx context.Context, params internal.ActionMapfixValidatedParams) error {
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusValidated)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
}
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
func (svc *Service) ActionMapfixAccepted(ctx context.Context, params internal.ActionMapfixAcceptedParams) error {
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusAccepted)
smap.Add("status_message", params.StatusMessage)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
}
// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
func (svc *Service) ActionMapfixUploaded(ctx context.Context, params internal.ActionMapfixUploadedParams) error {
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusUploaded)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUploading}, smap)
}

View File

@@ -67,12 +67,12 @@ func (svc *Service) ListScriptPolicy(ctx context.Context, params api.ListScriptP
}
var resp []api.ScriptPolicy
for i := 0; i < len(items); i++ {
for _, item := range items {
resp = append(resp, api.ScriptPolicy{
ID: items[i].ID,
FromScriptHash: model.HashFormat(uint64(items[i].FromScriptHash)),
ToScriptID: items[i].ToScriptID,
Policy: int32(items[i].Policy),
ID: item.ID,
FromScriptHash: model.HashFormat(uint64(item.FromScriptHash)),
ToScriptID: item.ToScriptID,
Policy: int32(item.Policy),
})
}

View File

@@ -19,7 +19,8 @@ func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*a
Name: req.Name,
Hash: int64(model.HashSource(req.Source)),
Source: req.Source,
SubmissionID: req.SubmissionID.Or(0),
ResourceType: model.ResourceType(req.ResourceType),
ResourceID: req.ResourceID.Or(0),
})
if err != nil {
return nil, err
@@ -51,8 +52,11 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
if params.Source.IsSet(){
filter.AddNotNil("source", params.Source.Value)
}
if params.SubmissionID.IsSet(){
filter.AddNotNil("submission_id", params.SubmissionID.Value)
if params.ResourceType.IsSet(){
filter.AddNotNil("resource_type", params.ResourceType.Value)
}
if params.ResourceID.IsSet(){
filter.AddNotNil("resource_id", params.ResourceID.Value)
}
items, err := svc.DB.Scripts().List(ctx, filter, model.Page{
@@ -64,12 +68,13 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
}
var resp []api.Script
for i := 0; i < len(items); i++ {
for _, item := range items {
resp = append(resp, api.Script{
ID: items[i].ID,
Hash: model.HashFormat(uint64(items[i].Hash)),
Source: items[i].Source,
SubmissionID: items[i].SubmissionID,
ID: item.ID,
Hash: model.HashFormat(uint64(item.Hash)),
Source: item.Source,
ResourceType: int32(item.ResourceType),
ResourceID: item.ResourceID,
})
}
@@ -92,6 +97,7 @@ func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (
Name: script.Name,
Hash: model.HashFormat(uint64(script.Hash)),
Source: script.Source,
SubmissionID: script.SubmissionID,
ResourceType: int32(script.ResourceType),
ResourceID: script.ResourceID,
}, nil
}

View File

@@ -8,19 +8,19 @@ import (
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
// UpdateSubmissionModel implements patchSubmissionModel operation.
// UpdateSubmissionValidatedModel implements patchSubmissionModel operation.
//
// Update model following role restrictions.
//
// POST /submissions/{SubmissionID}/model
func (svc *Service) UpdateSubmissionModel(ctx context.Context, params internal.UpdateSubmissionModelParams) error {
// POST /submissions/{SubmissionID}/validated-model
func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params internal.UpdateSubmissionValidatedModelParams) error {
// check if Status is ChangesRequested|Submitted|UnderConstruction
pmap := datastore.Optional()
pmap.AddNotNil("asset_id", params.ModelID)
pmap.AddNotNil("asset_version", params.VersionID)
//always reset completed when model changes
pmap.Add("completed", false)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, pmap)
pmap.AddNotNil("validated_asset_id", params.ValidatedModelID)
pmap.AddNotNil("validated_asset_version", params.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)
}
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
@@ -29,12 +29,10 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params internal.U
//
// POST /submissions/{SubmissionID}/status/validator-validated
func (svc *Service) ActionSubmissionValidated(ctx context.Context, params internal.ActionSubmissionValidatedParams) error {
println("[ActionSubmissionValidated] Implicit Validator permission granted!")
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusValidated)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, smap)
smap.Add("status_id", model.SubmissionStatusValidated)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
@@ -43,26 +41,11 @@ 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 {
println("[ActionSubmissionAccepted] Implicit Validator permission granted!")
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusAccepted)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, smap)
}
// ActionSubmissionReleased implements actionSubmissionReleased operation.
//
// (Internal endpoint) Role Releaser changes status from Uploaded -> Released.
//
// POST /submissions/{SubmissionID}/status/releaser-released
func (svc *Service) ActionSubmissionReleased(ctx context.Context, params internal.ActionSubmissionReleasedParams) error {
println("[ActionSubmissionReleased] Implicit Validator permission granted!")
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusReleased)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUploaded}, smap)
smap.Add("status_id", model.SubmissionStatusAccepted)
smap.Add("status_message", params.StatusMessage)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
}
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
@@ -71,13 +54,9 @@ func (svc *Service) ActionSubmissionReleased(ctx context.Context, params interna
//
// POST /submissions/{SubmissionID}/status/validator-uploaded
func (svc *Service) ActionSubmissionUploaded(ctx context.Context, params internal.ActionSubmissionUploadedParams) error {
println("[ActionSubmissionUploaded] Implicit Validator permission granted!")
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusUploaded)
if params.TargetAssetID.IsSet() {
smap.AddNotNil("target_asset_id", params.TargetAssetID.Value)
}
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUploading}, smap)
smap.Add("status_id", model.SubmissionStatusUploaded)
smap.Add("uploaded_asset_id", params.UploadedAssetID)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
}

View File

@@ -1 +0,0 @@
/target

View File

@@ -1,11 +1,11 @@
[package]
name = "maps-validation"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
[dependencies]
submissions-api = { path = "api", features = ["internal"], registry = "strafesnet" }
async-nats = "0.38.0"
submissions-api = { path = "api", features = ["internal"], default-features = false, registry = "strafesnet" }
async-nats = "0.40.0"
futures = "0.3.31"
rbx_asset = { version = "0.2.5", registry = "strafesnet" }
rbx_binary = { version = "0.7.4", registry = "strafesnet"}

View File

@@ -1 +0,0 @@
/target

1463
validation/api/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "submissions-api"
version = "0.3.0"
version = "0.6.1"
edition = "2021"
publish = ["strafesnet"]
repository = "https://git.itzana.me/StrafesNET/maps-service"
@@ -18,6 +18,6 @@ serde_repr = "0.1.19"
url = "2"
[features]
default = ["internal"]
default = ["external"]
internal = []
external = []

View File

@@ -14,8 +14,7 @@ pub struct Context{
}
impl Context{
pub fn new(mut base_url:String,cookie:Option<Cookie>)->reqwest::Result<Self>{
base_url+="/v1";
pub fn new(base_url:String,cookie:Option<Cookie>)->reqwest::Result<Self>{
Ok(Self{
base_url,
client:{

View File

@@ -14,7 +14,7 @@ impl Context{
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest)
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
let url_raw=format!("{}/scripts",self.0.base_url);
@@ -33,15 +33,18 @@ impl Context{
if let Some(source)=config.Source{
query_pairs.append_pair("Source",source);
}
if let Some(submission_id)=config.SubmissionID{
query_pairs.append_pair("SubmissionID",submission_id.to_string().as_str());
if let Some(resource_type)=config.ResourceType{
query_pairs.append_pair("ResourceType",(resource_type as i32).to_string().as_str());
}
if let Some(resource_id)=config.ResourceID{
query_pairs.append_pair("ResourceID",resource_id.to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest)
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
let scripts=self.get_scripts(GetScriptsRequest{
@@ -50,7 +53,8 @@ impl Context{
Hash:Some(config.hash),
Name:None,
Source:None,
SubmissionID:None,
ResourceType:None,
ResourceID:None,
}).await.map_err(SingleItemError::Other)?;
if 1<scripts.len(){
return Err(SingleItemError::DuplicateItems);
@@ -66,7 +70,7 @@ impl Context{
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest)
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{
let url_raw=format!("{}/script-policy",self.0.base_url);
@@ -90,7 +94,7 @@ impl Context{
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest)
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
let policies=self.get_script_policies(GetScriptPoliciesRequest{
@@ -114,7 +118,7 @@ impl Context{
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest)
.json().await.map_err(Error::ReqwestJson)
}
pub async fn update_script_policy(&self,config:UpdateScriptPolicyRequest)->Result<(),Error>{
let url_raw=format!("{}/script-policy/{}",self.0.base_url,config.ID.0);

View File

@@ -3,12 +3,27 @@ use crate::types::*;
#[derive(Clone)]
pub struct Context(crate::context::Context);
// conditionally include specified query pairs
macro_rules! query_pairs{
($url:expr,)=>{
$url
};
($url:expr,$(($param:expr,$value:expr))+)=>{
{
let mut url=$url;
url.query_pairs_mut()
$(.append_pair($param,$value))*;
url
}
};
}
// there are lots of action endpoints and they all follow the same pattern
macro_rules! action{
($fname:ident,$action:expr)=>{
pub async fn $fname(&self,config:SubmissionID)->Result<(),Error>{
let url_raw=format!(concat!("{}/submissions/{}/status/",$action),self.0.base_url,config.0);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
($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=query_pairs!(reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?,$(($param,$value))*);
response_ok(
self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
@@ -29,7 +44,52 @@ impl Context{
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest)
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
let url_raw=format!("{}/scripts",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
let mut query_pairs=url.query_pairs_mut();
query_pairs.append_pair("Page",config.Page.to_string().as_str());
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
if let Some(name)=config.Name{
query_pairs.append_pair("Name",name);
}
if let Some(hash)=config.Hash{
query_pairs.append_pair("Hash",hash);
}
if let Some(source)=config.Source{
query_pairs.append_pair("Source",source);
}
if let Some(resource_type)=config.ResourceType{
query_pairs.append_pair("ResourceType",(resource_type as i32).to_string().as_str());
}
if let Some(resource_id)=config.ResourceID{
query_pairs.append_pair("ResourceID",resource_id.to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
let scripts=self.get_scripts(GetScriptsRequest{
Page:1,
Limit:2,
Hash:Some(config.hash),
Name:None,
Source:None,
ResourceType:None,
ResourceID:None,
}).await.map_err(SingleItemError::Other)?;
if 1<scripts.len(){
return Err(SingleItemError::DuplicateItems);
}
Ok(scripts.into_iter().next())
}
pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{
let url_raw=format!("{}/scripts",self.0.base_url);
@@ -40,7 +100,7 @@ impl Context{
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest)
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{
let url_raw=format!("{}/script-policy",self.0.base_url);
@@ -64,7 +124,7 @@ impl Context{
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest)
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
let policies=self.get_script_policies(GetScriptPoliciesRequest{
@@ -88,39 +148,28 @@ impl Context{
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest)
.json().await.map_err(Error::ReqwestJson)
}
pub async fn update_submission_model(&self,config:UpdateSubmissionModelRequest)->Result<(),Error>{
let url_raw=format!("{}/submissions/{}/model",self.0.base_url,config.SubmissionID);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
url.query_pairs_mut()
.append_pair("ModelID",config.ModelID.to_string().as_str())
.append_pair("ModelVersion",config.ModelVersion.to_string().as_str());
}
response_ok(
self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
pub async fn action_submission_uploaded(&self,config:ActionSubmissionUploadedRequest)->Result<(),Error>{
let url_raw=format!("{}/submissions/{}/status/validator-uploaded",self.0.base_url,config.SubmissionID);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
if let Some(target_asset_id)=config.TargetAssetID{
url.query_pairs_mut()
.append_pair("TargetAssetID",target_asset_id.to_string().as_str());
}
response_ok(
self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
action!(action_submission_validated,"validator-validated");
action!(action_submission_accepted,"validator-failed");
action!(action_submission_released,"releaser-released");
// simple submission endpoints
action!("submissions",action_submission_validated,config,SubmissionID,"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,
("UploadedAssetID",config.UploadedAssetID.to_string().as_str())
);
action!("submissions",action_submission_accepted,config,ActionSubmissionAcceptedRequest,"validator-failed",config.SubmissionID,
("StatusMessage",config.StatusMessage.as_str())
);
// simple mapfixes endpoints
action!("mapfixes",action_mapfix_validated,config,MapfixID,"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,
("StatusMessage",config.StatusMessage.as_str())
);
}

View File

@@ -2,6 +2,7 @@
pub enum Error{
Parse(url::ParseError),
Reqwest(reqwest::Error),
ReqwestJson(reqwest::Error),
Response(ResponseError),
JSON(serde_json::Error),
}
@@ -60,17 +61,25 @@ pub async fn response_ok(response:reqwest::Response)->Result<reqwest::Response,R
}
}
#[derive(Clone,Copy,PartialEq,Eq,serde::Serialize,serde::Deserialize)]
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde::Serialize,serde::Deserialize)]
pub struct ScriptID(pub(crate)i64);
#[derive(Clone,Copy,serde::Serialize,serde::Deserialize)]
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
pub struct ScriptPolicyID(pub(crate)i64);
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
#[repr(i32)]
pub enum ResourceType{
Unknown=0,
Mapfix=1,
Submission=2,
}
#[allow(nonstandard_style)]
pub struct GetScriptRequest{
pub ScriptID:ScriptID,
}
#[allow(nonstandard_style)]
#[derive(serde::Serialize)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct GetScriptsRequest<'a>{
pub Page:u32,
pub Limit:u32,
@@ -81,35 +90,40 @@ pub struct GetScriptsRequest<'a>{
#[serde(skip_serializing_if="Option::is_none")]
pub Source:Option<&'a str>,
#[serde(skip_serializing_if="Option::is_none")]
pub SubmissionID:Option<i64>,
pub ResourceType:Option<ResourceType>,
#[serde(skip_serializing_if="Option::is_none")]
pub ResourceID:Option<i64>,
}
#[derive(Clone,Copy,Debug)]
pub struct HashRequest<'a>{
pub hash:&'a str,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct ScriptResponse{
pub ID:ScriptID,
pub Name:String,
pub Hash:String,
pub Source:String,
pub SubmissionID:i64,
pub ResourceType:ResourceType,
pub ResourceID:i64,
}
#[allow(nonstandard_style)]
#[derive(serde::Serialize)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct CreateScriptRequest<'a>{
pub Name:&'a str,
pub Source:&'a str,
pub ResourceType:ResourceType,
#[serde(skip_serializing_if="Option::is_none")]
pub SubmissionID:Option<i64>,
pub ResourceID:Option<i64>,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct ScriptIDResponse{
pub ID:ScriptID,
}
#[derive(PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
#[repr(i32)]
pub enum Policy{
None=0, // not yet reviewed
@@ -120,7 +134,7 @@ pub enum Policy{
}
#[allow(nonstandard_style)]
#[derive(serde::Serialize)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct GetScriptPoliciesRequest<'a>{
pub Page:u32,
pub Limit:u32,
@@ -132,7 +146,7 @@ pub struct GetScriptPoliciesRequest<'a>{
pub Policy:Option<Policy>,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct ScriptPolicyResponse{
pub ID:ScriptPolicyID,
pub FromScriptHash:String,
@@ -140,20 +154,20 @@ pub struct ScriptPolicyResponse{
pub Policy:Policy
}
#[allow(nonstandard_style)]
#[derive(serde::Serialize)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct CreateScriptPolicyRequest{
pub FromScriptID:ScriptID,
pub ToScriptID:ScriptID,
pub Policy:Policy,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct ScriptPolicyIDResponse{
pub ID:ScriptPolicyID,
}
#[allow(nonstandard_style)]
#[derive(serde::Serialize)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct UpdateScriptPolicyRequest{
pub ID:ScriptPolicyID,
#[serde(skip_serializing_if="Option::is_none")]
@@ -165,6 +179,7 @@ pub struct UpdateScriptPolicyRequest{
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct UpdateSubmissionModelRequest{
pub SubmissionID:i64,
pub ModelID:u64,
@@ -172,9 +187,42 @@ pub struct UpdateSubmissionModelRequest{
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionUploadedRequest{
pub SubmissionID:i64,
pub TargetAssetID:Option<u64>,
pub UploadedAssetID:u64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionAcceptedRequest{
pub SubmissionID:i64,
pub StatusMessage:String,
}
#[derive(Clone,Copy,Debug)]
pub struct SubmissionID(pub i64);
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct UpdateMapfixModelRequest{
pub MapfixID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixUploadedRequest{
pub MapfixID:i64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixAcceptedRequest{
pub MapfixID:i64,
pub StatusMessage:String,
}
#[derive(Clone,Copy,Debug)]
pub struct MapfixID(pub i64);

View File

@@ -1,10 +1,14 @@
use futures::StreamExt;
mod nats_types;
mod validator;
mod publish_new;
mod publish_fix;
mod message_handler;
mod nats_types;
mod types;
mod uploader;
mod upload_mapfix;
mod upload_submission;
mod validator;
mod validate_mapfix;
mod validate_submission;
#[allow(dead_code)]
#[derive(Debug)]
@@ -52,7 +56,7 @@ async fn main()->Result<(),StartupError>{
.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.submissions.>".to_owned(),
filter_subject:"maptest.>".to_owned(),
..Default::default()
}).await.map_err(StartupError::NatsConsumer)?
.messages().await.map_err(StartupError::NatsStream)

View File

@@ -5,9 +5,10 @@ pub enum HandleMessageError{
DoubleAck(async_nats::Error),
Json(serde_json::Error),
UnknownSubject(String),
PublishNew(crate::publish_new::PublishError),
PublishFix(crate::publish_fix::PublishError),
Validation(crate::validator::ValidateError),
UploadMapfix(crate::upload_mapfix::UploadError),
UploadSubmission(crate::upload_submission::UploadError),
ValidateMapfix(crate::validate_mapfix::ValidateMapfixError),
ValidateSubmission(crate::validate_submission::ValidateSubmissionError),
}
impl std::fmt::Display for HandleMessageError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@@ -23,9 +24,10 @@ fn from_slice<'a,T:serde::de::Deserialize<'a>>(slice:&'a [u8])->Result<T,HandleM
}
pub struct MessageHandler{
publish_new:crate::publish_new::Publisher,
publish_fix:crate::publish_fix::Publisher,
validator:crate::validator::Validator,
upload_mapfix:crate::upload_mapfix::Uploader,
upload_submission:crate::upload_submission::Uploader,
validate_mapfix:crate::validate_mapfix::Validator,
validate_submission:crate::validate_submission::Validator,
}
impl MessageHandler{
@@ -35,18 +37,20 @@ impl MessageHandler{
api:submissions_api::internal::Context,
)->Self{
Self{
publish_new:crate::publish_new::Publisher::new(cookie_context.clone(),group_id,api.clone()),
publish_fix:crate::publish_fix::Publisher::new(cookie_context.clone(),group_id,api.clone()),
validator:crate::validator::Validator::new(cookie_context,api),
upload_mapfix:crate::upload_mapfix::Uploader::new(crate::uploader::Uploader::new(cookie_context.clone(),group_id,api.clone())),
upload_submission:crate::upload_submission::Uploader::new(crate::uploader::Uploader::new(cookie_context.clone(),group_id,api.clone())),
validate_mapfix:crate::validate_mapfix::Validator::new(crate::validator::Validator::new(cookie_context.clone(),api.clone())),
validate_submission:crate::validate_submission::Validator::new(crate::validator::Validator::new(cookie_context,api)),
}
}
pub async fn handle_message_result(&self,message_result:MessageResult)->Result<(),HandleMessageError>{
let message=message_result.map_err(HandleMessageError::Messages)?;
message.double_ack().await.map_err(HandleMessageError::DoubleAck)?;
match message.subject.as_str(){
"maptest.submissions.publishnew"=>self.publish_new.publish(from_slice(&message.payload)?).await.map_err(HandleMessageError::PublishNew),
"maptest.submissions.publishfix"=>self.publish_fix.publish(from_slice(&message.payload)?).await.map_err(HandleMessageError::PublishFix),
"maptest.submissions.validate"=>self.validator.validate(from_slice(&message.payload)?).await.map_err(HandleMessageError::Validation),
"maptest.mapfixes.upload"=>self.upload_mapfix.upload(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadMapfix),
"maptest.submissions.upload"=>self.upload_submission.upload(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadSubmission),
"maptest.mapfixes.validate"=>self.validate_mapfix.validate(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateMapfix),
"maptest.submissions.validate"=>self.validate_submission.validate(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateSubmission),
other=>Err(HandleMessageError::UnknownSubject(other.to_owned()))
}
}

View File

@@ -6,7 +6,7 @@
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct ValidateRequest{
pub struct ValidateSubmissionRequest{
// submission_id is passed back in the response message
pub SubmissionID:i64,
pub ModelID:u64,
@@ -14,10 +14,20 @@ pub struct ValidateRequest{
pub ValidatedModelID:Option<u64>,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct ValidateMapfixRequest{
// submission_id is passed back in the response message
pub MapfixID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
pub ValidatedModelID:Option<u64>,
}
// Create a new map
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct PublishNewRequest{
pub struct UploadSubmissionRequest{
pub SubmissionID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
@@ -26,8 +36,8 @@ pub struct PublishNewRequest{
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct PublishFixRequest{
pub SubmissionID:i64,
pub struct UploadMapfixRequest{
pub MapfixID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
pub TargetAssetID:u64,

View File

@@ -1,62 +0,0 @@
use crate::nats_types::PublishFixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum PublishError{
Get(rbx_asset::cookie::GetError),
Json(serde_json::Error),
Upload(rbx_asset::cookie::UploadError),
ApiActionSubmissionUploaded(submissions_api::Error),
}
impl std::fmt::Display for PublishError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for PublishError{}
pub struct Publisher{
roblox_cookie:rbx_asset::cookie::CookieContext,
group_id:Option<u64>,
api_internal:submissions_api::internal::Context,
}
impl Publisher{
pub const fn new(
roblox_cookie:rbx_asset::cookie::CookieContext,
group_id:Option<u64>,
api_internal:submissions_api::internal::Context,
)->Self{
Self{
roblox_cookie,
group_id,
api_internal,
}
}
pub async fn publish(&self,publish_info:PublishFixRequest)->Result<(),PublishError>{
// download the map model version
let model_data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:publish_info.ModelID,
version:Some(publish_info.ModelVersion),
}).await.map_err(PublishError::Get)?;
// upload the map to the strafesnet group
let _upload_response=self.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{
assetid:publish_info.TargetAssetID,
groupId:self.group_id,
name:None,
description:None,
ispublic:None,
allowComments:None,
},model_data).await.map_err(PublishError::Upload)?;
// that's it, the database entry does not need to be changed.
// mark submission as uploaded, TargetAssetID is unchanged
self.api_internal.action_submission_uploaded(submissions_api::types::ActionSubmissionUploadedRequest{
SubmissionID:publish_info.SubmissionID,
TargetAssetID:None,
}).await.map_err(PublishError::ApiActionSubmissionUploaded)?;
Ok(())
}
}

View File

@@ -1,60 +0,0 @@
use crate::nats_types::PublishNewRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum PublishError{
Get(rbx_asset::cookie::GetError),
Json(serde_json::Error),
Create(rbx_asset::cookie::CreateError),
SystemTime(std::time::SystemTimeError),
ApiActionSubmissionPublish(submissions_api::Error),
}
impl std::fmt::Display for PublishError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for PublishError{}
pub struct Publisher{
roblox_cookie:rbx_asset::cookie::CookieContext,
group_id:Option<u64>,
api:submissions_api::internal::Context,
}
impl Publisher{
pub const fn new(
roblox_cookie:rbx_asset::cookie::CookieContext,
group_id:Option<u64>,
api:submissions_api::internal::Context,
)->Self{
Self{
roblox_cookie,
group_id,
api,
}
}
pub async fn publish(&self,publish_info:PublishNewRequest)->Result<(),PublishError>{
// download the map model version
let model_data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:publish_info.ModelID,
version:Some(publish_info.ModelVersion),
}).await.map_err(PublishError::Get)?;
// upload the map to the strafesnet group
let upload_response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
name:publish_info.ModelName.clone(),
description:"".to_owned(),
ispublic:false,
allowComments:false,
groupId:self.group_id,
},model_data).await.map_err(PublishError::Create)?;
// note the asset id of the created model for later release, and mark the submission as uploaded
self.api.action_submission_uploaded(submissions_api::types::ActionSubmissionUploadedRequest{
SubmissionID:publish_info.SubmissionID,
TargetAssetID:Some(upload_response.AssetId),
}).await.map_err(PublishError::ApiActionSubmissionPublish)?;
Ok(())
}
}

4
validation/src/types.rs Normal file
View File

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

View File

@@ -0,0 +1,50 @@
use crate::nats_types::UploadMapfixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum UploadError{
Get(rbx_asset::cookie::GetError),
Json(serde_json::Error),
Upload(rbx_asset::cookie::UploadError),
ApiActionMapfixUploaded(submissions_api::Error),
}
impl std::fmt::Display for UploadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for UploadError{}
pub struct Uploader(crate::uploader::Uploader);
impl Uploader{
pub const fn new(inner:crate::uploader::Uploader)->Self{
Self(inner)
}
pub async fn upload(&self,upload_info:UploadMapfixRequest)->Result<(),UploadError>{
let Self(uploader)=self;
// download the map model version
let model_data=uploader.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:upload_info.ModelID,
version:Some(upload_info.ModelVersion),
}).await.map_err(UploadError::Get)?;
// upload the map to the strafesnet group
let _upload_response=uploader.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{
assetid:upload_info.TargetAssetID,
groupId:uploader.group_id,
name:None,
description:None,
ispublic:None,
allowComments:None,
},model_data).await.map_err(UploadError::Upload)?;
// that's it, the database entry does not need to be changed.
// mark mapfix as uploaded, TargetAssetID is unchanged
uploader.api.action_mapfix_uploaded(submissions_api::types::ActionMapfixUploadedRequest{
MapfixID:upload_info.MapfixID,
}).await.map_err(UploadError::ApiActionMapfixUploaded)?;
Ok(())
}
}

View File

@@ -0,0 +1,49 @@
use crate::nats_types::UploadSubmissionRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum UploadError{
Get(rbx_asset::cookie::GetError),
Json(serde_json::Error),
Create(rbx_asset::cookie::CreateError),
SystemTime(std::time::SystemTimeError),
ApiActionSubmissionUploaded(submissions_api::Error),
}
impl std::fmt::Display for UploadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for UploadError{}
pub struct Uploader(crate::uploader::Uploader);
impl Uploader{
pub const fn new(inner:crate::uploader::Uploader)->Self{
Self(inner)
}
pub async fn upload(&self,upload_info:UploadSubmissionRequest)->Result<(),UploadError>{
let Self(uploader)=self;
// download the map model version
let model_data=uploader.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:upload_info.ModelID,
version:Some(upload_info.ModelVersion),
}).await.map_err(UploadError::Get)?;
// upload the map to the strafesnet group
let upload_response=uploader.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
name:upload_info.ModelName.clone(),
description:"".to_owned(),
ispublic:false,
allowComments:false,
groupId:uploader.group_id,
},model_data).await.map_err(UploadError::Create)?;
// note the asset id of the created model for later release, and mark the submission as uploaded
uploader.api.action_submission_uploaded(submissions_api::types::ActionSubmissionUploadedRequest{
SubmissionID:upload_info.SubmissionID,
UploadedAssetID:upload_response.AssetId,
}).await.map_err(UploadError::ApiActionSubmissionUploaded)?;
Ok(())
}
}

View File

@@ -0,0 +1,18 @@
pub struct Uploader{
pub(crate) roblox_cookie:rbx_asset::cookie::CookieContext,
pub(crate) group_id:Option<u64>,
pub(crate) api:submissions_api::internal::Context,
}
impl Uploader{
pub const fn new(
roblox_cookie:rbx_asset::cookie::CookieContext,
group_id:Option<u64>,
api:submissions_api::internal::Context,
)->Self{
Self{
roblox_cookie,
group_id,
api,
}
}
}

View File

@@ -0,0 +1,45 @@
use crate::nats_types::ValidateMapfixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum ValidateMapfixError{
ApiActionMapfixValidate(submissions_api::Error),
}
impl std::fmt::Display for ValidateMapfixError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for ValidateMapfixError{}
pub struct Validator(crate::validator::Validator);
impl Validator{
pub const fn new(inner:crate::validator::Validator)->Self{
Self(inner)
}
pub async fn validate(&self,validate_info:ValidateMapfixRequest)->Result<(),ValidateMapfixError>{
let Self(validator)=self;
let mapfix_id=validate_info.MapfixID;
let validate_result=validator.validate(validate_info.into()).await;
// update the mapfix depending on the result
match &validate_result{
Ok(())=>{
// update the mapfix model status to validated
validator.api.action_mapfix_validated(
submissions_api::types::MapfixID(mapfix_id)
).await.map_err(ValidateMapfixError::ApiActionMapfixValidate)?;
},
Err(e)=>{
// update the mapfix model status to accepted
validator.api.action_mapfix_accepted(submissions_api::types::ActionMapfixAcceptedRequest{
MapfixID:mapfix_id,
StatusMessage:format!("{e}"),
}).await.map_err(ValidateMapfixError::ApiActionMapfixValidate)?;
},
}
Ok(())
}
}

View File

@@ -0,0 +1,45 @@
use crate::nats_types::ValidateSubmissionRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum ValidateSubmissionError{
ApiActionSubmissionValidate(submissions_api::Error),
}
impl std::fmt::Display for ValidateSubmissionError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for ValidateSubmissionError{}
pub struct Validator(crate::validator::Validator);
impl Validator{
pub const fn new(inner:crate::validator::Validator)->Self{
Self(inner)
}
pub async fn validate(&self,validate_info:ValidateSubmissionRequest)->Result<(),ValidateSubmissionError>{
let Self(validator)=self;
let submission_id=validate_info.SubmissionID;
let validate_result=validator.validate(validate_info.into()).await;
// update the submission depending on the result
match &validate_result{
Ok(())=>{
// update the submission model status to validated
validator.api.action_submission_validated(
submissions_api::types::SubmissionID(submission_id)
).await.map_err(ValidateSubmissionError::ApiActionSubmissionValidate)?;
},
Err(e)=>{
// update the submission model status to accepted
validator.api.action_submission_accepted(submissions_api::types::ActionSubmissionAcceptedRequest{
SubmissionID:submission_id,
StatusMessage:format!("{e}"),
}).await.map_err(ValidateSubmissionError::ApiActionSubmissionValidate)?;
},
}
Ok(())
}
}

View File

@@ -1,6 +1,7 @@
use futures::TryStreamExt;
use submissions_api::types::ResourceType;
use crate::nats_types::ValidateRequest;
use crate::types::ResourceID;
const SCRIPT_CONCURRENCY:usize=16;
@@ -21,23 +22,33 @@ fn source_has_illegal_keywords(source:&str)->bool{
source.find("getfenv").is_some()||source.find("require").is_some()
}
fn hash_source(source:&str)->String{
let mut hasher=siphasher::sip::SipHasher::new();
std::hash::Hasher::write(&mut hasher,source.as_bytes());
let hash=std::hash::Hasher::finish(&hasher);
format!("{:016x}",hash)
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum ValidateError{
Flagged,
Blocked,
NotAllowed,
Get(rbx_asset::cookie::GetError),
ReadDom(ReadDomError),
ApiGetScriptPolicy(submissions_api::types::SingleItemError),
ScriptFlaggedIllegalKeyword(String),
ScriptBlocked(Option<submissions_api::types::ScriptID>),
ScriptNotYetReviewed(Option<submissions_api::types::ScriptID>),
ModelFileDownload(rbx_asset::cookie::GetError),
ModelFileDecode(ReadDomError),
ApiGetScriptPolicyFromHash(submissions_api::types::SingleItemError),
ApiGetScript(submissions_api::Error),
ApiCreateScript(submissions_api::Error),
ApiCreateScriptPolicy(submissions_api::Error),
ApiGetScriptFromHash(submissions_api::types::SingleItemError),
ApiUpdateMapfixModel(submissions_api::Error),
ApiUpdateSubmissionModel(submissions_api::Error),
ApiActionSubmissionValidate(submissions_api::Error),
WriteDom(rbx_binary::EncodeError),
Upload(rbx_asset::cookie::UploadError),
Create(rbx_asset::cookie::CreateError),
ModelFileRootMustHaveOneChild,
ModelFileChildRefIsNil,
ModelFileEncode(rbx_binary::EncodeError),
AssetUpload(rbx_asset::cookie::UploadError),
AssetCreate(rbx_asset::cookie::CreateError),
}
impl std::fmt::Display for ValidateError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@@ -46,9 +57,38 @@ impl std::fmt::Display for ValidateError{
}
impl std::error::Error for ValidateError{}
#[allow(nonstandard_style)]
pub struct ValidateRequest{
pub ModelID:u64,
pub ModelVersion:u64,
pub ValidatedModelID:Option<u64>,
pub ResourceID:ResourceID,
}
impl From<crate::nats_types::ValidateMapfixRequest> for ValidateRequest{
fn from(value:crate::nats_types::ValidateMapfixRequest)->Self{
Self{
ModelID:value.ModelID,
ModelVersion:value.ModelVersion,
ValidatedModelID:value.ValidatedModelID,
ResourceID:ResourceID::Mapfix(value.MapfixID),
}
}
}
impl From<crate::nats_types::ValidateSubmissionRequest> for ValidateRequest{
fn from(value:crate::nats_types::ValidateSubmissionRequest)->Self{
Self{
ModelID:value.ModelID,
ModelVersion:value.ModelVersion,
ValidatedModelID:value.ValidatedModelID,
ResourceID:ResourceID::Submission(value.SubmissionID),
}
}
}
pub struct Validator{
roblox_cookie:rbx_asset::cookie::CookieContext,
api:submissions_api::internal::Context,
pub(crate) roblox_cookie:rbx_asset::cookie::CookieContext,
pub(crate) api:submissions_api::internal::Context,
}
impl Validator{
@@ -62,36 +102,14 @@ impl Validator{
}
}
pub async fn validate(&self,validate_info:ValidateRequest)->Result<(),ValidateError>{
let submission_id=validate_info.SubmissionID;
let validate_result=self.validate_inner(validate_info).await;
// update the submission depending on the result
match &validate_result{
Ok(())=>{
// update the submission model status to validated
self.api.action_submission_validated(
submissions_api::types::SubmissionID(submission_id)
).await.map_err(ValidateError::ApiActionSubmissionValidate)?;
},
Err(_)=>{
// update the submission model status to accepted
self.api.action_submission_accepted(
submissions_api::types::SubmissionID(submission_id)
).await.map_err(ValidateError::ApiActionSubmissionValidate)?;
},
}
validate_result
}
pub async fn validate_inner(&self,validate_info:ValidateRequest)->Result<(),ValidateError>{
// download map
let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:validate_info.ModelID,
version:Some(validate_info.ModelVersion),
}).await.map_err(ValidateError::Get)?;
}).await.map_err(ValidateError::ModelFileDownload)?;
// decode dom (slow!)
let mut dom=read_dom(&mut std::io::Cursor::new(data)).map_err(ValidateError::ReadDom)?;
let mut dom=read_dom(&mut std::io::Cursor::new(data)).map_err(ValidateError::ModelFileDecode)?;
/* VALIDATE MAP */
@@ -104,12 +122,14 @@ impl Validator{
// check the source for illegal keywords
if source_has_illegal_keywords(source){
// immediately abort
return Err(ValidateError::Flagged);
// grab path to offending script
let path=get_partial_path(&dom,script);
return Err(ValidateError::ScriptFlaggedIllegalKeyword(path));
}
// associate a name and policy with the source code
// policy will be fetched from the database to replace the default policy
script_map.insert(source.clone(),NamePolicy{
name:script.name.clone(),
name:get_partial_path(&dom,script),
policy:Policy::None,
});
}
@@ -120,14 +140,12 @@ impl Validator{
futures::stream::iter(script_map.iter_mut().map(Ok))
.try_for_each_concurrent(Some(SCRIPT_CONCURRENCY),|(source,NamePolicy{policy,name})|async{
// get the hash
let mut hasher=siphasher::sip::SipHasher::new();
std::hash::Hasher::write(&mut hasher,source.as_bytes());
let hash=std::hash::Hasher::finish(&hasher);
let hash=hash_source(source.as_str());
// fetch the script policy
let script_policy=self.api.get_script_policy_from_hash(submissions_api::types::HashRequest{
hash:format!("{:016x}",hash).as_str(),
}).await.map_err(ValidateError::ApiGetScriptPolicy)?;
hash:hash.as_str(),
}).await.map_err(ValidateError::ApiGetScriptPolicyFromHash)?;
// write the policy to the script_map, fetching the replacement code if necessary
if let Some(script_policy)=script_policy{
@@ -144,11 +162,17 @@ impl Validator{
},
};
}else{
let (resource_type,resource_id)=match validate_info.ResourceID{
ResourceID::Mapfix(mapfix_id)=>(ResourceType::Mapfix,mapfix_id),
ResourceID::Submission(submission_id)=>(ResourceType::Submission,submission_id),
};
// upload the script
let script=self.api.create_script(submissions_api::types::CreateScriptRequest{
Name:name.as_str(),
Source:source.as_str(),
SubmissionID:Some(validate_info.SubmissionID),
ResourceType:resource_type,
ResourceID:Some(resource_id),
}).await.map_err(ValidateError::ApiCreateScript)?;
// create a None policy (pending review by yours truly)
@@ -169,10 +193,22 @@ impl Validator{
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"){
match script_map.get(source.as_str()).map(|p|&p.policy){
Some(Policy::Blocked)=>return Err(ValidateError::Blocked),
Some(Policy::Blocked)=>{
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)));
},
None
|Some(Policy::None)
=>return Err(ValidateError::NotAllowed),
=>{
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)));
},
Some(Policy::Allowed)=>(),
Some(Policy::Delete)=>{
modified=true;
@@ -194,7 +230,10 @@ impl Validator{
if modified{
// serialize model (slow!)
let mut data=Vec::new();
rbx_binary::to_writer(&mut data,&dom,dom.root().children()).map_err(ValidateError::WriteDom)?;
let &[map_ref]=dom.root().children()else{
return Err(ValidateError::ModelFileRootMustHaveOneChild);
};
rbx_binary::to_writer(&mut data,&dom,&[map_ref]).map_err(ValidateError::ModelFileEncode)?;
// upload a model lol
let model_id=if let Some(model_id)=validate_info.ValidatedModelID{
@@ -206,29 +245,45 @@ impl Validator{
ispublic:None,
allowComments:None,
groupId:None,
},data).await.map_err(ValidateError::Upload)?;
},data).await.map_err(ValidateError::AssetUpload)?;
response.AssetId
}else{
// grab the map instance from the map re
let Some(map_instance)=dom.get_by_ref(map_ref)else{
return Err(ValidateError::ModelFileChildRefIsNil);
};
// create new model
let response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
name:dom.root().name.clone(),
name:map_instance.name.clone(),
description:"".to_owned(),
ispublic:true,
allowComments:true,
groupId:None,
},data).await.map_err(ValidateError::Create)?;
},data).await.map_err(ValidateError::AssetCreate)?;
response.AssetId
};
// update the submission to use the validated model
self.api.update_submission_model(submissions_api::types::UpdateSubmissionModelRequest{
SubmissionID:validate_info.SubmissionID,
ModelID:model_id,
ModelVersion:1, //TODO
}).await.map_err(ValidateError::ApiUpdateSubmissionModel)?;
};
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)?;
},
}
}
Ok(())
}
@@ -290,6 +345,19 @@ fn recursive_collect_superclass(objects:&mut std::vec::Vec<rbx_dom_weak::types::
}
}
fn get_partial_path(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->String{
let mut names:Vec<_>=core::iter::successors(
Some(instance),
|i|dom.get_by_ref(i.parent())
).map(
|i|i.name.as_str()
).collect();
// discard the name of the root object
names.pop();
names.reverse();
names.join(".")
}
fn get_script_refs(dom:&rbx_dom_weak::WeakDom)->Vec<rbx_dom_weak::types::Ref>{
let mut scripts=std::vec::Vec::new();
recursive_collect_superclass(&mut scripts,dom,dom.root(),"LuaSourceContainer");

View File

@@ -3,15 +3,7 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
distDir: "build",
output: "standalone",
rewrites: async () => {
return [
{
source: "/api/:path*",
destination: "http://localhost:8082/v1/:path*"
}
]
},
images: {
images: {
remotePatterns: [
{
protocol: 'https',

View File

@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 3000",
"dev": "next dev -p 3000 --turbopack",
"build": "next build",
"start": "next start -p 3000",
"lint": "next lint"

View File

@@ -1,8 +1,27 @@
"use client"
import { redirect } from "next/navigation";
import { useEffect } from "react";
import Header from "./header";
async function login_check() {
const response = await fetch("/api/session/validate")
if (response.ok) {
const logged_in = await response.json()
if (!logged_in) {
redirect("https://auth.staging.strafes.net/oauth2/login?redirect=" + window.location.href)
}
} else {
console.error("No response from /api/session/validate")
}
}
export default function Webpage({children}: Readonly<{children?: React.ReactNode}>) {
return (<>
<Header/>
{children}
</>)
}
useEffect(() => { login_check() }, [])
return <>
<Header/>
{children}
</>
}

View File

@@ -10,7 +10,7 @@ $form-label-fontsize: 1.3rem;
:root {
color-scheme: light dark;
--header-height: 60px;
--header-height: 45px;
--page: white;
--header-grad-left: #363b40;

View File

@@ -0,0 +1,75 @@
@forward "./page/card.scss";
@use "../../globals.scss";
a {
color:rgb(255, 255, 255);
&:visited, &:hover, &:focus {
text-decoration: none;
color: rgb(255, 255, 255);
}
&:active {
color: rgb(192, 192, 192)
}
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
grid-template-rows: repeat(3, 1fr);
gap: 16px;
max-width: 100%;
margin: 0 auto;
overflow-x: hidden;
box-sizing: border-box;
}
@media (max-width: 768px) {
.grid {
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
margin: 0.3rem;
}
.pagination button {
padding: 0.25rem 0.5rem;
font-size: 1.15rem;
border: none;
border-radius: 0.35rem;
background-color: #33333350;
color: #fff;
cursor: pointer;
}
.pagination button:disabled {
background-color: #5555559a;
cursor: not-allowed;
}
.pagination-dots {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
justify-content: center;
width: 100%;
}
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #bbb;
cursor: pointer;
}
.dot.active {
background-color: #333;
}

View File

@@ -0,0 +1,87 @@
@use "../../../globals.scss";
.submissionCard {
display: flex;
background-color: #2020207c;
border: 1px solid #97979783;
border-radius: 10px;
box-sizing: border-box;
flex-direction: column;
justify-content: space-between;
height: 100%;
min-width: 180px;
max-width: 340px;
}
.content {
display: flex;
flex-grow: 1;
flex-direction: column;
justify-content: space-between;
}
.details {
padding: 2px 4px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin: 3px 0px;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
margin: 3px 0px;
}
.map-image {
border-radius: 10px 10px 0 0;
overflow: hidden;
> img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.displayName {
font-size: 1rem;
font-weight: bold;
overflow: hidden;
max-width: 70%;
text-overflow: ellipsis;
white-space: nowrap;
}
.rating {
flex-shrink: 0;
}
.author {
display: flex;
align-items: center;
gap: 0.5rem;
flex-grow: 1;
min-width: 0;
}
.author span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.rating {
margin-left: auto;
flex-shrink: 0;
}
.avatar {
border-radius: 50%;
object-fit: cover;
}

View File

@@ -0,0 +1,19 @@
@forward "./page/commentWindow.scss";
@forward "./page/reviewStatus.scss";
@forward "./page/ratingWindow.scss";
@forward "./page/reviewButtons.scss";
@forward "./page/comments.scss";
@forward "./page/review.scss";
@forward "./page/map.scss";
@use "../../../globals.scss";
.map-page-main {
display: flex;
justify-content: center;
width: 100vw;
}
.by-creator {
margin-top: 10px;
}

View File

@@ -0,0 +1,56 @@
@use "../../../../globals.scss";
#comment-text-field {
@include globals.border-with-radius;
resize: none;
width: 100%;
height: 100px;
background-color: var(--comment-area)
}
.leave-comment-window {
@include globals.border-with-radius;
width: 100%;
height: 230px;
margin-top: 35px;
.rating-type {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
gap: 35%;
.rating-right {
display: grid;
> span {
margin: 6px 0 6px 0;
}
}
p {
margin: 15px 0 15px 0;
}
}
header {
display: flex;
align-items: center;
background-color: var(--window-header);
border-bottom: globals.$review-border;
height: 45px;
p {
font-weight: bold;
margin: 0 0 0 20px;
}
}
main {
padding: 20px;
button {
margin-top: 9px;
}
}
}

View File

@@ -0,0 +1,49 @@
$comments-size: 60px;
.comments {
display: grid;
gap: 25px;
margin-top: 20px;
.no-comments {
text-align: center;
margin: 0;
}
.commenter {
display: flex;
height: $comments-size;
//BhopMaptest comment
&[data-highlighted="true"] {
background-color: var(--comment-highlighted);
}
> img {
border-radius: 50%;
}
.name {
font: {
weight: 500;
size: 1.3em;
};
}
.date {
font-size: .8em;
margin: 0 0 0 5px;
color: #646464
}
.details {
display: grid;
margin-left: 10px;
header {
display: flex;
align-items: center;
}
p:not(.date) {
margin: 0;
}
}
}
}

View File

@@ -0,0 +1,15 @@
@use "../../../../globals.scss";
.map-image-area {
@include globals.border-with-radius;
display: flex;
justify-content: center;
align-items: center;
width: 350px;
height: 350px;
> p {
text-align: center;
margin: 0;
}
}

View File

@@ -0,0 +1,43 @@
@use "../../../../globals.scss";
.rating-window {
@include globals.border-with-radius;
width: 100%;
.rating-type {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
gap: 35%;
.rating-right {
display: grid;
> span {
margin: 6px 0 6px 0;
}
}
p {
margin: 15px 0 15px 0;
}
}
header {
display: flex;
align-items: center;
background-color: var(--window-header);
border-bottom: globals.$review-border;
height: 45px;
p {
font-weight: bold;
margin: 0 0 0 20px;
}
}
main {
display: grid;
place-items: center;
}
}

View File

@@ -0,0 +1,47 @@
@use "../../../../globals.scss";
.review-info {
width: 650px;
height: 100%;
> div {
display: flex;
justify-content: space-between;
align-items: center;
}
p, h1 {
color: var(--text-color);
}
h1 {
font: {
weight: 500;
size: 1.8rem
};
margin: 0;
}
a {
color: var(--anchor-link-review);
&:hover {
text-decoration: underline;
}
}
}
.review-section {
display: flex;
gap: 50px;
margin-top: 20px;
}
.review-area {
display: grid;
justify-content: center;
gap: 25px;
img {
width: 100%;
height: 350px;
object-fit: contain
}
}

View File

@@ -0,0 +1,13 @@
@use "../../../../globals.scss";
.review-set {
@include globals.border-with-radius;
display: grid;
align-items: center;
gap: 10px;
padding: 10px;
button {
width: 100%;
}
}

View File

@@ -0,0 +1,80 @@
$UnderConstruction: "0";
$Submitted: "1";
$ChangesRequested: "2";
$Accepted: "3";
$Validating: "4";
$Validated: "5";
$Uploading: "6";
$Uploaded: "7";
$Rejected: "8";
$Released: "9";
.review-status {
border-radius: 5px;
p {
margin: 3px 25px 3px 25px;
font-weight: bold;
}
&[data-review-status="#{$Released}"] {
background-color: orange;
p {
color: white;
}
}
&[data-review-status="#{$Rejected}"] {
background-color: orange;
p {
color: white;
}
}
&[data-review-status="#{$Uploading}"] {
background-color: orange;
p {
color: white;
}
}
&[data-review-status="#{$Uploaded}"] {
background-color: orange;
p {
color: white;
}
}
&[data-review-status="#{$Validated}"] {
background-color: orange;
p {
color: white;
}
}
&[data-review-status="#{$Validating}"] {
background-color: orange;
p {
color: white;
}
}
&[data-review-status="#{$Accepted}"] {
background-color: rgb(2, 162, 2);
p {
color: white;
}
}
&[data-review-status="#{$ChangesRequested}"] {
background-color: orange;
p {
color: white;
}
}
&[data-review-status="#{$Submitted}"] {
background-color: orange;
p {
color: white;
}
}
&[data-review-status="#{$UnderConstruction}"] {
background-color: orange;
p {
color: white;
}
}
}

View File

@@ -0,0 +1,68 @@
import type { MapfixInfo } from "@/app/ts/Mapfix";
import { Button } from "@mui/material"
import Window from "./_window";
import SendIcon from '@mui/icons-material/Send';
import Image from "next/image";
interface CommentersProps {
comments_data: CreatorAndReviewStatus
}
interface CreatorAndReviewStatus {
asset_id: MapfixInfo["AssetID"],
creator: MapfixInfo["DisplayName"],
review: MapfixInfo["StatusID"],
status_message: MapfixInfo["StatusMessage"],
comments: Comment[],
name: string
}
interface Comment {
picture?: string, //TEMP
comment: string,
date: string,
name: string
}
function AddComment(comment: Comment) {
const IsBhopMaptest = comment.name == "BhopMaptest" //Highlighted commenter
return (
<div className="commenter" data-highlighted={IsBhopMaptest}>
<Image src={comment.picture as string} alt={`${comment.name}'s comment`}/>
<div className="details">
<header>
<p className="name">{comment.name}</p>
<p className="date">{comment.date}</p>
</header>
<p className="comment">{comment.comment}</p>
</div>
</div>
);
}
function LeaveAComment() {
return (
<Window title="Leave a Comment:" className="leave-comment-window">
<textarea name="comment-box" id="comment-text-field"></textarea>
<Button variant="outlined" endIcon={<SendIcon/>}>Submit</Button>
</Window>
)
}
export default function Comments(stats: CommentersProps) {
return (<>
<section className="comments">
{stats.comments_data.comments.length===0
&& <p className="no-comments">There are no comments.</p>
|| stats.comments_data.comments.map(comment => (
<AddComment key={comment.name} name={comment.name} date={comment.date} comment={comment.comment}/>
))}
</section>
<LeaveAComment/>
</>)
}
export {
type CreatorAndReviewStatus
}

View File

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

View File

@@ -0,0 +1,74 @@
import { Button, ButtonOwnProps } from "@mui/material";
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 ReviewButton {
name: Review,
action: ApiActions,
mapfixId: string,
color: ButtonOwnProps["color"]
}
interface ReviewId {
mapfixId: string
}
async function ReviewButtonClicked(action: ApiActions, mapfixId: string) {
try {
const response = await fetch(`/api/mapfixes/${mapfixId}/status/${action}`, {
method: "POST",
headers: {
"Content-type": "application/json",
}
});
// Check if the HTTP request was successful
if (!response.ok) {
const errorDetails = await response.text();
// Throw an error with detailed information
throw new Error(`HTTP error! status: ${response.status}, details: ${errorDetails}`);
}
window.location.reload();
} catch (error) {
console.error("Error updating mapfix status:", error);
}
}
function ReviewButton(props: ReviewButton) {
return <Button
color={props.color}
variant="contained"
onClick={() => { ReviewButtonClicked(props.action, props.mapfixId) }}>{props.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:
// ---------------|-----------|-----------------------
// Submit | Submitter | UnderConstruction, ChangesRequested
// Revoke | Submitter | Submitted, ChangesRequested
// Accept | Reviewer | Submitted
// Validate | Reviewer | Accepted
// ResetValidating| Reviewer | Validating
// Reject | Reviewer | Submitted
// RequestChanges | Reviewer | Validated, Accepted, Submitted
// Upload | MapAdmin | Validated
// ResetUploading | MapAdmin | Uploading
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}/>
</section>
)
}

View File

@@ -0,0 +1,20 @@
interface WindowStruct {
className: string,
title: string,
children: React.ReactNode
}
export default function Window(window: WindowStruct) {
return (
<section className={window.className}>
<header>
<p>{window.title}</p>
</header>
<main>{window.children}</main>
</section>
)
}
export {
type WindowStruct
}

View File

@@ -0,0 +1,105 @@
"use client"
import { MapfixInfo, MapfixStatusToString } from "@/app/ts/Mapfix";
import type { CreatorAndReviewStatus } from "./_comments";
import { MapImage } from "./_map";
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>
)
}
function RatingArea(mapfix: ReviewId) {
return (
<aside className="review-area">
<section className="map-image-area">
<MapImage/>
</section>
<Ratings/>
<ReviewButtons mapfixId={mapfix.mapfixId}/>
</aside>
)
}
function TitleAndComments(stats: CreatorAndReviewStatus) {
const Review = MapfixStatusToString(stats.review)
// TODO: hide status message when status is not "Accepted"
return (
<main className="review-info">
<div>
<h1>{stats.name}</h1>
<aside data-review-status={stats.review} className="review-status">
<p>{Review}</p>
</aside>
</div>
<p className="by-creator">by <Link href="" target="_blank">{stats.creator}</Link></p>
<p className="asset-id">Model Asset ID {stats.asset_id}</p>
<p className="status-message">Validation Error: {stats.status_message}</p>
<span className="spacer"></span>
<Comments comments_data={stats}/>
</main>
)
}
export default function MapfixInfoPage() {
const dynamicId = useParams<{mapfixId: string}>()
const [mapfix, setMapfix] = useState<MapfixInfo | null>(null)
useEffect(() => { // needs to be client sided since server doesn't have a session, nextjs got mad at me for exporting an async function: (https://nextjs.org/docs/messages/no-async-client-component)
async function getMapfix() {
const res = await fetch(`/api/mapfixes/${dynamicId.mapfixId}`)
if (res.ok) {
setMapfix(await res.json())
}
}
getMapfix()
}, [dynamicId.mapfixId])
if (!mapfix) {
return <Webpage>
{/* TODO: Add skeleton loading thingy ? Maybe ? (https://mui.com/material-ui/react-skeleton/) */}
</Webpage>
}
return (
<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={[]}/>
</section>
</main>
</Webpage>
)
}

View File

@@ -0,0 +1,41 @@
import React from "react";
import Image from "next/image";
import Link from "next/link";
import { Rating } from "@mui/material";
interface SubmissionCardProps {
displayName: string;
assetId: number;
rating: number;
author: string;
id: number;
}
export default function SubmissionCard(props: SubmissionCardProps) {
return (
<Link href={`/submissions/${props.id}`}>
<div className="submissionCard">
<div className="content">
<div className="map-image">
{/* TODO: Grab image of model */}
<Image height={200} width={200} priority={true} src="https://api.ic3.space/strafe/map-images/11222350808" style={{ width: `100%` }} 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="https://api.ic3.space/strafe/map-images/11222350808" alt={props.author}/>
<span>{props.author}</span>
</div>
</div>
</div>
</div>
</div>
</Link>
);
}

View File

@@ -0,0 +1,18 @@
interface WindowStruct {
children: React.ReactNode,
className: string,
title: string,
}
export default function Window(window: WindowStruct) {
return <section className={window.className}>
<header>
<p>{window.title}</p>
</header>
<main>{window.children}</main>
</section>
}
export {
type WindowStruct
}

View File

@@ -0,0 +1,110 @@
'use client'
import React, { useState, useEffect } from "react";
import { MapfixInfo } from "../ts/Mapfix";
import MapfixCard from "./_card";
import Webpage from "@/app/_components/webpage";
import "./(styles)/page.scss";
export default function MapfixInfoPage() {
const [mapfixes, setMapfixes] = useState<MapfixInfo[]>([])
const [currentPage, setCurrentPage] = useState(0);
const cardsPerPage = 24; // built to fit on a 1920x1080 monitor
const totalPages = Math.ceil(mapfixes.length / cardsPerPage);
const currentCards = mapfixes.slice(
currentPage * cardsPerPage,
(currentPage + 1) * cardsPerPage
);
const nextPage = () => {
if (currentPage < totalPages - 1) {
setCurrentPage(currentPage + 1);
}
};
const prevPage = () => {
if (currentPage > 0) {
setCurrentPage(currentPage - 1);
}
};
useEffect(() => {
async function fetchMapfixes() {
const res = await fetch('/api/mapfixes?Page=1&Limit=100')
if (res.ok) {
setMapfixes(await res.json())
}
}
setTimeout(() => {
fetchMapfixes()
}, 50);
}, [])
if (!mapfixes) {
return <Webpage>
<main>
Loading...
</main>
</Webpage>
}
if (mapfixes && mapfixes.length == 0) {
return <Webpage>
<main>
Mapfixes list is empty.
</main>
</Webpage>
}
return (
// TODO: Add filter settings & searchbar & page selector
<Webpage>
<main
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
padding: '1rem',
width: '100%',
maxWidth: '100vw',
boxSizing: 'border-box',
overflowX: 'hidden'
}}
>
<div className="pagination-dots">
{Array.from({ length: totalPages }).map((_, index) => (
<span
key={index}
className={`dot ${index === currentPage ? 'active' : ''}`}
onClick={() => setCurrentPage(index)}
></span>
))}
</div>
<div className="pagination">
<button onClick={prevPage} disabled={currentPage === 0}>&lt;</button>
<span>
Page {currentPage + 1} of {totalPages}
</span>
<button onClick={nextPage} disabled={currentPage === totalPages - 1}>&gt;</button>
</div>
<div className="grid">
{currentCards.map((mapfix) => (
<MapfixCard
key={mapfix.ID}
id={mapfix.ID}
assetId={mapfix.AssetID}
displayName={mapfix.DisplayName}
author={mapfix.Creator}
rating={mapfix.StatusID}
/>
))}
</div>
</main>
</Webpage>
)
}

View File

@@ -0,0 +1,54 @@
@use "../../../../globals.scss";
::placeholder {
color: var(--placeholder-text)
}
.form-spacer {
margin-bottom: 20px;
&:last-of-type {
margin-top: 15px;
}
}
#target-asset-radio {
color: var(--text-color);
font-size: globals.$form-label-fontsize;
}
.form-field {
width: 850px;
& label, & input {
color: var(--text-color);
}
& fieldset {
border-color: rgb(100,100,100);
}
& span {
color: white;
}
}
main {
display: grid;
justify-content: center;
align-items: center;
margin-inline: auto;
width: 700px;
}
header h1 {
text-align: center;
color: var(--text-color);
}
form {
display: grid;
gap: 25px;
fieldset {
border: blue
}
}

View File

@@ -0,0 +1,65 @@
import { FormControl, Select, InputLabel, MenuItem } from "@mui/material";
import { styled } from '@mui/material/styles';
import InputBase from '@mui/material/InputBase';
import React from "react";
import { SelectChangeEvent } from "@mui/material";
// TODO: Properly style everything instead of pasting 🤚
type GameSelectionProps = {
game: number;
setGame: React.Dispatch<React.SetStateAction<number>>;
};
const BootstrapInput = styled(InputBase)(({ theme }) => ({
'label + &': {
marginTop: theme.spacing(3),
},
'& .MuiInputBase-input': {
backgroundColor: '#0000',
color: '#FFF',
border: '1px solid rgba(175, 175, 175, 0.66)',
fontSize: 16,
padding: '10px 26px 10px 12px',
transition: theme.transitions.create(['border-color', 'box-shadow']),
fontFamily: [
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'"Helvetica Neue"',
'Arial',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
].join(','),
'&:focus': {
borderRadius: 4,
borderColor: '#80bdff',
boxShadow: '0 0 0 0.2rem rgba(0,123,255,.25)',
},
},
}));
export default function GameSelection({ game, setGame }: GameSelectionProps) {
const handleChange = (event: SelectChangeEvent) => {
setGame(Number(event.target.value)); // TODO: Change later!! there's 100% a proper way of doing this
};
return (
<FormControl>
<InputLabel sx={{ color: "#646464" }}>Game</InputLabel>
<Select
value={String(game)}
label="Game"
onChange={handleChange}
input={<BootstrapInput />}
>
<MenuItem value={1}>Bhop</MenuItem>
<MenuItem value={2}>Surf</MenuItem>
<MenuItem value={3}>Fly Trials</MenuItem>
</Select>
</FormControl>
);
}

Some files were not shown because too many files have changed in this diff Show More