Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
3f8a6fdff6
|
|||
|
c5274ff194
|
|||
| 170e7c64b6 | |||
| b443866dd6 | |||
|
ebe37ad6a2
|
|||
|
131dad7ae0
|
|||
| 127402fa77 | |||
|
40f83a4e30
|
|||
|
b6d4ce4f80
|
|||
| 07391a84cb | |||
|
|
3f848a35c8 | ||
| e5e2387502 | |||
|
90d13d28ae
|
|||
| 513b9722b1 | |||
|
3da8e414e6
|
|||
|
2acc30e18c
|
|||
|
a990ed458c
|
|||
|
4055ef550e
|
|||
|
555844e6ee
|
|||
|
80f30d20fa
|
|||
|
489a8c9c10
|
|||
|
534598ba70
|
|||
|
fdc0240698
|
|||
|
b0829bc1fc
|
|||
|
845f8e69d9
|
|||
|
0d8937e896
|
|||
| 2927afd848 | |||
| 8f6c543f81 | |||
|
7c95f8ddd0
|
|||
|
24964407bd
|
|||
|
|
8d5bd9e523 | ||
|
|
e1fc637619 | ||
|
|
762ee874a0 | ||
| a1c84ff225 | |||
|
cea6242dd7
|
|||
| fefe116611 | |||
|
0ada77421f
|
|||
|
fa2d611534
|
|||
| 81539a606c | |||
| 32095296c2 | |||
|
8ea5ee2d41
|
|||
|
954dbaeac6
|
|||
| 5b7efa2426 | |||
|
|
740e3c8932 | ||
|
4f31f8c75a
|
|||
|
d39a8c0208
|
|||
|
636282b993
|
|||
|
e3667fec0c
|
|||
|
0dff464561
|
|||
|
e30fb5f916
|
|||
|
9dd7156f38
|
|||
|
fc9aae4235
|
|||
|
a11a0d2fd5
|
|||
|
9dad1a6b4d
|
|||
|
a95e6b7a9a
|
|||
|
|
4c1aef9113 | ||
|
|
c98d170423 | ||
|
6d14047f57
|
|||
|
41663624d3
|
|||
|
49b9b41085
|
|||
|
3614018794
|
|||
|
872b98aa74
|
|||
|
d5c8477869
|
|||
|
b600ca582b
|
|||
|
adbcbed9ac
|
|||
|
8f8d685f71
|
|||
|
a669de3c0b
|
|||
|
649b941d5f
|
|||
|
1b4456f30a
|
|||
|
d34a5c7091
|
|||
|
2f36877cb6
|
|||
|
3a124b8190
|
|||
|
6cc6da4879
|
|||
|
123b0c9a81
|
|||
|
54b0abbbf3
|
|||
|
1b0384da11
|
|||
|
e0cebfd80e
|
|||
|
5ba52ecb57
|
|||
|
9e42050a65
|
|||
|
c817bfc8c8
|
|||
|
8f97ca6690
|
|||
|
f220cb62bc
|
|||
|
f090fd7d68
|
|||
|
404e1281ff
|
|||
|
e4f710c83f
|
|||
|
a942c81ea8
|
|||
|
109b24061a
|
|||
|
ddef30984f
|
|||
|
9331f37d70
|
|||
|
c4f910c1f0
|
|||
|
343a4011dd
|
|||
|
c63997d161
|
|||
|
ea58fcedc9
|
|||
|
50e3fb283c
|
|||
|
aa513a7973
|
|||
|
eff9097456
|
|||
|
668c5fef51
|
|||
|
57db5f738e
|
|||
|
3789755a19
|
|||
|
ee6c37ab9d
|
|||
|
12bfbfb0a0
|
|||
|
c57a53692d
|
|||
|
ccf07c5931
|
|||
|
6efab4f411
|
|||
|
34d1db02a5
|
|||
|
d86ed0cdf5
|
|||
|
d19763349e
|
|||
|
5846e92924
|
|||
|
34b8d7475d
|
|||
|
a5daa2df4a
|
|||
|
1b73af9fe2
|
|||
|
8433030562
|
|||
|
8372665fd3
|
|||
|
d24b342738
|
|||
|
796f31aadf
|
|||
|
44f8736838
|
|||
|
b7e5d82c13
|
|||
|
169007f16e
|
|||
|
2519c9faa1
|
|||
|
1ff6bdbd4c
|
|||
|
d1ca9bdab9
|
|||
| c76ff3b687 | |||
| a42501d254 | |||
| f915c51ba4 | |||
| ff9da333eb | |||
| 1dabd216aa | |||
| cc7e890580 | |||
| 99d1b38535 | |||
| 12ca1b7dab | |||
|
fa1b44f172
|
|||
|
03519e9337
|
|||
|
60b6d30379
|
|||
|
19b8f7b7a2
|
|||
|
4f586c6176
|
|||
|
d1a70509b7
|
|||
|
95bfb87c6e
|
|||
|
de0cf37918
|
|||
|
f1fd826c62
|
|||
|
1380a00872
|
|||
|
174a210f81
|
|||
|
67a03f394f
|
|||
|
6eebe404d5
|
|||
|
1d409218a5
|
|||
|
e2c72c90c7
|
867
Cargo.lock
generated
867
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -2,5 +2,6 @@
|
|||||||
members = [
|
members = [
|
||||||
"validation",
|
"validation",
|
||||||
"validation/api",
|
"validation/api",
|
||||||
|
"validation/cli",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|||||||
@@ -26,10 +26,11 @@ Prerequisite: golang installed
|
|||||||
|
|
||||||
Prerequisite: bun installed
|
Prerequisite: bun installed
|
||||||
|
|
||||||
The environment variable `API_HOST` will need to be set for the middleware.
|
The environment variables `API_HOST` and `AUTH_HOST` will need to be set for the middleware.
|
||||||
Example `.env` in web's root:
|
Example `.env` in web's root:
|
||||||
```
|
```
|
||||||
API_HOST="http://localhost:8082/v1/"
|
API_HOST="http://localhost:8082/"
|
||||||
|
AUTH_HOST="http://localhost:8083/"
|
||||||
```
|
```
|
||||||
|
|
||||||
1. `cd web`
|
1. `cd web`
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ services:
|
|||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
environment:
|
environment:
|
||||||
- API_HOST=http://submissions:8082/v1
|
- API_HOST=http://submissions:8082/v1
|
||||||
|
- AUTH_HOST=http://localhost:8080/
|
||||||
|
|
||||||
validation:
|
validation:
|
||||||
image:
|
image:
|
||||||
|
|||||||
@@ -73,6 +73,56 @@ paths:
|
|||||||
- Mapfixes
|
- Mapfixes
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/components/parameters/MapfixID'
|
- $ref: '#/components/parameters/MapfixID'
|
||||||
|
- name: ModelVersion
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
minimum: 0
|
||||||
|
- name: DisplayName
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
maxLength: 128
|
||||||
|
- name: Creator
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
maxLength: 128
|
||||||
|
- name: GameID
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
minimum: 0
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: Successful response
|
||||||
|
default:
|
||||||
|
description: General Error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Error"
|
||||||
|
/mapfixes/{MapfixID}/status/validator-request-changes:
|
||||||
|
post:
|
||||||
|
summary: (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested
|
||||||
|
operationId: actionMapfixRequestChanges
|
||||||
|
tags:
|
||||||
|
- Mapfixes
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/MapfixID'
|
||||||
|
- name: ErrorMessage
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
minLength: 0
|
||||||
|
maxLength: 4096
|
||||||
responses:
|
responses:
|
||||||
"204":
|
"204":
|
||||||
description: Successful response
|
description: Successful response
|
||||||
@@ -107,7 +157,7 @@ paths:
|
|||||||
- Mapfixes
|
- Mapfixes
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/components/parameters/MapfixID'
|
- $ref: '#/components/parameters/MapfixID'
|
||||||
- name: StatusMessage
|
- name: ErrorMessage
|
||||||
in: query
|
in: query
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
@@ -228,6 +278,56 @@ paths:
|
|||||||
- Submissions
|
- Submissions
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/components/parameters/SubmissionID'
|
- $ref: '#/components/parameters/SubmissionID'
|
||||||
|
- name: ModelVersion
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
minimum: 0
|
||||||
|
- name: DisplayName
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
maxLength: 128
|
||||||
|
- name: Creator
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
maxLength: 128
|
||||||
|
- name: GameID
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
minimum: 0
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: Successful response
|
||||||
|
default:
|
||||||
|
description: General Error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Error"
|
||||||
|
/submissions/{SubmissionID}/status/validator-request-changes:
|
||||||
|
post:
|
||||||
|
summary: (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested
|
||||||
|
operationId: actionSubmissionRequestChanges
|
||||||
|
tags:
|
||||||
|
- Submissions
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/SubmissionID'
|
||||||
|
- name: ErrorMessage
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
minLength: 0
|
||||||
|
maxLength: 4096
|
||||||
responses:
|
responses:
|
||||||
"204":
|
"204":
|
||||||
description: Successful response
|
description: Successful response
|
||||||
@@ -262,7 +362,7 @@ paths:
|
|||||||
- Submissions
|
- Submissions
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/components/parameters/SubmissionID'
|
- $ref: '#/components/parameters/SubmissionID'
|
||||||
- name: StatusMessage
|
- name: ErrorMessage
|
||||||
in: query
|
in: query
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
@@ -567,6 +667,7 @@ components:
|
|||||||
- AssetID
|
- AssetID
|
||||||
- AssetVersion
|
- AssetVersion
|
||||||
- TargetAssetID
|
- TargetAssetID
|
||||||
|
- Description
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
OperationID:
|
OperationID:
|
||||||
@@ -599,6 +700,9 @@ components:
|
|||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
minimum: 0
|
minimum: 0
|
||||||
|
Description:
|
||||||
|
type: string
|
||||||
|
maxLength: 256
|
||||||
SubmissionCreate:
|
SubmissionCreate:
|
||||||
required:
|
required:
|
||||||
- OperationID
|
- OperationID
|
||||||
@@ -608,6 +712,8 @@ components:
|
|||||||
- GameID
|
- GameID
|
||||||
- AssetID
|
- AssetID
|
||||||
- AssetVersion
|
- AssetVersion
|
||||||
|
- Status
|
||||||
|
- Roles
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
OperationID:
|
OperationID:
|
||||||
@@ -636,6 +742,14 @@ components:
|
|||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
minimum: 0
|
minimum: 0
|
||||||
|
Status:
|
||||||
|
type: integer
|
||||||
|
format: uint32
|
||||||
|
minimum: 0
|
||||||
|
maximum: 9
|
||||||
|
Roles:
|
||||||
|
type: integer
|
||||||
|
format: uint32
|
||||||
Script:
|
Script:
|
||||||
required:
|
required:
|
||||||
- ID
|
- ID
|
||||||
|
|||||||
88
openapi.yaml
88
openapi.yaml
@@ -384,6 +384,23 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Error"
|
$ref: "#/components/schemas/Error"
|
||||||
|
/mapfixes/{MapfixID}/status/bypass-submit:
|
||||||
|
post:
|
||||||
|
summary: Role Reviewer changes status from ChangesRequested -> Submitted
|
||||||
|
operationId: actionMapfixBypassSubmit
|
||||||
|
tags:
|
||||||
|
- Mapfixes
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/MapfixID'
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: Successful response
|
||||||
|
default:
|
||||||
|
description: General Error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Error"
|
||||||
/mapfixes/{MapfixID}/status/reset-submitting:
|
/mapfixes/{MapfixID}/status/reset-submitting:
|
||||||
post:
|
post:
|
||||||
summary: Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction
|
summary: Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction
|
||||||
@@ -654,6 +671,31 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Error"
|
$ref: "#/components/schemas/Error"
|
||||||
|
/submissions-admin:
|
||||||
|
post:
|
||||||
|
summary: Trigger the validator to create a new submission
|
||||||
|
operationId: createSubmissionAdmin
|
||||||
|
tags:
|
||||||
|
- Submissions
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SubmissionTriggerCreate'
|
||||||
|
responses:
|
||||||
|
"201":
|
||||||
|
description: Successful response
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/OperationID"
|
||||||
|
default:
|
||||||
|
description: General Error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Error"
|
||||||
/submissions/{SubmissionID}:
|
/submissions/{SubmissionID}:
|
||||||
get:
|
get:
|
||||||
summary: Retrieve map with ID
|
summary: Retrieve map with ID
|
||||||
@@ -791,6 +833,23 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Error"
|
$ref: "#/components/schemas/Error"
|
||||||
|
/submissions/{SubmissionID}/status/bypass-submit:
|
||||||
|
post:
|
||||||
|
summary: Role Reviewer changes status from ChangesRequested -> Submitted
|
||||||
|
operationId: actionSubmissionBypassSubmit
|
||||||
|
tags:
|
||||||
|
- Submissions
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/SubmissionID'
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: Successful response
|
||||||
|
default:
|
||||||
|
description: General Error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Error"
|
||||||
/submissions/{SubmissionID}/status/reset-submitting:
|
/submissions/{SubmissionID}/status/reset-submitting:
|
||||||
post:
|
post:
|
||||||
summary: Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction
|
summary: Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction
|
||||||
@@ -1318,6 +1377,7 @@ components:
|
|||||||
- ID
|
- ID
|
||||||
- Date
|
- Date
|
||||||
- User
|
- User
|
||||||
|
- Username
|
||||||
- ResourceType
|
- ResourceType
|
||||||
- ResourceID
|
- ResourceID
|
||||||
- EventType
|
- EventType
|
||||||
@@ -1332,6 +1392,9 @@ components:
|
|||||||
User:
|
User:
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
|
Username:
|
||||||
|
type: string
|
||||||
|
maxLength: 64
|
||||||
ResourceType:
|
ResourceType:
|
||||||
type: integer
|
type: integer
|
||||||
format: int32
|
format: int32
|
||||||
@@ -1440,7 +1503,7 @@ components:
|
|||||||
- Completed
|
- Completed
|
||||||
- TargetAssetID
|
- TargetAssetID
|
||||||
- StatusID
|
- StatusID
|
||||||
- StatusMessage
|
- Description
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
ID:
|
ID:
|
||||||
@@ -1487,7 +1550,7 @@ components:
|
|||||||
type: integer
|
type: integer
|
||||||
format: int32
|
format: int32
|
||||||
minimum: 0
|
minimum: 0
|
||||||
StatusMessage:
|
Description:
|
||||||
type: string
|
type: string
|
||||||
maxLength: 256
|
maxLength: 256
|
||||||
Mapfixes:
|
Mapfixes:
|
||||||
@@ -1508,6 +1571,7 @@ components:
|
|||||||
required:
|
required:
|
||||||
- AssetID
|
- AssetID
|
||||||
- TargetAssetID
|
- TargetAssetID
|
||||||
|
- Description
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
AssetID:
|
AssetID:
|
||||||
@@ -1518,6 +1582,9 @@ components:
|
|||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
minimum: 0
|
minimum: 0
|
||||||
|
Description:
|
||||||
|
type: string
|
||||||
|
maxLength: 256
|
||||||
Operation:
|
Operation:
|
||||||
required:
|
required:
|
||||||
- OperationID
|
- OperationID
|
||||||
@@ -1566,7 +1633,6 @@ components:
|
|||||||
- Completed
|
- Completed
|
||||||
# - UploadedAssetID
|
# - UploadedAssetID
|
||||||
- StatusID
|
- StatusID
|
||||||
- StatusMessage
|
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
ID:
|
ID:
|
||||||
@@ -1621,9 +1687,6 @@ components:
|
|||||||
type: integer
|
type: integer
|
||||||
format: int32
|
format: int32
|
||||||
minimum: 0
|
minimum: 0
|
||||||
StatusMessage:
|
|
||||||
type: string
|
|
||||||
maxLength: 256
|
|
||||||
Submissions:
|
Submissions:
|
||||||
required:
|
required:
|
||||||
- Total
|
- Total
|
||||||
@@ -1641,12 +1704,25 @@ components:
|
|||||||
SubmissionTriggerCreate:
|
SubmissionTriggerCreate:
|
||||||
required:
|
required:
|
||||||
- AssetID
|
- AssetID
|
||||||
|
- DisplayName
|
||||||
|
- Creator
|
||||||
|
- GameID
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
AssetID:
|
AssetID:
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
minimum: 0
|
minimum: 0
|
||||||
|
DisplayName:
|
||||||
|
type: string
|
||||||
|
maxLength: 128
|
||||||
|
Creator:
|
||||||
|
type: string
|
||||||
|
maxLength: 128
|
||||||
|
GameID:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
minimum: 0
|
||||||
ReleaseInfo:
|
ReleaseInfo:
|
||||||
required:
|
required:
|
||||||
- SubmissionID
|
- SubmissionID
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ type Invoker interface {
|
|||||||
//
|
//
|
||||||
// POST /mapfixes/{MapfixID}/status/reset-validating
|
// POST /mapfixes/{MapfixID}/status/reset-validating
|
||||||
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
|
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
|
||||||
|
// ActionMapfixBypassSubmit invokes actionMapfixBypassSubmit operation.
|
||||||
|
//
|
||||||
|
// Role Reviewer changes status from ChangesRequested -> Submitted.
|
||||||
|
//
|
||||||
|
// POST /mapfixes/{MapfixID}/status/bypass-submit
|
||||||
|
ActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) error
|
||||||
// ActionMapfixReject invokes actionMapfixReject operation.
|
// ActionMapfixReject invokes actionMapfixReject operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer changes status from Submitted -> Rejected.
|
// Role Reviewer changes status from Submitted -> Rejected.
|
||||||
@@ -96,6 +102,12 @@ type Invoker interface {
|
|||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/reset-validating
|
// POST /submissions/{SubmissionID}/status/reset-validating
|
||||||
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
|
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
|
||||||
|
// ActionSubmissionBypassSubmit invokes actionSubmissionBypassSubmit operation.
|
||||||
|
//
|
||||||
|
// Role Reviewer changes status from ChangesRequested -> Submitted.
|
||||||
|
//
|
||||||
|
// POST /submissions/{SubmissionID}/status/bypass-submit
|
||||||
|
ActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) error
|
||||||
// ActionSubmissionReject invokes actionSubmissionReject operation.
|
// ActionSubmissionReject invokes actionSubmissionReject operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer changes status from Submitted -> Rejected.
|
// Role Reviewer changes status from Submitted -> Rejected.
|
||||||
@@ -181,6 +193,12 @@ type Invoker interface {
|
|||||||
//
|
//
|
||||||
// POST /submissions
|
// POST /submissions
|
||||||
CreateSubmission(ctx context.Context, request *SubmissionTriggerCreate) (*OperationID, error)
|
CreateSubmission(ctx context.Context, request *SubmissionTriggerCreate) (*OperationID, error)
|
||||||
|
// CreateSubmissionAdmin invokes createSubmissionAdmin operation.
|
||||||
|
//
|
||||||
|
// Trigger the validator to create a new submission.
|
||||||
|
//
|
||||||
|
// POST /submissions-admin
|
||||||
|
CreateSubmissionAdmin(ctx context.Context, request *SubmissionTriggerCreate) (*OperationID, error)
|
||||||
// CreateSubmissionAuditComment invokes createSubmissionAuditComment operation.
|
// CreateSubmissionAuditComment invokes createSubmissionAuditComment operation.
|
||||||
//
|
//
|
||||||
// Post a comment to the audit log.
|
// Post a comment to the audit log.
|
||||||
@@ -512,6 +530,130 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionMapfixBypassSubmit invokes actionMapfixBypassSubmit operation.
|
||||||
|
//
|
||||||
|
// Role Reviewer changes status from ChangesRequested -> Submitted.
|
||||||
|
//
|
||||||
|
// POST /mapfixes/{MapfixID}/status/bypass-submit
|
||||||
|
func (c *Client) ActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) error {
|
||||||
|
_, err := c.sendActionMapfixBypassSubmit(ctx, params)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) (res *ActionMapfixBypassSubmitNoContent, err error) {
|
||||||
|
otelAttrs := []attribute.KeyValue{
|
||||||
|
otelogen.OperationID("actionMapfixBypassSubmit"),
|
||||||
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
|
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/bypass-submit"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run stopwatch.
|
||||||
|
startTime := time.Now()
|
||||||
|
defer func() {
|
||||||
|
// Use floating point division here for higher precision (instead of Millisecond method).
|
||||||
|
elapsedDuration := time.Since(startTime)
|
||||||
|
c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...))
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Increment request counter.
|
||||||
|
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
|
||||||
|
|
||||||
|
// Start a span for this request.
|
||||||
|
ctx, span := c.cfg.Tracer.Start(ctx, ActionMapfixBypassSubmitOperation,
|
||||||
|
trace.WithAttributes(otelAttrs...),
|
||||||
|
clientSpanKind,
|
||||||
|
)
|
||||||
|
// Track stage for error reporting.
|
||||||
|
var stage string
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, stage)
|
||||||
|
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
|
stage = "BuildURL"
|
||||||
|
u := uri.Clone(c.requestURL(ctx))
|
||||||
|
var pathParts [3]string
|
||||||
|
pathParts[0] = "/mapfixes/"
|
||||||
|
{
|
||||||
|
// Encode "MapfixID" parameter.
|
||||||
|
e := uri.NewPathEncoder(uri.PathEncoderConfig{
|
||||||
|
Param: "MapfixID",
|
||||||
|
Style: uri.PathStyleSimple,
|
||||||
|
Explode: false,
|
||||||
|
})
|
||||||
|
if err := func() error {
|
||||||
|
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
|
||||||
|
}(); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode path")
|
||||||
|
}
|
||||||
|
encoded, err := e.Result()
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode path")
|
||||||
|
}
|
||||||
|
pathParts[1] = encoded
|
||||||
|
}
|
||||||
|
pathParts[2] = "/status/bypass-submit"
|
||||||
|
uri.AddPathParts(u, pathParts[:]...)
|
||||||
|
|
||||||
|
stage = "EncodeRequest"
|
||||||
|
r, err := ht.NewRequest(ctx, "POST", u)
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "create request")
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
type bitset = [1]uint8
|
||||||
|
var satisfied bitset
|
||||||
|
{
|
||||||
|
stage = "Security:CookieAuth"
|
||||||
|
switch err := c.securityCookieAuth(ctx, ActionMapfixBypassSubmitOperation, r); {
|
||||||
|
case err == nil: // if NO error
|
||||||
|
satisfied[0] |= 1 << 0
|
||||||
|
case errors.Is(err, ogenerrors.ErrSkipClientSecurity):
|
||||||
|
// Skip this security.
|
||||||
|
default:
|
||||||
|
return res, errors.Wrap(err, "security \"CookieAuth\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := func() bool {
|
||||||
|
nextRequirement:
|
||||||
|
for _, requirement := range []bitset{
|
||||||
|
{0b00000001},
|
||||||
|
} {
|
||||||
|
for i, mask := range requirement {
|
||||||
|
if satisfied[i]&mask != mask {
|
||||||
|
continue nextRequirement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}(); !ok {
|
||||||
|
return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage = "SendRequest"
|
||||||
|
resp, err := c.cfg.Client.Do(r)
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "do request")
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
stage = "DecodeResponse"
|
||||||
|
result, err := decodeActionMapfixBypassSubmitResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "decode response")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ActionMapfixReject invokes actionMapfixReject operation.
|
// ActionMapfixReject invokes actionMapfixReject operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer changes status from Submitted -> Rejected.
|
// Role Reviewer changes status from Submitted -> Rejected.
|
||||||
@@ -1753,6 +1895,130 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionSubmissionBypassSubmit invokes actionSubmissionBypassSubmit operation.
|
||||||
|
//
|
||||||
|
// Role Reviewer changes status from ChangesRequested -> Submitted.
|
||||||
|
//
|
||||||
|
// POST /submissions/{SubmissionID}/status/bypass-submit
|
||||||
|
func (c *Client) ActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) error {
|
||||||
|
_, err := c.sendActionSubmissionBypassSubmit(ctx, params)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) (res *ActionSubmissionBypassSubmitNoContent, err error) {
|
||||||
|
otelAttrs := []attribute.KeyValue{
|
||||||
|
otelogen.OperationID("actionSubmissionBypassSubmit"),
|
||||||
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
|
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/bypass-submit"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run stopwatch.
|
||||||
|
startTime := time.Now()
|
||||||
|
defer func() {
|
||||||
|
// Use floating point division here for higher precision (instead of Millisecond method).
|
||||||
|
elapsedDuration := time.Since(startTime)
|
||||||
|
c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...))
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Increment request counter.
|
||||||
|
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
|
||||||
|
|
||||||
|
// Start a span for this request.
|
||||||
|
ctx, span := c.cfg.Tracer.Start(ctx, ActionSubmissionBypassSubmitOperation,
|
||||||
|
trace.WithAttributes(otelAttrs...),
|
||||||
|
clientSpanKind,
|
||||||
|
)
|
||||||
|
// Track stage for error reporting.
|
||||||
|
var stage string
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, stage)
|
||||||
|
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
|
stage = "BuildURL"
|
||||||
|
u := uri.Clone(c.requestURL(ctx))
|
||||||
|
var pathParts [3]string
|
||||||
|
pathParts[0] = "/submissions/"
|
||||||
|
{
|
||||||
|
// Encode "SubmissionID" parameter.
|
||||||
|
e := uri.NewPathEncoder(uri.PathEncoderConfig{
|
||||||
|
Param: "SubmissionID",
|
||||||
|
Style: uri.PathStyleSimple,
|
||||||
|
Explode: false,
|
||||||
|
})
|
||||||
|
if err := func() error {
|
||||||
|
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
|
||||||
|
}(); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode path")
|
||||||
|
}
|
||||||
|
encoded, err := e.Result()
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode path")
|
||||||
|
}
|
||||||
|
pathParts[1] = encoded
|
||||||
|
}
|
||||||
|
pathParts[2] = "/status/bypass-submit"
|
||||||
|
uri.AddPathParts(u, pathParts[:]...)
|
||||||
|
|
||||||
|
stage = "EncodeRequest"
|
||||||
|
r, err := ht.NewRequest(ctx, "POST", u)
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "create request")
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
type bitset = [1]uint8
|
||||||
|
var satisfied bitset
|
||||||
|
{
|
||||||
|
stage = "Security:CookieAuth"
|
||||||
|
switch err := c.securityCookieAuth(ctx, ActionSubmissionBypassSubmitOperation, r); {
|
||||||
|
case err == nil: // if NO error
|
||||||
|
satisfied[0] |= 1 << 0
|
||||||
|
case errors.Is(err, ogenerrors.ErrSkipClientSecurity):
|
||||||
|
// Skip this security.
|
||||||
|
default:
|
||||||
|
return res, errors.Wrap(err, "security \"CookieAuth\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := func() bool {
|
||||||
|
nextRequirement:
|
||||||
|
for _, requirement := range []bitset{
|
||||||
|
{0b00000001},
|
||||||
|
} {
|
||||||
|
for i, mask := range requirement {
|
||||||
|
if satisfied[i]&mask != mask {
|
||||||
|
continue nextRequirement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}(); !ok {
|
||||||
|
return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage = "SendRequest"
|
||||||
|
resp, err := c.cfg.Client.Do(r)
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "do request")
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
stage = "DecodeResponse"
|
||||||
|
result, err := decodeActionSubmissionBypassSubmitResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "decode response")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ActionSubmissionReject invokes actionSubmissionReject operation.
|
// ActionSubmissionReject invokes actionSubmissionReject operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer changes status from Submitted -> Rejected.
|
// Role Reviewer changes status from Submitted -> Rejected.
|
||||||
@@ -3429,6 +3695,114 @@ func (c *Client) sendCreateSubmission(ctx context.Context, request *SubmissionTr
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateSubmissionAdmin invokes createSubmissionAdmin operation.
|
||||||
|
//
|
||||||
|
// Trigger the validator to create a new submission.
|
||||||
|
//
|
||||||
|
// POST /submissions-admin
|
||||||
|
func (c *Client) CreateSubmissionAdmin(ctx context.Context, request *SubmissionTriggerCreate) (*OperationID, error) {
|
||||||
|
res, err := c.sendCreateSubmissionAdmin(ctx, request)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendCreateSubmissionAdmin(ctx context.Context, request *SubmissionTriggerCreate) (res *OperationID, err error) {
|
||||||
|
otelAttrs := []attribute.KeyValue{
|
||||||
|
otelogen.OperationID("createSubmissionAdmin"),
|
||||||
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
|
semconv.HTTPRouteKey.String("/submissions-admin"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, CreateSubmissionAdminOperation,
|
||||||
|
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 [1]string
|
||||||
|
pathParts[0] = "/submissions-admin"
|
||||||
|
uri.AddPathParts(u, pathParts[:]...)
|
||||||
|
|
||||||
|
stage = "EncodeRequest"
|
||||||
|
r, err := ht.NewRequest(ctx, "POST", u)
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "create request")
|
||||||
|
}
|
||||||
|
if err := encodeCreateSubmissionAdminRequest(request, r); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode request")
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
type bitset = [1]uint8
|
||||||
|
var satisfied bitset
|
||||||
|
{
|
||||||
|
stage = "Security:CookieAuth"
|
||||||
|
switch err := c.securityCookieAuth(ctx, CreateSubmissionAdminOperation, r); {
|
||||||
|
case err == nil: // if NO error
|
||||||
|
satisfied[0] |= 1 << 0
|
||||||
|
case errors.Is(err, ogenerrors.ErrSkipClientSecurity):
|
||||||
|
// Skip this security.
|
||||||
|
default:
|
||||||
|
return res, errors.Wrap(err, "security \"CookieAuth\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := func() bool {
|
||||||
|
nextRequirement:
|
||||||
|
for _, requirement := range []bitset{
|
||||||
|
{0b00000001},
|
||||||
|
} {
|
||||||
|
for i, mask := range requirement {
|
||||||
|
if satisfied[i]&mask != mask {
|
||||||
|
continue nextRequirement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}(); !ok {
|
||||||
|
return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage = "SendRequest"
|
||||||
|
resp, err := c.cfg.Client.Do(r)
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "do request")
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
stage = "DecodeResponse"
|
||||||
|
result, err := decodeCreateSubmissionAdminResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "decode response")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// CreateSubmissionAuditComment invokes createSubmissionAuditComment operation.
|
// CreateSubmissionAuditComment invokes createSubmissionAuditComment operation.
|
||||||
//
|
//
|
||||||
// Post a comment to the audit log.
|
// Post a comment to the audit log.
|
||||||
|
|||||||
@@ -225,6 +225,201 @@ func (s *Server) handleActionMapfixAcceptedRequest(args [1]string, argsEscaped b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleActionMapfixBypassSubmitRequest handles actionMapfixBypassSubmit operation.
|
||||||
|
//
|
||||||
|
// Role Reviewer changes status from ChangesRequested -> Submitted.
|
||||||
|
//
|
||||||
|
// POST /mapfixes/{MapfixID}/status/bypass-submit
|
||||||
|
func (s *Server) handleActionMapfixBypassSubmitRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
|
||||||
|
statusWriter := &codeRecorder{ResponseWriter: w}
|
||||||
|
w = statusWriter
|
||||||
|
otelAttrs := []attribute.KeyValue{
|
||||||
|
otelogen.OperationID("actionMapfixBypassSubmit"),
|
||||||
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
|
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/bypass-submit"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a span for this request.
|
||||||
|
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixBypassSubmitOperation,
|
||||||
|
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: ActionMapfixBypassSubmitOperation,
|
||||||
|
ID: "actionMapfixBypassSubmit",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
type bitset = [1]uint8
|
||||||
|
var satisfied bitset
|
||||||
|
{
|
||||||
|
sctx, ok, err := s.securityCookieAuth(ctx, ActionMapfixBypassSubmitOperation, r)
|
||||||
|
if err != nil {
|
||||||
|
err = &ogenerrors.SecurityError{
|
||||||
|
OperationContext: opErrContext,
|
||||||
|
Security: "CookieAuth",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
|
||||||
|
defer recordError("Security:CookieAuth", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
satisfied[0] |= 1 << 0
|
||||||
|
ctx = sctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := func() bool {
|
||||||
|
nextRequirement:
|
||||||
|
for _, requirement := range []bitset{
|
||||||
|
{0b00000001},
|
||||||
|
} {
|
||||||
|
for i, mask := range requirement {
|
||||||
|
if satisfied[i]&mask != mask {
|
||||||
|
continue nextRequirement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}(); !ok {
|
||||||
|
err = &ogenerrors.SecurityError{
|
||||||
|
OperationContext: opErrContext,
|
||||||
|
Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied,
|
||||||
|
}
|
||||||
|
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
|
||||||
|
defer recordError("Security", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params, err := decodeActionMapfixBypassSubmitParams(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 *ActionMapfixBypassSubmitNoContent
|
||||||
|
if m := s.cfg.Middleware; m != nil {
|
||||||
|
mreq := middleware.Request{
|
||||||
|
Context: ctx,
|
||||||
|
OperationName: ActionMapfixBypassSubmitOperation,
|
||||||
|
OperationSummary: "Role Reviewer changes status from ChangesRequested -> Submitted",
|
||||||
|
OperationID: "actionMapfixBypassSubmit",
|
||||||
|
Body: nil,
|
||||||
|
Params: middleware.Parameters{
|
||||||
|
{
|
||||||
|
Name: "MapfixID",
|
||||||
|
In: "path",
|
||||||
|
}: params.MapfixID,
|
||||||
|
},
|
||||||
|
Raw: r,
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
Request = struct{}
|
||||||
|
Params = ActionMapfixBypassSubmitParams
|
||||||
|
Response = *ActionMapfixBypassSubmitNoContent
|
||||||
|
)
|
||||||
|
response, err = middleware.HookMiddleware[
|
||||||
|
Request,
|
||||||
|
Params,
|
||||||
|
Response,
|
||||||
|
](
|
||||||
|
m,
|
||||||
|
mreq,
|
||||||
|
unpackActionMapfixBypassSubmitParams,
|
||||||
|
func(ctx context.Context, request Request, params Params) (response Response, err error) {
|
||||||
|
err = s.h.ActionMapfixBypassSubmit(ctx, params)
|
||||||
|
return response, err
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
err = s.h.ActionMapfixBypassSubmit(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 := encodeActionMapfixBypassSubmitResponse(response, w, span); err != nil {
|
||||||
|
defer recordError("EncodeResponse", err)
|
||||||
|
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
|
||||||
|
s.cfg.ErrorHandler(ctx, w, r, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handleActionMapfixRejectRequest handles actionMapfixReject operation.
|
// handleActionMapfixRejectRequest handles actionMapfixReject operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer changes status from Submitted -> Rejected.
|
// Role Reviewer changes status from Submitted -> Rejected.
|
||||||
@@ -2176,6 +2371,201 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleActionSubmissionBypassSubmitRequest handles actionSubmissionBypassSubmit operation.
|
||||||
|
//
|
||||||
|
// Role Reviewer changes status from ChangesRequested -> Submitted.
|
||||||
|
//
|
||||||
|
// POST /submissions/{SubmissionID}/status/bypass-submit
|
||||||
|
func (s *Server) handleActionSubmissionBypassSubmitRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
|
||||||
|
statusWriter := &codeRecorder{ResponseWriter: w}
|
||||||
|
w = statusWriter
|
||||||
|
otelAttrs := []attribute.KeyValue{
|
||||||
|
otelogen.OperationID("actionSubmissionBypassSubmit"),
|
||||||
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
|
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/bypass-submit"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a span for this request.
|
||||||
|
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionBypassSubmitOperation,
|
||||||
|
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: ActionSubmissionBypassSubmitOperation,
|
||||||
|
ID: "actionSubmissionBypassSubmit",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
type bitset = [1]uint8
|
||||||
|
var satisfied bitset
|
||||||
|
{
|
||||||
|
sctx, ok, err := s.securityCookieAuth(ctx, ActionSubmissionBypassSubmitOperation, r)
|
||||||
|
if err != nil {
|
||||||
|
err = &ogenerrors.SecurityError{
|
||||||
|
OperationContext: opErrContext,
|
||||||
|
Security: "CookieAuth",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
|
||||||
|
defer recordError("Security:CookieAuth", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
satisfied[0] |= 1 << 0
|
||||||
|
ctx = sctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := func() bool {
|
||||||
|
nextRequirement:
|
||||||
|
for _, requirement := range []bitset{
|
||||||
|
{0b00000001},
|
||||||
|
} {
|
||||||
|
for i, mask := range requirement {
|
||||||
|
if satisfied[i]&mask != mask {
|
||||||
|
continue nextRequirement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}(); !ok {
|
||||||
|
err = &ogenerrors.SecurityError{
|
||||||
|
OperationContext: opErrContext,
|
||||||
|
Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied,
|
||||||
|
}
|
||||||
|
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
|
||||||
|
defer recordError("Security", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params, err := decodeActionSubmissionBypassSubmitParams(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 *ActionSubmissionBypassSubmitNoContent
|
||||||
|
if m := s.cfg.Middleware; m != nil {
|
||||||
|
mreq := middleware.Request{
|
||||||
|
Context: ctx,
|
||||||
|
OperationName: ActionSubmissionBypassSubmitOperation,
|
||||||
|
OperationSummary: "Role Reviewer changes status from ChangesRequested -> Submitted",
|
||||||
|
OperationID: "actionSubmissionBypassSubmit",
|
||||||
|
Body: nil,
|
||||||
|
Params: middleware.Parameters{
|
||||||
|
{
|
||||||
|
Name: "SubmissionID",
|
||||||
|
In: "path",
|
||||||
|
}: params.SubmissionID,
|
||||||
|
},
|
||||||
|
Raw: r,
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
Request = struct{}
|
||||||
|
Params = ActionSubmissionBypassSubmitParams
|
||||||
|
Response = *ActionSubmissionBypassSubmitNoContent
|
||||||
|
)
|
||||||
|
response, err = middleware.HookMiddleware[
|
||||||
|
Request,
|
||||||
|
Params,
|
||||||
|
Response,
|
||||||
|
](
|
||||||
|
m,
|
||||||
|
mreq,
|
||||||
|
unpackActionSubmissionBypassSubmitParams,
|
||||||
|
func(ctx context.Context, request Request, params Params) (response Response, err error) {
|
||||||
|
err = s.h.ActionSubmissionBypassSubmit(ctx, params)
|
||||||
|
return response, err
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
err = s.h.ActionSubmissionBypassSubmit(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 := encodeActionSubmissionBypassSubmitResponse(response, w, span); err != nil {
|
||||||
|
defer recordError("EncodeResponse", err)
|
||||||
|
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
|
||||||
|
s.cfg.ErrorHandler(ctx, w, r, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handleActionSubmissionRejectRequest handles actionSubmissionReject operation.
|
// handleActionSubmissionRejectRequest handles actionSubmissionReject operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer changes status from Submitted -> Rejected.
|
// Role Reviewer changes status from Submitted -> Rejected.
|
||||||
@@ -4922,6 +5312,201 @@ func (s *Server) handleCreateSubmissionRequest(args [0]string, argsEscaped bool,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleCreateSubmissionAdminRequest handles createSubmissionAdmin operation.
|
||||||
|
//
|
||||||
|
// Trigger the validator to create a new submission.
|
||||||
|
//
|
||||||
|
// POST /submissions-admin
|
||||||
|
func (s *Server) handleCreateSubmissionAdminRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
|
||||||
|
statusWriter := &codeRecorder{ResponseWriter: w}
|
||||||
|
w = statusWriter
|
||||||
|
otelAttrs := []attribute.KeyValue{
|
||||||
|
otelogen.OperationID("createSubmissionAdmin"),
|
||||||
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
|
semconv.HTTPRouteKey.String("/submissions-admin"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a span for this request.
|
||||||
|
ctx, span := s.cfg.Tracer.Start(r.Context(), CreateSubmissionAdminOperation,
|
||||||
|
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: CreateSubmissionAdminOperation,
|
||||||
|
ID: "createSubmissionAdmin",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
type bitset = [1]uint8
|
||||||
|
var satisfied bitset
|
||||||
|
{
|
||||||
|
sctx, ok, err := s.securityCookieAuth(ctx, CreateSubmissionAdminOperation, r)
|
||||||
|
if err != nil {
|
||||||
|
err = &ogenerrors.SecurityError{
|
||||||
|
OperationContext: opErrContext,
|
||||||
|
Security: "CookieAuth",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
|
||||||
|
defer recordError("Security:CookieAuth", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
satisfied[0] |= 1 << 0
|
||||||
|
ctx = sctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := func() bool {
|
||||||
|
nextRequirement:
|
||||||
|
for _, requirement := range []bitset{
|
||||||
|
{0b00000001},
|
||||||
|
} {
|
||||||
|
for i, mask := range requirement {
|
||||||
|
if satisfied[i]&mask != mask {
|
||||||
|
continue nextRequirement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}(); !ok {
|
||||||
|
err = &ogenerrors.SecurityError{
|
||||||
|
OperationContext: opErrContext,
|
||||||
|
Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied,
|
||||||
|
}
|
||||||
|
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
|
||||||
|
defer recordError("Security", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request, close, err := s.decodeCreateSubmissionAdminRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
err = &ogenerrors.DecodeRequestError{
|
||||||
|
OperationContext: opErrContext,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
defer recordError("DecodeRequest", err)
|
||||||
|
s.cfg.ErrorHandler(ctx, w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := close(); err != nil {
|
||||||
|
recordError("CloseRequest", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var response *OperationID
|
||||||
|
if m := s.cfg.Middleware; m != nil {
|
||||||
|
mreq := middleware.Request{
|
||||||
|
Context: ctx,
|
||||||
|
OperationName: CreateSubmissionAdminOperation,
|
||||||
|
OperationSummary: "Trigger the validator to create a new submission",
|
||||||
|
OperationID: "createSubmissionAdmin",
|
||||||
|
Body: request,
|
||||||
|
Params: middleware.Parameters{},
|
||||||
|
Raw: r,
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
Request = *SubmissionTriggerCreate
|
||||||
|
Params = struct{}
|
||||||
|
Response = *OperationID
|
||||||
|
)
|
||||||
|
response, err = middleware.HookMiddleware[
|
||||||
|
Request,
|
||||||
|
Params,
|
||||||
|
Response,
|
||||||
|
](
|
||||||
|
m,
|
||||||
|
mreq,
|
||||||
|
nil,
|
||||||
|
func(ctx context.Context, request Request, params Params) (response Response, err error) {
|
||||||
|
response, err = s.h.CreateSubmissionAdmin(ctx, request)
|
||||||
|
return response, err
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
response, err = s.h.CreateSubmissionAdmin(ctx, request)
|
||||||
|
}
|
||||||
|
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 := encodeCreateSubmissionAdminResponse(response, w, span); err != nil {
|
||||||
|
defer recordError("EncodeResponse", err)
|
||||||
|
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
|
||||||
|
s.cfg.ErrorHandler(ctx, w, r, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handleCreateSubmissionAuditCommentRequest handles createSubmissionAuditComment operation.
|
// handleCreateSubmissionAuditCommentRequest handles createSubmissionAuditComment operation.
|
||||||
//
|
//
|
||||||
// Post a comment to the audit log.
|
// Post a comment to the audit log.
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ func (s *AuditEvent) encodeFields(e *jx.Encoder) {
|
|||||||
e.FieldStart("User")
|
e.FieldStart("User")
|
||||||
e.Int64(s.User)
|
e.Int64(s.User)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
e.FieldStart("Username")
|
||||||
|
e.Str(s.Username)
|
||||||
|
}
|
||||||
{
|
{
|
||||||
e.FieldStart("ResourceType")
|
e.FieldStart("ResourceType")
|
||||||
e.Int32(s.ResourceType)
|
e.Int32(s.ResourceType)
|
||||||
@@ -52,14 +56,15 @@ func (s *AuditEvent) encodeFields(e *jx.Encoder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsonFieldsNameOfAuditEvent = [7]string{
|
var jsonFieldsNameOfAuditEvent = [8]string{
|
||||||
0: "ID",
|
0: "ID",
|
||||||
1: "Date",
|
1: "Date",
|
||||||
2: "User",
|
2: "User",
|
||||||
3: "ResourceType",
|
3: "Username",
|
||||||
4: "ResourceID",
|
4: "ResourceType",
|
||||||
5: "EventType",
|
5: "ResourceID",
|
||||||
6: "EventData",
|
6: "EventType",
|
||||||
|
7: "EventData",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes AuditEvent from json.
|
// Decode decodes AuditEvent from json.
|
||||||
@@ -107,8 +112,20 @@ func (s *AuditEvent) Decode(d *jx.Decoder) error {
|
|||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return errors.Wrap(err, "decode field \"User\"")
|
return errors.Wrap(err, "decode field \"User\"")
|
||||||
}
|
}
|
||||||
case "ResourceType":
|
case "Username":
|
||||||
requiredBitSet[0] |= 1 << 3
|
requiredBitSet[0] |= 1 << 3
|
||||||
|
if err := func() error {
|
||||||
|
v, err := d.Str()
|
||||||
|
s.Username = string(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return errors.Wrap(err, "decode field \"Username\"")
|
||||||
|
}
|
||||||
|
case "ResourceType":
|
||||||
|
requiredBitSet[0] |= 1 << 4
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
v, err := d.Int32()
|
v, err := d.Int32()
|
||||||
s.ResourceType = int32(v)
|
s.ResourceType = int32(v)
|
||||||
@@ -120,7 +137,7 @@ func (s *AuditEvent) Decode(d *jx.Decoder) error {
|
|||||||
return errors.Wrap(err, "decode field \"ResourceType\"")
|
return errors.Wrap(err, "decode field \"ResourceType\"")
|
||||||
}
|
}
|
||||||
case "ResourceID":
|
case "ResourceID":
|
||||||
requiredBitSet[0] |= 1 << 4
|
requiredBitSet[0] |= 1 << 5
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
v, err := d.Int64()
|
v, err := d.Int64()
|
||||||
s.ResourceID = int64(v)
|
s.ResourceID = int64(v)
|
||||||
@@ -132,7 +149,7 @@ func (s *AuditEvent) Decode(d *jx.Decoder) error {
|
|||||||
return errors.Wrap(err, "decode field \"ResourceID\"")
|
return errors.Wrap(err, "decode field \"ResourceID\"")
|
||||||
}
|
}
|
||||||
case "EventType":
|
case "EventType":
|
||||||
requiredBitSet[0] |= 1 << 5
|
requiredBitSet[0] |= 1 << 6
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
v, err := d.Int32()
|
v, err := d.Int32()
|
||||||
s.EventType = int32(v)
|
s.EventType = int32(v)
|
||||||
@@ -144,7 +161,7 @@ func (s *AuditEvent) Decode(d *jx.Decoder) error {
|
|||||||
return errors.Wrap(err, "decode field \"EventType\"")
|
return errors.Wrap(err, "decode field \"EventType\"")
|
||||||
}
|
}
|
||||||
case "EventData":
|
case "EventData":
|
||||||
requiredBitSet[0] |= 1 << 6
|
requiredBitSet[0] |= 1 << 7
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
if err := s.EventData.Decode(d); err != nil {
|
if err := s.EventData.Decode(d); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -163,7 +180,7 @@ func (s *AuditEvent) Decode(d *jx.Decoder) error {
|
|||||||
// Validate required fields.
|
// Validate required fields.
|
||||||
var failures []validate.FieldError
|
var failures []validate.FieldError
|
||||||
for i, mask := range [1]uint8{
|
for i, mask := range [1]uint8{
|
||||||
0b01111111,
|
0b11111111,
|
||||||
} {
|
} {
|
||||||
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
||||||
// Mask only required fields and check equality to mask using XOR.
|
// Mask only required fields and check equality to mask using XOR.
|
||||||
@@ -602,8 +619,8 @@ func (s *Mapfix) encodeFields(e *jx.Encoder) {
|
|||||||
e.Int32(s.StatusID)
|
e.Int32(s.StatusID)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
e.FieldStart("StatusMessage")
|
e.FieldStart("Description")
|
||||||
e.Str(s.StatusMessage)
|
e.Str(s.Description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,7 +637,7 @@ var jsonFieldsNameOfMapfix = [13]string{
|
|||||||
9: "Completed",
|
9: "Completed",
|
||||||
10: "TargetAssetID",
|
10: "TargetAssetID",
|
||||||
11: "StatusID",
|
11: "StatusID",
|
||||||
12: "StatusMessage",
|
12: "Description",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes Mapfix from json.
|
// Decode decodes Mapfix from json.
|
||||||
@@ -776,17 +793,17 @@ func (s *Mapfix) Decode(d *jx.Decoder) error {
|
|||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return errors.Wrap(err, "decode field \"StatusID\"")
|
return errors.Wrap(err, "decode field \"StatusID\"")
|
||||||
}
|
}
|
||||||
case "StatusMessage":
|
case "Description":
|
||||||
requiredBitSet[1] |= 1 << 4
|
requiredBitSet[1] |= 1 << 4
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
v, err := d.Str()
|
v, err := d.Str()
|
||||||
s.StatusMessage = string(v)
|
s.Description = string(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return errors.Wrap(err, "decode field \"StatusMessage\"")
|
return errors.Wrap(err, "decode field \"Description\"")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
@@ -862,11 +879,16 @@ func (s *MapfixTriggerCreate) encodeFields(e *jx.Encoder) {
|
|||||||
e.FieldStart("TargetAssetID")
|
e.FieldStart("TargetAssetID")
|
||||||
e.Int64(s.TargetAssetID)
|
e.Int64(s.TargetAssetID)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
e.FieldStart("Description")
|
||||||
|
e.Str(s.Description)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsonFieldsNameOfMapfixTriggerCreate = [2]string{
|
var jsonFieldsNameOfMapfixTriggerCreate = [3]string{
|
||||||
0: "AssetID",
|
0: "AssetID",
|
||||||
1: "TargetAssetID",
|
1: "TargetAssetID",
|
||||||
|
2: "Description",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes MapfixTriggerCreate from json.
|
// Decode decodes MapfixTriggerCreate from json.
|
||||||
@@ -902,6 +924,18 @@ func (s *MapfixTriggerCreate) Decode(d *jx.Decoder) error {
|
|||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return errors.Wrap(err, "decode field \"TargetAssetID\"")
|
return errors.Wrap(err, "decode field \"TargetAssetID\"")
|
||||||
}
|
}
|
||||||
|
case "Description":
|
||||||
|
requiredBitSet[0] |= 1 << 2
|
||||||
|
if err := func() error {
|
||||||
|
v, err := d.Str()
|
||||||
|
s.Description = string(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return errors.Wrap(err, "decode field \"Description\"")
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
}
|
}
|
||||||
@@ -912,7 +946,7 @@ func (s *MapfixTriggerCreate) Decode(d *jx.Decoder) error {
|
|||||||
// Validate required fields.
|
// Validate required fields.
|
||||||
var failures []validate.FieldError
|
var failures []validate.FieldError
|
||||||
for i, mask := range [1]uint8{
|
for i, mask := range [1]uint8{
|
||||||
0b00000011,
|
0b00000111,
|
||||||
} {
|
} {
|
||||||
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
||||||
// Mask only required fields and check equality to mask using XOR.
|
// Mask only required fields and check equality to mask using XOR.
|
||||||
@@ -2851,13 +2885,9 @@ func (s *Submission) encodeFields(e *jx.Encoder) {
|
|||||||
e.FieldStart("StatusID")
|
e.FieldStart("StatusID")
|
||||||
e.Int32(s.StatusID)
|
e.Int32(s.StatusID)
|
||||||
}
|
}
|
||||||
{
|
|
||||||
e.FieldStart("StatusMessage")
|
|
||||||
e.Str(s.StatusMessage)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsonFieldsNameOfSubmission = [15]string{
|
var jsonFieldsNameOfSubmission = [14]string{
|
||||||
0: "ID",
|
0: "ID",
|
||||||
1: "DisplayName",
|
1: "DisplayName",
|
||||||
2: "Creator",
|
2: "Creator",
|
||||||
@@ -2872,7 +2902,6 @@ var jsonFieldsNameOfSubmission = [15]string{
|
|||||||
11: "Completed",
|
11: "Completed",
|
||||||
12: "UploadedAssetID",
|
12: "UploadedAssetID",
|
||||||
13: "StatusID",
|
13: "StatusID",
|
||||||
14: "StatusMessage",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes Submission from json.
|
// Decode decodes Submission from json.
|
||||||
@@ -3046,18 +3075,6 @@ func (s *Submission) Decode(d *jx.Decoder) error {
|
|||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return errors.Wrap(err, "decode field \"StatusID\"")
|
return errors.Wrap(err, "decode field \"StatusID\"")
|
||||||
}
|
}
|
||||||
case "StatusMessage":
|
|
||||||
requiredBitSet[1] |= 1 << 6
|
|
||||||
if err := func() error {
|
|
||||||
v, err := d.Str()
|
|
||||||
s.StatusMessage = string(v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}(); err != nil {
|
|
||||||
return errors.Wrap(err, "decode field \"StatusMessage\"")
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
}
|
}
|
||||||
@@ -3069,7 +3086,7 @@ func (s *Submission) Decode(d *jx.Decoder) error {
|
|||||||
var failures []validate.FieldError
|
var failures []validate.FieldError
|
||||||
for i, mask := range [2]uint8{
|
for i, mask := range [2]uint8{
|
||||||
0b11111111,
|
0b11111111,
|
||||||
0b01101001,
|
0b00101001,
|
||||||
} {
|
} {
|
||||||
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
||||||
// Mask only required fields and check equality to mask using XOR.
|
// Mask only required fields and check equality to mask using XOR.
|
||||||
@@ -3128,10 +3145,25 @@ func (s *SubmissionTriggerCreate) encodeFields(e *jx.Encoder) {
|
|||||||
e.FieldStart("AssetID")
|
e.FieldStart("AssetID")
|
||||||
e.Int64(s.AssetID)
|
e.Int64(s.AssetID)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
e.FieldStart("DisplayName")
|
||||||
|
e.Str(s.DisplayName)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
e.FieldStart("Creator")
|
||||||
|
e.Str(s.Creator)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
e.FieldStart("GameID")
|
||||||
|
e.Int32(s.GameID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsonFieldsNameOfSubmissionTriggerCreate = [1]string{
|
var jsonFieldsNameOfSubmissionTriggerCreate = [4]string{
|
||||||
0: "AssetID",
|
0: "AssetID",
|
||||||
|
1: "DisplayName",
|
||||||
|
2: "Creator",
|
||||||
|
3: "GameID",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes SubmissionTriggerCreate from json.
|
// Decode decodes SubmissionTriggerCreate from json.
|
||||||
@@ -3155,6 +3187,42 @@ func (s *SubmissionTriggerCreate) Decode(d *jx.Decoder) error {
|
|||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return errors.Wrap(err, "decode field \"AssetID\"")
|
return errors.Wrap(err, "decode field \"AssetID\"")
|
||||||
}
|
}
|
||||||
|
case "DisplayName":
|
||||||
|
requiredBitSet[0] |= 1 << 1
|
||||||
|
if err := func() error {
|
||||||
|
v, err := d.Str()
|
||||||
|
s.DisplayName = string(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return errors.Wrap(err, "decode field \"DisplayName\"")
|
||||||
|
}
|
||||||
|
case "Creator":
|
||||||
|
requiredBitSet[0] |= 1 << 2
|
||||||
|
if err := func() error {
|
||||||
|
v, err := d.Str()
|
||||||
|
s.Creator = string(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return errors.Wrap(err, "decode field \"Creator\"")
|
||||||
|
}
|
||||||
|
case "GameID":
|
||||||
|
requiredBitSet[0] |= 1 << 3
|
||||||
|
if err := func() error {
|
||||||
|
v, err := d.Int32()
|
||||||
|
s.GameID = int32(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return errors.Wrap(err, "decode field \"GameID\"")
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
}
|
}
|
||||||
@@ -3165,7 +3233,7 @@ func (s *SubmissionTriggerCreate) Decode(d *jx.Decoder) error {
|
|||||||
// Validate required fields.
|
// Validate required fields.
|
||||||
var failures []validate.FieldError
|
var failures []validate.FieldError
|
||||||
for i, mask := range [1]uint8{
|
for i, mask := range [1]uint8{
|
||||||
0b00000001,
|
0b00001111,
|
||||||
} {
|
} {
|
||||||
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
||||||
// Mask only required fields and check equality to mask using XOR.
|
// Mask only required fields and check equality to mask using XOR.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ type OperationName = string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
|
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
|
||||||
|
ActionMapfixBypassSubmitOperation OperationName = "ActionMapfixBypassSubmit"
|
||||||
ActionMapfixRejectOperation OperationName = "ActionMapfixReject"
|
ActionMapfixRejectOperation OperationName = "ActionMapfixReject"
|
||||||
ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges"
|
ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges"
|
||||||
ActionMapfixResetSubmittingOperation OperationName = "ActionMapfixResetSubmitting"
|
ActionMapfixResetSubmittingOperation OperationName = "ActionMapfixResetSubmitting"
|
||||||
@@ -17,6 +18,7 @@ const (
|
|||||||
ActionMapfixTriggerValidateOperation OperationName = "ActionMapfixTriggerValidate"
|
ActionMapfixTriggerValidateOperation OperationName = "ActionMapfixTriggerValidate"
|
||||||
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
|
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
|
||||||
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
|
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
|
||||||
|
ActionSubmissionBypassSubmitOperation OperationName = "ActionSubmissionBypassSubmit"
|
||||||
ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject"
|
ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject"
|
||||||
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
|
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
|
||||||
ActionSubmissionResetSubmittingOperation OperationName = "ActionSubmissionResetSubmitting"
|
ActionSubmissionResetSubmittingOperation OperationName = "ActionSubmissionResetSubmitting"
|
||||||
@@ -31,6 +33,7 @@ const (
|
|||||||
CreateScriptOperation OperationName = "CreateScript"
|
CreateScriptOperation OperationName = "CreateScript"
|
||||||
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
|
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
|
||||||
CreateSubmissionOperation OperationName = "CreateSubmission"
|
CreateSubmissionOperation OperationName = "CreateSubmission"
|
||||||
|
CreateSubmissionAdminOperation OperationName = "CreateSubmissionAdmin"
|
||||||
CreateSubmissionAuditCommentOperation OperationName = "CreateSubmissionAuditComment"
|
CreateSubmissionAuditCommentOperation OperationName = "CreateSubmissionAuditComment"
|
||||||
DeleteScriptOperation OperationName = "DeleteScript"
|
DeleteScriptOperation OperationName = "DeleteScript"
|
||||||
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"
|
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"
|
||||||
|
|||||||
@@ -98,6 +98,89 @@ func decodeActionMapfixAcceptedParams(args [1]string, argsEscaped bool, r *http.
|
|||||||
return params, nil
|
return params, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionMapfixBypassSubmitParams is parameters of actionMapfixBypassSubmit operation.
|
||||||
|
type ActionMapfixBypassSubmitParams struct {
|
||||||
|
// The unique identifier for a mapfix.
|
||||||
|
MapfixID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func unpackActionMapfixBypassSubmitParams(packed middleware.Parameters) (params ActionMapfixBypassSubmitParams) {
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "MapfixID",
|
||||||
|
In: "path",
|
||||||
|
}
|
||||||
|
params.MapfixID = packed[key].(int64)
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeActionMapfixBypassSubmitParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixBypassSubmitParams, _ error) {
|
||||||
|
// Decode path: MapfixID.
|
||||||
|
if err := func() error {
|
||||||
|
param := args[0]
|
||||||
|
if argsEscaped {
|
||||||
|
unescaped, err := url.PathUnescape(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unescape path")
|
||||||
|
}
|
||||||
|
param = unescaped
|
||||||
|
}
|
||||||
|
if len(param) > 0 {
|
||||||
|
d := uri.NewPathDecoder(uri.PathDecoderConfig{
|
||||||
|
Param: "MapfixID",
|
||||||
|
Value: param,
|
||||||
|
Style: uri.PathStyleSimple,
|
||||||
|
Explode: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := func() error {
|
||||||
|
val, err := d.DecodeValue()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := conv.ToInt64(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
params.MapfixID = c
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := func() error {
|
||||||
|
if err := (validate.Int{
|
||||||
|
MinSet: true,
|
||||||
|
Min: 0,
|
||||||
|
MaxSet: false,
|
||||||
|
Max: 0,
|
||||||
|
MinExclusive: false,
|
||||||
|
MaxExclusive: false,
|
||||||
|
MultipleOfSet: false,
|
||||||
|
MultipleOf: 0,
|
||||||
|
}).Validate(int64(params.MapfixID)); err != nil {
|
||||||
|
return errors.Wrap(err, "int")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return validate.ErrFieldRequired
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return params, &ogenerrors.DecodeParamError{
|
||||||
|
Name: "MapfixID",
|
||||||
|
In: "path",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ActionMapfixRejectParams is parameters of actionMapfixReject operation.
|
// ActionMapfixRejectParams is parameters of actionMapfixReject operation.
|
||||||
type ActionMapfixRejectParams struct {
|
type ActionMapfixRejectParams struct {
|
||||||
// The unique identifier for a mapfix.
|
// The unique identifier for a mapfix.
|
||||||
@@ -928,6 +1011,89 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
|
|||||||
return params, nil
|
return params, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionSubmissionBypassSubmitParams is parameters of actionSubmissionBypassSubmit operation.
|
||||||
|
type ActionSubmissionBypassSubmitParams struct {
|
||||||
|
// The unique identifier for a submission.
|
||||||
|
SubmissionID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func unpackActionSubmissionBypassSubmitParams(packed middleware.Parameters) (params ActionSubmissionBypassSubmitParams) {
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "SubmissionID",
|
||||||
|
In: "path",
|
||||||
|
}
|
||||||
|
params.SubmissionID = packed[key].(int64)
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeActionSubmissionBypassSubmitParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionBypassSubmitParams, _ error) {
|
||||||
|
// Decode path: SubmissionID.
|
||||||
|
if err := func() error {
|
||||||
|
param := args[0]
|
||||||
|
if argsEscaped {
|
||||||
|
unescaped, err := url.PathUnescape(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unescape path")
|
||||||
|
}
|
||||||
|
param = unescaped
|
||||||
|
}
|
||||||
|
if len(param) > 0 {
|
||||||
|
d := uri.NewPathDecoder(uri.PathDecoderConfig{
|
||||||
|
Param: "SubmissionID",
|
||||||
|
Value: param,
|
||||||
|
Style: uri.PathStyleSimple,
|
||||||
|
Explode: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := func() error {
|
||||||
|
val, err := d.DecodeValue()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := conv.ToInt64(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
params.SubmissionID = c
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := func() error {
|
||||||
|
if err := (validate.Int{
|
||||||
|
MinSet: true,
|
||||||
|
Min: 0,
|
||||||
|
MaxSet: false,
|
||||||
|
Max: 0,
|
||||||
|
MinExclusive: false,
|
||||||
|
MaxExclusive: false,
|
||||||
|
MultipleOfSet: false,
|
||||||
|
MultipleOf: 0,
|
||||||
|
}).Validate(int64(params.SubmissionID)); err != nil {
|
||||||
|
return errors.Wrap(err, "int")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return validate.ErrFieldRequired
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return params, &ogenerrors.DecodeParamError{
|
||||||
|
Name: "SubmissionID",
|
||||||
|
In: "path",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ActionSubmissionRejectParams is parameters of actionSubmissionReject operation.
|
// ActionSubmissionRejectParams is parameters of actionSubmissionReject operation.
|
||||||
type ActionSubmissionRejectParams struct {
|
type ActionSubmissionRejectParams struct {
|
||||||
// The unique identifier for a submission.
|
// The unique identifier for a submission.
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
|
|
||||||
"github.com/go-faster/errors"
|
"github.com/go-faster/errors"
|
||||||
"github.com/go-faster/jx"
|
"github.com/go-faster/jx"
|
||||||
"go.uber.org/multierr"
|
|
||||||
|
|
||||||
"github.com/ogen-go/ogen/ogenerrors"
|
"github.com/ogen-go/ogen/ogenerrors"
|
||||||
"github.com/ogen-go/ogen/validate"
|
"github.com/ogen-go/ogen/validate"
|
||||||
@@ -27,13 +26,13 @@ func (s *Server) decodeCreateMapfixRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
@@ -98,13 +97,13 @@ func (s *Server) decodeCreateMapfixAuditCommentRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
@@ -132,13 +131,13 @@ func (s *Server) decodeCreateScriptRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
@@ -203,13 +202,13 @@ func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
@@ -274,13 +273,84 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
|
if err != nil {
|
||||||
|
return req, close, errors.Wrap(err, "parse media type")
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case ct == "application/json":
|
||||||
|
if r.ContentLength == 0 {
|
||||||
|
return req, close, validate.ErrBodyRequired
|
||||||
|
}
|
||||||
|
buf, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return req, close, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return req, close, validate.ErrBodyRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
d := jx.DecodeBytes(buf)
|
||||||
|
|
||||||
|
var request SubmissionTriggerCreate
|
||||||
|
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) decodeCreateSubmissionAdminRequest(r *http.Request) (
|
||||||
|
req *SubmissionTriggerCreate,
|
||||||
|
close func() error,
|
||||||
|
rerr error,
|
||||||
|
) {
|
||||||
|
var closers []func() error
|
||||||
|
close = func() error {
|
||||||
|
var merr error
|
||||||
|
// Close in reverse order, to match defer behavior.
|
||||||
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
|
c := closers[i]
|
||||||
|
merr = errors.Join(merr, c())
|
||||||
|
}
|
||||||
|
return merr
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if rerr != nil {
|
||||||
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
@@ -345,13 +415,13 @@ func (s *Server) decodeCreateSubmissionAuditCommentRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
@@ -379,13 +449,13 @@ func (s *Server) decodeReleaseSubmissionsRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
@@ -483,13 +553,13 @@ func (s *Server) decodeUpdateScriptRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
@@ -554,13 +624,13 @@ func (s *Server) decodeUpdateScriptPolicyRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
|
|||||||
@@ -77,6 +77,20 @@ func encodeCreateSubmissionRequest(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeCreateSubmissionAdminRequest(
|
||||||
|
req *SubmissionTriggerCreate,
|
||||||
|
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 encodeCreateSubmissionAuditCommentRequest(
|
func encodeCreateSubmissionAuditCommentRequest(
|
||||||
req CreateSubmissionAuditCommentReq,
|
req CreateSubmissionAuditCommentReq,
|
||||||
r *http.Request,
|
r *http.Request,
|
||||||
|
|||||||
@@ -75,6 +75,66 @@ func decodeActionMapfixAcceptedResponse(resp *http.Response) (res *ActionMapfixA
|
|||||||
return res, errors.Wrap(defRes, "error")
|
return res, errors.Wrap(defRes, "error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeActionMapfixBypassSubmitResponse(resp *http.Response) (res *ActionMapfixBypassSubmitNoContent, _ error) {
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case 204:
|
||||||
|
// Code 204.
|
||||||
|
return &ActionMapfixBypassSubmitNoContent{}, nil
|
||||||
|
}
|
||||||
|
// Convenient error response.
|
||||||
|
defRes, err := func() (res *ErrorStatusCode, err error) {
|
||||||
|
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "parse media type")
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case ct == "application/json":
|
||||||
|
buf, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
d := jx.DecodeBytes(buf)
|
||||||
|
|
||||||
|
var response Error
|
||||||
|
if err := func() error {
|
||||||
|
if err := response.Decode(d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Skip(); err != io.EOF {
|
||||||
|
return errors.New("unexpected trailing data")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
err = &ogenerrors.DecodeBodyError{
|
||||||
|
ContentType: ct,
|
||||||
|
Body: buf,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
// Validate response.
|
||||||
|
if err := func() error {
|
||||||
|
if err := response.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return res, errors.Wrap(err, "validate")
|
||||||
|
}
|
||||||
|
return &ErrorStatusCode{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Response: response,
|
||||||
|
}, nil
|
||||||
|
default:
|
||||||
|
return res, validate.InvalidContentType(ct)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return res, errors.Wrap(defRes, "error")
|
||||||
|
}
|
||||||
|
|
||||||
func decodeActionMapfixRejectResponse(resp *http.Response) (res *ActionMapfixRejectNoContent, _ error) {
|
func decodeActionMapfixRejectResponse(resp *http.Response) (res *ActionMapfixRejectNoContent, _ error) {
|
||||||
switch resp.StatusCode {
|
switch resp.StatusCode {
|
||||||
case 204:
|
case 204:
|
||||||
@@ -675,6 +735,66 @@ func decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSub
|
|||||||
return res, errors.Wrap(defRes, "error")
|
return res, errors.Wrap(defRes, "error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeActionSubmissionBypassSubmitResponse(resp *http.Response) (res *ActionSubmissionBypassSubmitNoContent, _ error) {
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case 204:
|
||||||
|
// Code 204.
|
||||||
|
return &ActionSubmissionBypassSubmitNoContent{}, nil
|
||||||
|
}
|
||||||
|
// Convenient error response.
|
||||||
|
defRes, err := func() (res *ErrorStatusCode, err error) {
|
||||||
|
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "parse media type")
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case ct == "application/json":
|
||||||
|
buf, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
d := jx.DecodeBytes(buf)
|
||||||
|
|
||||||
|
var response Error
|
||||||
|
if err := func() error {
|
||||||
|
if err := response.Decode(d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Skip(); err != io.EOF {
|
||||||
|
return errors.New("unexpected trailing data")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
err = &ogenerrors.DecodeBodyError{
|
||||||
|
ContentType: ct,
|
||||||
|
Body: buf,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
// Validate response.
|
||||||
|
if err := func() error {
|
||||||
|
if err := response.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return res, errors.Wrap(err, "validate")
|
||||||
|
}
|
||||||
|
return &ErrorStatusCode{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Response: response,
|
||||||
|
}, nil
|
||||||
|
default:
|
||||||
|
return res, validate.InvalidContentType(ct)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return res, errors.Wrap(defRes, "error")
|
||||||
|
}
|
||||||
|
|
||||||
func decodeActionSubmissionRejectResponse(resp *http.Response) (res *ActionSubmissionRejectNoContent, _ error) {
|
func decodeActionSubmissionRejectResponse(resp *http.Response) (res *ActionSubmissionRejectNoContent, _ error) {
|
||||||
switch resp.StatusCode {
|
switch resp.StatusCode {
|
||||||
case 204:
|
case 204:
|
||||||
@@ -1679,6 +1799,107 @@ func decodeCreateSubmissionResponse(resp *http.Response) (res *OperationID, _ er
|
|||||||
return res, errors.Wrap(defRes, "error")
|
return res, errors.Wrap(defRes, "error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeCreateSubmissionAdminResponse(resp *http.Response) (res *OperationID, _ error) {
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case 201:
|
||||||
|
// Code 201.
|
||||||
|
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 OperationID
|
||||||
|
if err := func() error {
|
||||||
|
if err := response.Decode(d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Skip(); err != io.EOF {
|
||||||
|
return errors.New("unexpected trailing data")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
err = &ogenerrors.DecodeBodyError{
|
||||||
|
ContentType: ct,
|
||||||
|
Body: buf,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
// Validate response.
|
||||||
|
if err := func() error {
|
||||||
|
if err := response.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return res, errors.Wrap(err, "validate")
|
||||||
|
}
|
||||||
|
return &response, nil
|
||||||
|
default:
|
||||||
|
return res, validate.InvalidContentType(ct)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Convenient error response.
|
||||||
|
defRes, err := func() (res *ErrorStatusCode, err error) {
|
||||||
|
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "parse media type")
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case ct == "application/json":
|
||||||
|
buf, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
d := jx.DecodeBytes(buf)
|
||||||
|
|
||||||
|
var response Error
|
||||||
|
if err := func() error {
|
||||||
|
if err := response.Decode(d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Skip(); err != io.EOF {
|
||||||
|
return errors.New("unexpected trailing data")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
err = &ogenerrors.DecodeBodyError{
|
||||||
|
ContentType: ct,
|
||||||
|
Body: buf,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
// Validate response.
|
||||||
|
if err := func() error {
|
||||||
|
if err := response.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return res, errors.Wrap(err, "validate")
|
||||||
|
}
|
||||||
|
return &ErrorStatusCode{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Response: response,
|
||||||
|
}, nil
|
||||||
|
default:
|
||||||
|
return res, validate.InvalidContentType(ct)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return res, errors.Wrap(defRes, "error")
|
||||||
|
}
|
||||||
|
|
||||||
func decodeCreateSubmissionAuditCommentResponse(resp *http.Response) (res *CreateSubmissionAuditCommentNoContent, _ error) {
|
func decodeCreateSubmissionAuditCommentResponse(resp *http.Response) (res *CreateSubmissionAuditCommentNoContent, _ error) {
|
||||||
switch resp.StatusCode {
|
switch resp.StatusCode {
|
||||||
case 204:
|
case 204:
|
||||||
@@ -2511,6 +2732,23 @@ func decodeListMapfixAuditEventsResponse(resp *http.Response) (res []AuditEvent,
|
|||||||
if response == nil {
|
if response == nil {
|
||||||
return errors.New("nil is invalid value")
|
return errors.New("nil is invalid value")
|
||||||
}
|
}
|
||||||
|
var failures []validate.FieldError
|
||||||
|
for i, elem := range response {
|
||||||
|
if err := func() error {
|
||||||
|
if err := elem.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
failures = append(failures, validate.FieldError{
|
||||||
|
Name: fmt.Sprintf("[%d]", i),
|
||||||
|
Error: err,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(failures) > 0 {
|
||||||
|
return &validate.Error{Fields: failures}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return res, errors.Wrap(err, "validate")
|
return res, errors.Wrap(err, "validate")
|
||||||
@@ -3099,6 +3337,23 @@ func decodeListSubmissionAuditEventsResponse(resp *http.Response) (res []AuditEv
|
|||||||
if response == nil {
|
if response == nil {
|
||||||
return errors.New("nil is invalid value")
|
return errors.New("nil is invalid value")
|
||||||
}
|
}
|
||||||
|
var failures []validate.FieldError
|
||||||
|
for i, elem := range response {
|
||||||
|
if err := func() error {
|
||||||
|
if err := elem.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
failures = append(failures, validate.FieldError{
|
||||||
|
Name: fmt.Sprintf("[%d]", i),
|
||||||
|
Error: err,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(failures) > 0 {
|
||||||
|
return &validate.Error{Fields: failures}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return res, errors.Wrap(err, "validate")
|
return res, errors.Wrap(err, "validate")
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ func encodeActionMapfixAcceptedResponse(response *ActionMapfixAcceptedNoContent,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeActionMapfixBypassSubmitResponse(response *ActionMapfixBypassSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
|
||||||
|
w.WriteHeader(204)
|
||||||
|
span.SetStatus(codes.Ok, http.StatusText(204))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func encodeActionMapfixRejectResponse(response *ActionMapfixRejectNoContent, w http.ResponseWriter, span trace.Span) error {
|
func encodeActionMapfixRejectResponse(response *ActionMapfixRejectNoContent, w http.ResponseWriter, span trace.Span) error {
|
||||||
w.WriteHeader(204)
|
w.WriteHeader(204)
|
||||||
span.SetStatus(codes.Ok, http.StatusText(204))
|
span.SetStatus(codes.Ok, http.StatusText(204))
|
||||||
@@ -90,6 +97,13 @@ func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeActionSubmissionBypassSubmitResponse(response *ActionSubmissionBypassSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
|
||||||
|
w.WriteHeader(204)
|
||||||
|
span.SetStatus(codes.Ok, http.StatusText(204))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func encodeActionSubmissionRejectResponse(response *ActionSubmissionRejectNoContent, w http.ResponseWriter, span trace.Span) error {
|
func encodeActionSubmissionRejectResponse(response *ActionSubmissionRejectNoContent, w http.ResponseWriter, span trace.Span) error {
|
||||||
w.WriteHeader(204)
|
w.WriteHeader(204)
|
||||||
span.SetStatus(codes.Ok, http.StatusText(204))
|
span.SetStatus(codes.Ok, http.StatusText(204))
|
||||||
@@ -216,6 +230,20 @@ func encodeCreateSubmissionResponse(response *OperationID, w http.ResponseWriter
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeCreateSubmissionAdminResponse(response *OperationID, 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 encodeCreateSubmissionAuditCommentResponse(response *CreateSubmissionAuditCommentNoContent, w http.ResponseWriter, span trace.Span) error {
|
func encodeCreateSubmissionAuditCommentResponse(response *CreateSubmissionAuditCommentNoContent, w http.ResponseWriter, span trace.Span) error {
|
||||||
w.WriteHeader(204)
|
w.WriteHeader(204)
|
||||||
span.SetStatus(codes.Ok, http.StatusText(204))
|
span.SetStatus(codes.Ok, http.StatusText(204))
|
||||||
|
|||||||
@@ -250,6 +250,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
switch elem[0] {
|
switch elem[0] {
|
||||||
|
case 'b': // Prefix: "bypass-submit"
|
||||||
|
|
||||||
|
if l := len("bypass-submit"); len(elem) >= l && elem[0:l] == "bypass-submit" {
|
||||||
|
elem = elem[l:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch r.Method {
|
||||||
|
case "POST":
|
||||||
|
s.handleActionMapfixBypassSubmitRequest([1]string{
|
||||||
|
args[0],
|
||||||
|
}, elemIsEscaped, w, r)
|
||||||
|
default:
|
||||||
|
s.notAllowed(w, r, "POST")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
case 'r': // Prefix: "re"
|
case 'r': // Prefix: "re"
|
||||||
|
|
||||||
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
|
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
|
||||||
@@ -870,6 +892,26 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch elem[0] {
|
switch elem[0] {
|
||||||
|
case '-': // Prefix: "-admin"
|
||||||
|
|
||||||
|
if l := len("-admin"); len(elem) >= l && elem[0:l] == "-admin" {
|
||||||
|
elem = elem[l:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch r.Method {
|
||||||
|
case "POST":
|
||||||
|
s.handleCreateSubmissionAdminRequest([0]string{}, elemIsEscaped, w, r)
|
||||||
|
default:
|
||||||
|
s.notAllowed(w, r, "POST")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
case '/': // Prefix: "/"
|
case '/': // Prefix: "/"
|
||||||
|
|
||||||
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
|
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
|
||||||
@@ -1026,6 +1068,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
switch elem[0] {
|
switch elem[0] {
|
||||||
|
case 'b': // Prefix: "bypass-submit"
|
||||||
|
|
||||||
|
if l := len("bypass-submit"); len(elem) >= l && elem[0:l] == "bypass-submit" {
|
||||||
|
elem = elem[l:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch r.Method {
|
||||||
|
case "POST":
|
||||||
|
s.handleActionSubmissionBypassSubmitRequest([1]string{
|
||||||
|
args[0],
|
||||||
|
}, elemIsEscaped, w, r)
|
||||||
|
default:
|
||||||
|
s.notAllowed(w, r, "POST")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
case 'r': // Prefix: "re"
|
case 'r': // Prefix: "re"
|
||||||
|
|
||||||
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
|
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
|
||||||
@@ -1601,6 +1665,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
switch elem[0] {
|
switch elem[0] {
|
||||||
|
case 'b': // Prefix: "bypass-submit"
|
||||||
|
|
||||||
|
if l := len("bypass-submit"); len(elem) >= l && elem[0:l] == "bypass-submit" {
|
||||||
|
elem = elem[l:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch method {
|
||||||
|
case "POST":
|
||||||
|
r.name = ActionMapfixBypassSubmitOperation
|
||||||
|
r.summary = "Role Reviewer changes status from ChangesRequested -> Submitted"
|
||||||
|
r.operationID = "actionMapfixBypassSubmit"
|
||||||
|
r.pathPattern = "/mapfixes/{MapfixID}/status/bypass-submit"
|
||||||
|
r.args = args
|
||||||
|
r.count = 1
|
||||||
|
return r, true
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case 'r': // Prefix: "re"
|
case 'r': // Prefix: "re"
|
||||||
|
|
||||||
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
|
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
|
||||||
@@ -2315,6 +2403,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch elem[0] {
|
switch elem[0] {
|
||||||
|
case '-': // Prefix: "-admin"
|
||||||
|
|
||||||
|
if l := len("-admin"); len(elem) >= l && elem[0:l] == "-admin" {
|
||||||
|
elem = elem[l:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch method {
|
||||||
|
case "POST":
|
||||||
|
r.name = CreateSubmissionAdminOperation
|
||||||
|
r.summary = "Trigger the validator to create a new submission"
|
||||||
|
r.operationID = "createSubmissionAdmin"
|
||||||
|
r.pathPattern = "/submissions-admin"
|
||||||
|
r.args = args
|
||||||
|
r.count = 0
|
||||||
|
return r, true
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case '/': // Prefix: "/"
|
case '/': // Prefix: "/"
|
||||||
|
|
||||||
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
|
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
|
||||||
@@ -2481,6 +2593,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
switch elem[0] {
|
switch elem[0] {
|
||||||
|
case 'b': // Prefix: "bypass-submit"
|
||||||
|
|
||||||
|
if l := len("bypass-submit"); len(elem) >= l && elem[0:l] == "bypass-submit" {
|
||||||
|
elem = elem[l:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch method {
|
||||||
|
case "POST":
|
||||||
|
r.name = ActionSubmissionBypassSubmitOperation
|
||||||
|
r.summary = "Role Reviewer changes status from ChangesRequested -> Submitted"
|
||||||
|
r.operationID = "actionSubmissionBypassSubmit"
|
||||||
|
r.pathPattern = "/submissions/{SubmissionID}/status/bypass-submit"
|
||||||
|
r.args = args
|
||||||
|
r.count = 1
|
||||||
|
return r, true
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case 'r': // Prefix: "re"
|
case 'r': // Prefix: "re"
|
||||||
|
|
||||||
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
|
if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ func (s *ErrorStatusCode) Error() string {
|
|||||||
// ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation.
|
// ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation.
|
||||||
type ActionMapfixAcceptedNoContent struct{}
|
type ActionMapfixAcceptedNoContent struct{}
|
||||||
|
|
||||||
|
// ActionMapfixBypassSubmitNoContent is response for ActionMapfixBypassSubmit operation.
|
||||||
|
type ActionMapfixBypassSubmitNoContent struct{}
|
||||||
|
|
||||||
// ActionMapfixRejectNoContent is response for ActionMapfixReject operation.
|
// ActionMapfixRejectNoContent is response for ActionMapfixReject operation.
|
||||||
type ActionMapfixRejectNoContent struct{}
|
type ActionMapfixRejectNoContent struct{}
|
||||||
|
|
||||||
@@ -47,6 +50,9 @@ type ActionMapfixValidatedNoContent struct{}
|
|||||||
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
|
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
|
||||||
type ActionSubmissionAcceptedNoContent struct{}
|
type ActionSubmissionAcceptedNoContent struct{}
|
||||||
|
|
||||||
|
// ActionSubmissionBypassSubmitNoContent is response for ActionSubmissionBypassSubmit operation.
|
||||||
|
type ActionSubmissionBypassSubmitNoContent struct{}
|
||||||
|
|
||||||
// ActionSubmissionRejectNoContent is response for ActionSubmissionReject operation.
|
// ActionSubmissionRejectNoContent is response for ActionSubmissionReject operation.
|
||||||
type ActionSubmissionRejectNoContent struct{}
|
type ActionSubmissionRejectNoContent struct{}
|
||||||
|
|
||||||
@@ -76,9 +82,10 @@ type ActionSubmissionValidatedNoContent struct{}
|
|||||||
|
|
||||||
// Ref: #/components/schemas/AuditEvent
|
// Ref: #/components/schemas/AuditEvent
|
||||||
type AuditEvent struct {
|
type AuditEvent struct {
|
||||||
ID int64 `json:"ID"`
|
ID int64 `json:"ID"`
|
||||||
Date int64 `json:"Date"`
|
Date int64 `json:"Date"`
|
||||||
User int64 `json:"User"`
|
User int64 `json:"User"`
|
||||||
|
Username string `json:"Username"`
|
||||||
// Is this a submission or is it a mapfix.
|
// Is this a submission or is it a mapfix.
|
||||||
ResourceType int32 `json:"ResourceType"`
|
ResourceType int32 `json:"ResourceType"`
|
||||||
ResourceID int64 `json:"ResourceID"`
|
ResourceID int64 `json:"ResourceID"`
|
||||||
@@ -102,6 +109,11 @@ func (s *AuditEvent) GetUser() int64 {
|
|||||||
return s.User
|
return s.User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUsername returns the value of Username.
|
||||||
|
func (s *AuditEvent) GetUsername() string {
|
||||||
|
return s.Username
|
||||||
|
}
|
||||||
|
|
||||||
// GetResourceType returns the value of ResourceType.
|
// GetResourceType returns the value of ResourceType.
|
||||||
func (s *AuditEvent) GetResourceType() int32 {
|
func (s *AuditEvent) GetResourceType() int32 {
|
||||||
return s.ResourceType
|
return s.ResourceType
|
||||||
@@ -137,6 +149,11 @@ func (s *AuditEvent) SetUser(val int64) {
|
|||||||
s.User = val
|
s.User = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetUsername sets the value of Username.
|
||||||
|
func (s *AuditEvent) SetUsername(val string) {
|
||||||
|
s.Username = val
|
||||||
|
}
|
||||||
|
|
||||||
// SetResourceType sets the value of ResourceType.
|
// SetResourceType sets the value of ResourceType.
|
||||||
func (s *AuditEvent) SetResourceType(val int32) {
|
func (s *AuditEvent) SetResourceType(val int32) {
|
||||||
s.ResourceType = val
|
s.ResourceType = val
|
||||||
@@ -171,6 +188,7 @@ func (s *AuditEventEventData) init() AuditEventEventData {
|
|||||||
|
|
||||||
type CookieAuth struct {
|
type CookieAuth struct {
|
||||||
APIKey string
|
APIKey string
|
||||||
|
Roles []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAPIKey returns the value of APIKey.
|
// GetAPIKey returns the value of APIKey.
|
||||||
@@ -178,11 +196,21 @@ func (s *CookieAuth) GetAPIKey() string {
|
|||||||
return s.APIKey
|
return s.APIKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRoles returns the value of Roles.
|
||||||
|
func (s *CookieAuth) GetRoles() []string {
|
||||||
|
return s.Roles
|
||||||
|
}
|
||||||
|
|
||||||
// SetAPIKey sets the value of APIKey.
|
// SetAPIKey sets the value of APIKey.
|
||||||
func (s *CookieAuth) SetAPIKey(val string) {
|
func (s *CookieAuth) SetAPIKey(val string) {
|
||||||
s.APIKey = val
|
s.APIKey = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetRoles sets the value of Roles.
|
||||||
|
func (s *CookieAuth) SetRoles(val []string) {
|
||||||
|
s.Roles = val
|
||||||
|
}
|
||||||
|
|
||||||
// CreateMapfixAuditCommentNoContent is response for CreateMapfixAuditComment operation.
|
// CreateMapfixAuditCommentNoContent is response for CreateMapfixAuditComment operation.
|
||||||
type CreateMapfixAuditCommentNoContent struct{}
|
type CreateMapfixAuditCommentNoContent struct{}
|
||||||
|
|
||||||
@@ -349,7 +377,7 @@ type Mapfix struct {
|
|||||||
Completed bool `json:"Completed"`
|
Completed bool `json:"Completed"`
|
||||||
TargetAssetID int64 `json:"TargetAssetID"`
|
TargetAssetID int64 `json:"TargetAssetID"`
|
||||||
StatusID int32 `json:"StatusID"`
|
StatusID int32 `json:"StatusID"`
|
||||||
StatusMessage string `json:"StatusMessage"`
|
Description string `json:"Description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the value of ID.
|
// GetID returns the value of ID.
|
||||||
@@ -412,9 +440,9 @@ func (s *Mapfix) GetStatusID() int32 {
|
|||||||
return s.StatusID
|
return s.StatusID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStatusMessage returns the value of StatusMessage.
|
// GetDescription returns the value of Description.
|
||||||
func (s *Mapfix) GetStatusMessage() string {
|
func (s *Mapfix) GetDescription() string {
|
||||||
return s.StatusMessage
|
return s.Description
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetID sets the value of ID.
|
// SetID sets the value of ID.
|
||||||
@@ -477,15 +505,16 @@ func (s *Mapfix) SetStatusID(val int32) {
|
|||||||
s.StatusID = val
|
s.StatusID = val
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetStatusMessage sets the value of StatusMessage.
|
// SetDescription sets the value of Description.
|
||||||
func (s *Mapfix) SetStatusMessage(val string) {
|
func (s *Mapfix) SetDescription(val string) {
|
||||||
s.StatusMessage = val
|
s.Description = val
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ref: #/components/schemas/MapfixTriggerCreate
|
// Ref: #/components/schemas/MapfixTriggerCreate
|
||||||
type MapfixTriggerCreate struct {
|
type MapfixTriggerCreate struct {
|
||||||
AssetID int64 `json:"AssetID"`
|
AssetID int64 `json:"AssetID"`
|
||||||
TargetAssetID int64 `json:"TargetAssetID"`
|
TargetAssetID int64 `json:"TargetAssetID"`
|
||||||
|
Description string `json:"Description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAssetID returns the value of AssetID.
|
// GetAssetID returns the value of AssetID.
|
||||||
@@ -498,6 +527,11 @@ func (s *MapfixTriggerCreate) GetTargetAssetID() int64 {
|
|||||||
return s.TargetAssetID
|
return s.TargetAssetID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDescription returns the value of Description.
|
||||||
|
func (s *MapfixTriggerCreate) GetDescription() string {
|
||||||
|
return s.Description
|
||||||
|
}
|
||||||
|
|
||||||
// SetAssetID sets the value of AssetID.
|
// SetAssetID sets the value of AssetID.
|
||||||
func (s *MapfixTriggerCreate) SetAssetID(val int64) {
|
func (s *MapfixTriggerCreate) SetAssetID(val int64) {
|
||||||
s.AssetID = val
|
s.AssetID = val
|
||||||
@@ -508,6 +542,11 @@ func (s *MapfixTriggerCreate) SetTargetAssetID(val int64) {
|
|||||||
s.TargetAssetID = val
|
s.TargetAssetID = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDescription sets the value of Description.
|
||||||
|
func (s *MapfixTriggerCreate) SetDescription(val string) {
|
||||||
|
s.Description = val
|
||||||
|
}
|
||||||
|
|
||||||
// Ref: #/components/schemas/Mapfixes
|
// Ref: #/components/schemas/Mapfixes
|
||||||
type Mapfixes struct {
|
type Mapfixes struct {
|
||||||
Total int64 `json:"Total"`
|
Total int64 `json:"Total"`
|
||||||
@@ -1163,7 +1202,6 @@ type Submission struct {
|
|||||||
Completed bool `json:"Completed"`
|
Completed bool `json:"Completed"`
|
||||||
UploadedAssetID OptInt64 `json:"UploadedAssetID"`
|
UploadedAssetID OptInt64 `json:"UploadedAssetID"`
|
||||||
StatusID int32 `json:"StatusID"`
|
StatusID int32 `json:"StatusID"`
|
||||||
StatusMessage string `json:"StatusMessage"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the value of ID.
|
// GetID returns the value of ID.
|
||||||
@@ -1236,11 +1274,6 @@ func (s *Submission) GetStatusID() int32 {
|
|||||||
return s.StatusID
|
return s.StatusID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStatusMessage returns the value of StatusMessage.
|
|
||||||
func (s *Submission) GetStatusMessage() string {
|
|
||||||
return s.StatusMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetID sets the value of ID.
|
// SetID sets the value of ID.
|
||||||
func (s *Submission) SetID(val int64) {
|
func (s *Submission) SetID(val int64) {
|
||||||
s.ID = val
|
s.ID = val
|
||||||
@@ -1311,14 +1344,12 @@ func (s *Submission) SetStatusID(val int32) {
|
|||||||
s.StatusID = val
|
s.StatusID = val
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetStatusMessage sets the value of StatusMessage.
|
|
||||||
func (s *Submission) SetStatusMessage(val string) {
|
|
||||||
s.StatusMessage = val
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ref: #/components/schemas/SubmissionTriggerCreate
|
// Ref: #/components/schemas/SubmissionTriggerCreate
|
||||||
type SubmissionTriggerCreate struct {
|
type SubmissionTriggerCreate struct {
|
||||||
AssetID int64 `json:"AssetID"`
|
AssetID int64 `json:"AssetID"`
|
||||||
|
DisplayName string `json:"DisplayName"`
|
||||||
|
Creator string `json:"Creator"`
|
||||||
|
GameID int32 `json:"GameID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAssetID returns the value of AssetID.
|
// GetAssetID returns the value of AssetID.
|
||||||
@@ -1326,11 +1357,41 @@ func (s *SubmissionTriggerCreate) GetAssetID() int64 {
|
|||||||
return s.AssetID
|
return s.AssetID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDisplayName returns the value of DisplayName.
|
||||||
|
func (s *SubmissionTriggerCreate) GetDisplayName() string {
|
||||||
|
return s.DisplayName
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCreator returns the value of Creator.
|
||||||
|
func (s *SubmissionTriggerCreate) GetCreator() string {
|
||||||
|
return s.Creator
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGameID returns the value of GameID.
|
||||||
|
func (s *SubmissionTriggerCreate) GetGameID() int32 {
|
||||||
|
return s.GameID
|
||||||
|
}
|
||||||
|
|
||||||
// SetAssetID sets the value of AssetID.
|
// SetAssetID sets the value of AssetID.
|
||||||
func (s *SubmissionTriggerCreate) SetAssetID(val int64) {
|
func (s *SubmissionTriggerCreate) SetAssetID(val int64) {
|
||||||
s.AssetID = val
|
s.AssetID = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDisplayName sets the value of DisplayName.
|
||||||
|
func (s *SubmissionTriggerCreate) SetDisplayName(val string) {
|
||||||
|
s.DisplayName = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCreator sets the value of Creator.
|
||||||
|
func (s *SubmissionTriggerCreate) SetCreator(val string) {
|
||||||
|
s.Creator = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGameID sets the value of GameID.
|
||||||
|
func (s *SubmissionTriggerCreate) SetGameID(val int32) {
|
||||||
|
s.GameID = val
|
||||||
|
}
|
||||||
|
|
||||||
// Ref: #/components/schemas/Submissions
|
// Ref: #/components/schemas/Submissions
|
||||||
type Submissions struct {
|
type Submissions struct {
|
||||||
Total int64 `json:"Total"`
|
Total int64 `json:"Total"`
|
||||||
|
|||||||
@@ -33,6 +33,51 @@ func findAuthorization(h http.Header, prefix string) (string, bool) {
|
|||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var operationRolesCookieAuth = map[string][]string{
|
||||||
|
ActionMapfixAcceptedOperation: []string{},
|
||||||
|
ActionMapfixBypassSubmitOperation: []string{},
|
||||||
|
ActionMapfixRejectOperation: []string{},
|
||||||
|
ActionMapfixRequestChangesOperation: []string{},
|
||||||
|
ActionMapfixResetSubmittingOperation: []string{},
|
||||||
|
ActionMapfixRetryValidateOperation: []string{},
|
||||||
|
ActionMapfixRevokeOperation: []string{},
|
||||||
|
ActionMapfixTriggerSubmitOperation: []string{},
|
||||||
|
ActionMapfixTriggerUploadOperation: []string{},
|
||||||
|
ActionMapfixTriggerValidateOperation: []string{},
|
||||||
|
ActionMapfixValidatedOperation: []string{},
|
||||||
|
ActionSubmissionAcceptedOperation: []string{},
|
||||||
|
ActionSubmissionBypassSubmitOperation: []string{},
|
||||||
|
ActionSubmissionRejectOperation: []string{},
|
||||||
|
ActionSubmissionRequestChangesOperation: []string{},
|
||||||
|
ActionSubmissionResetSubmittingOperation: []string{},
|
||||||
|
ActionSubmissionRetryValidateOperation: []string{},
|
||||||
|
ActionSubmissionRevokeOperation: []string{},
|
||||||
|
ActionSubmissionTriggerSubmitOperation: []string{},
|
||||||
|
ActionSubmissionTriggerUploadOperation: []string{},
|
||||||
|
ActionSubmissionTriggerValidateOperation: []string{},
|
||||||
|
ActionSubmissionValidatedOperation: []string{},
|
||||||
|
CreateMapfixOperation: []string{},
|
||||||
|
CreateMapfixAuditCommentOperation: []string{},
|
||||||
|
CreateScriptOperation: []string{},
|
||||||
|
CreateScriptPolicyOperation: []string{},
|
||||||
|
CreateSubmissionOperation: []string{},
|
||||||
|
CreateSubmissionAdminOperation: []string{},
|
||||||
|
CreateSubmissionAuditCommentOperation: []string{},
|
||||||
|
DeleteScriptOperation: []string{},
|
||||||
|
DeleteScriptPolicyOperation: []string{},
|
||||||
|
GetOperationOperation: []string{},
|
||||||
|
ReleaseSubmissionsOperation: []string{},
|
||||||
|
SessionRolesOperation: []string{},
|
||||||
|
SessionUserOperation: []string{},
|
||||||
|
SessionValidateOperation: []string{},
|
||||||
|
SetMapfixCompletedOperation: []string{},
|
||||||
|
SetSubmissionCompletedOperation: []string{},
|
||||||
|
UpdateMapfixModelOperation: []string{},
|
||||||
|
UpdateScriptOperation: []string{},
|
||||||
|
UpdateScriptPolicyOperation: []string{},
|
||||||
|
UpdateSubmissionModelOperation: []string{},
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) securityCookieAuth(ctx context.Context, operationName OperationName, req *http.Request) (context.Context, bool, error) {
|
func (s *Server) securityCookieAuth(ctx context.Context, operationName OperationName, req *http.Request) (context.Context, bool, error) {
|
||||||
var t CookieAuth
|
var t CookieAuth
|
||||||
const parameterName = "session_id"
|
const parameterName = "session_id"
|
||||||
@@ -46,6 +91,7 @@ func (s *Server) securityCookieAuth(ctx context.Context, operationName Operation
|
|||||||
return nil, false, errors.Wrap(err, "get cookie value")
|
return nil, false, errors.Wrap(err, "get cookie value")
|
||||||
}
|
}
|
||||||
t.APIKey = value
|
t.APIKey = value
|
||||||
|
t.Roles = operationRolesCookieAuth[operationName]
|
||||||
rctx, err := s.sec.HandleCookieAuth(ctx, operationName, t)
|
rctx, err := s.sec.HandleCookieAuth(ctx, operationName, t)
|
||||||
if errors.Is(err, ogenerrors.ErrSkipServerSecurity) {
|
if errors.Is(err, ogenerrors.ErrSkipServerSecurity) {
|
||||||
return nil, false, nil
|
return nil, false, nil
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ type Handler interface {
|
|||||||
//
|
//
|
||||||
// POST /mapfixes/{MapfixID}/status/reset-validating
|
// POST /mapfixes/{MapfixID}/status/reset-validating
|
||||||
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
|
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
|
||||||
|
// ActionMapfixBypassSubmit implements actionMapfixBypassSubmit operation.
|
||||||
|
//
|
||||||
|
// Role Reviewer changes status from ChangesRequested -> Submitted.
|
||||||
|
//
|
||||||
|
// POST /mapfixes/{MapfixID}/status/bypass-submit
|
||||||
|
ActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) error
|
||||||
// ActionMapfixReject implements actionMapfixReject operation.
|
// ActionMapfixReject implements actionMapfixReject operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer changes status from Submitted -> Rejected.
|
// Role Reviewer changes status from Submitted -> Rejected.
|
||||||
@@ -75,6 +81,12 @@ type Handler interface {
|
|||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/reset-validating
|
// POST /submissions/{SubmissionID}/status/reset-validating
|
||||||
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
|
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
|
||||||
|
// ActionSubmissionBypassSubmit implements actionSubmissionBypassSubmit operation.
|
||||||
|
//
|
||||||
|
// Role Reviewer changes status from ChangesRequested -> Submitted.
|
||||||
|
//
|
||||||
|
// POST /submissions/{SubmissionID}/status/bypass-submit
|
||||||
|
ActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) error
|
||||||
// ActionSubmissionReject implements actionSubmissionReject operation.
|
// ActionSubmissionReject implements actionSubmissionReject operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer changes status from Submitted -> Rejected.
|
// Role Reviewer changes status from Submitted -> Rejected.
|
||||||
@@ -160,6 +172,12 @@ type Handler interface {
|
|||||||
//
|
//
|
||||||
// POST /submissions
|
// POST /submissions
|
||||||
CreateSubmission(ctx context.Context, req *SubmissionTriggerCreate) (*OperationID, error)
|
CreateSubmission(ctx context.Context, req *SubmissionTriggerCreate) (*OperationID, error)
|
||||||
|
// CreateSubmissionAdmin implements createSubmissionAdmin operation.
|
||||||
|
//
|
||||||
|
// Trigger the validator to create a new submission.
|
||||||
|
//
|
||||||
|
// POST /submissions-admin
|
||||||
|
CreateSubmissionAdmin(ctx context.Context, req *SubmissionTriggerCreate) (*OperationID, error)
|
||||||
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
|
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
|
||||||
//
|
//
|
||||||
// Post a comment to the audit log.
|
// Post a comment to the audit log.
|
||||||
|
|||||||
@@ -22,6 +22,15 @@ func (UnimplementedHandler) ActionMapfixAccepted(ctx context.Context, params Act
|
|||||||
return ht.ErrNotImplemented
|
return ht.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionMapfixBypassSubmit implements actionMapfixBypassSubmit operation.
|
||||||
|
//
|
||||||
|
// Role Reviewer changes status from ChangesRequested -> Submitted.
|
||||||
|
//
|
||||||
|
// POST /mapfixes/{MapfixID}/status/bypass-submit
|
||||||
|
func (UnimplementedHandler) ActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) error {
|
||||||
|
return ht.ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
// ActionMapfixReject implements actionMapfixReject operation.
|
// ActionMapfixReject implements actionMapfixReject operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer changes status from Submitted -> Rejected.
|
// Role Reviewer changes status from Submitted -> Rejected.
|
||||||
@@ -113,6 +122,15 @@ func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params
|
|||||||
return ht.ErrNotImplemented
|
return ht.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionSubmissionBypassSubmit implements actionSubmissionBypassSubmit operation.
|
||||||
|
//
|
||||||
|
// Role Reviewer changes status from ChangesRequested -> Submitted.
|
||||||
|
//
|
||||||
|
// POST /submissions/{SubmissionID}/status/bypass-submit
|
||||||
|
func (UnimplementedHandler) ActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) error {
|
||||||
|
return ht.ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
// ActionSubmissionReject implements actionSubmissionReject operation.
|
// ActionSubmissionReject implements actionSubmissionReject operation.
|
||||||
//
|
//
|
||||||
// Role Reviewer changes status from Submitted -> Rejected.
|
// Role Reviewer changes status from Submitted -> Rejected.
|
||||||
@@ -240,6 +258,15 @@ func (UnimplementedHandler) CreateSubmission(ctx context.Context, req *Submissio
|
|||||||
return r, ht.ErrNotImplemented
|
return r, ht.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateSubmissionAdmin implements createSubmissionAdmin operation.
|
||||||
|
//
|
||||||
|
// Trigger the validator to create a new submission.
|
||||||
|
//
|
||||||
|
// POST /submissions-admin
|
||||||
|
func (UnimplementedHandler) CreateSubmissionAdmin(ctx context.Context, req *SubmissionTriggerCreate) (r *OperationID, _ error) {
|
||||||
|
return r, ht.ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
|
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
|
||||||
//
|
//
|
||||||
// Post a comment to the audit log.
|
// Post a comment to the audit log.
|
||||||
|
|||||||
@@ -10,6 +10,37 @@ import (
|
|||||||
"github.com/ogen-go/ogen/validate"
|
"github.com/ogen-go/ogen/validate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (s *AuditEvent) 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: 64,
|
||||||
|
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 len(failures) > 0 {
|
||||||
|
return &validate.Error{Fields: failures}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Error) Validate() error {
|
func (s *Error) Validate() error {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return validate.ErrNilPointer
|
return validate.ErrNilPointer
|
||||||
@@ -408,13 +439,13 @@ func (s *Mapfix) Validate() error {
|
|||||||
Email: false,
|
Email: false,
|
||||||
Hostname: false,
|
Hostname: false,
|
||||||
Regex: nil,
|
Regex: nil,
|
||||||
}).Validate(string(s.StatusMessage)); err != nil {
|
}).Validate(string(s.Description)); err != nil {
|
||||||
return errors.Wrap(err, "string")
|
return errors.Wrap(err, "string")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
failures = append(failures, validate.FieldError{
|
failures = append(failures, validate.FieldError{
|
||||||
Name: "StatusMessage",
|
Name: "Description",
|
||||||
Error: err,
|
Error: err,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -470,6 +501,25 @@ func (s *MapfixTriggerCreate) Validate() error {
|
|||||||
Error: err,
|
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.Description)); err != nil {
|
||||||
|
return errors.Wrap(err, "string")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
failures = append(failures, validate.FieldError{
|
||||||
|
Name: "Description",
|
||||||
|
Error: err,
|
||||||
|
})
|
||||||
|
}
|
||||||
if len(failures) > 0 {
|
if len(failures) > 0 {
|
||||||
return &validate.Error{Fields: failures}
|
return &validate.Error{Fields: failures}
|
||||||
}
|
}
|
||||||
@@ -1751,25 +1801,6 @@ func (s *Submission) Validate() error {
|
|||||||
Error: err,
|
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 {
|
if len(failures) > 0 {
|
||||||
return &validate.Error{Fields: failures}
|
return &validate.Error{Fields: failures}
|
||||||
}
|
}
|
||||||
@@ -1802,6 +1833,64 @@ func (s *SubmissionTriggerCreate) Validate() error {
|
|||||||
Error: err,
|
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.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.Int{
|
||||||
|
MinSet: true,
|
||||||
|
Min: 0,
|
||||||
|
MaxSet: false,
|
||||||
|
Max: 0,
|
||||||
|
MinExclusive: false,
|
||||||
|
MaxExclusive: false,
|
||||||
|
MultipleOfSet: false,
|
||||||
|
MultipleOf: 0,
|
||||||
|
}).Validate(int64(s.GameID)); err != nil {
|
||||||
|
return errors.Wrap(err, "int")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
failures = append(failures, validate.FieldError{
|
||||||
|
Name: "GameID",
|
||||||
|
Error: err,
|
||||||
|
})
|
||||||
|
}
|
||||||
if len(failures) > 0 {
|
if len(failures) > 0 {
|
||||||
return &validate.Error{Fields: failures}
|
return &validate.Error{Fields: failures}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"git.itzana.me/strafesnet/go-grpc/auth"
|
"git.itzana.me/strafesnet/go-grpc/auth"
|
||||||
"git.itzana.me/strafesnet/go-grpc/maps"
|
"git.itzana.me/strafesnet/go-grpc/maps"
|
||||||
|
"git.itzana.me/strafesnet/go-grpc/users"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore"
|
"git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore"
|
||||||
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
|
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
|
||||||
@@ -125,7 +126,8 @@ func serve(ctx *cli.Context) error {
|
|||||||
svc := &service.Service{
|
svc := &service.Service{
|
||||||
DB: db,
|
DB: db,
|
||||||
Nats: js,
|
Nats: js,
|
||||||
Client: maps.NewMapsServiceClient(conn),
|
Maps: maps.NewMapsServiceClient(conn),
|
||||||
|
Users: users.NewUsersServiceClient(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()))
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ type Invoker interface {
|
|||||||
//
|
//
|
||||||
// POST /mapfixes/{MapfixID}/status/validator-failed
|
// POST /mapfixes/{MapfixID}/status/validator-failed
|
||||||
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
|
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
|
||||||
|
// ActionMapfixRequestChanges invokes actionMapfixRequestChanges operation.
|
||||||
|
//
|
||||||
|
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
|
||||||
|
//
|
||||||
|
// POST /mapfixes/{MapfixID}/status/validator-request-changes
|
||||||
|
ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error
|
||||||
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
|
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
|
||||||
//
|
//
|
||||||
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
||||||
@@ -64,6 +70,12 @@ type Invoker interface {
|
|||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/validator-failed
|
// POST /submissions/{SubmissionID}/status/validator-failed
|
||||||
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
|
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
|
||||||
|
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
|
||||||
|
//
|
||||||
|
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
|
||||||
|
//
|
||||||
|
// POST /submissions/{SubmissionID}/status/validator-request-changes
|
||||||
|
ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error
|
||||||
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
|
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
|
||||||
//
|
//
|
||||||
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
||||||
@@ -257,15 +269,15 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf
|
|||||||
stage = "EncodeQueryParams"
|
stage = "EncodeQueryParams"
|
||||||
q := uri.NewQueryEncoder()
|
q := uri.NewQueryEncoder()
|
||||||
{
|
{
|
||||||
// Encode "StatusMessage" parameter.
|
// Encode "ErrorMessage" parameter.
|
||||||
cfg := uri.QueryParameterEncodingConfig{
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
Name: "StatusMessage",
|
Name: "ErrorMessage",
|
||||||
Style: uri.QueryStyleForm,
|
Style: uri.QueryStyleForm,
|
||||||
Explode: true,
|
Explode: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
return e.EncodeValue(conv.StringToString(params.StatusMessage))
|
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return res, errors.Wrap(err, "encode query")
|
return res, errors.Wrap(err, "encode query")
|
||||||
}
|
}
|
||||||
@@ -294,6 +306,115 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionMapfixRequestChanges invokes actionMapfixRequestChanges operation.
|
||||||
|
//
|
||||||
|
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
|
||||||
|
//
|
||||||
|
// POST /mapfixes/{MapfixID}/status/validator-request-changes
|
||||||
|
func (c *Client) ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error {
|
||||||
|
_, err := c.sendActionMapfixRequestChanges(ctx, params)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) (res *ActionMapfixRequestChangesNoContent, err error) {
|
||||||
|
otelAttrs := []attribute.KeyValue{
|
||||||
|
otelogen.OperationID("actionMapfixRequestChanges"),
|
||||||
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
|
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-request-changes"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, ActionMapfixRequestChangesOperation,
|
||||||
|
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-request-changes"
|
||||||
|
uri.AddPathParts(u, pathParts[:]...)
|
||||||
|
|
||||||
|
stage = "EncodeQueryParams"
|
||||||
|
q := uri.NewQueryEncoder()
|
||||||
|
{
|
||||||
|
// Encode "ErrorMessage" parameter.
|
||||||
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
|
Name: "ErrorMessage",
|
||||||
|
Style: uri.QueryStyleForm,
|
||||||
|
Explode: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
|
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
|
||||||
|
}); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u.RawQuery = q.Values().Encode()
|
||||||
|
|
||||||
|
stage = "EncodeRequest"
|
||||||
|
r, err := ht.NewRequest(ctx, "POST", u)
|
||||||
|
if err != nil {
|
||||||
|
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 := decodeActionMapfixRequestChangesResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "decode response")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
|
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
|
||||||
//
|
//
|
||||||
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
||||||
@@ -363,6 +484,66 @@ func (c *Client) sendActionMapfixSubmitted(ctx context.Context, params ActionMap
|
|||||||
pathParts[2] = "/status/validator-submitted"
|
pathParts[2] = "/status/validator-submitted"
|
||||||
uri.AddPathParts(u, pathParts[:]...)
|
uri.AddPathParts(u, pathParts[:]...)
|
||||||
|
|
||||||
|
stage = "EncodeQueryParams"
|
||||||
|
q := uri.NewQueryEncoder()
|
||||||
|
{
|
||||||
|
// Encode "ModelVersion" parameter.
|
||||||
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
|
Name: "ModelVersion",
|
||||||
|
Style: uri.QueryStyleForm,
|
||||||
|
Explode: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
|
return e.EncodeValue(conv.Int64ToString(params.ModelVersion))
|
||||||
|
}); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Encode "DisplayName" parameter.
|
||||||
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
|
Name: "DisplayName",
|
||||||
|
Style: uri.QueryStyleForm,
|
||||||
|
Explode: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
|
return e.EncodeValue(conv.StringToString(params.DisplayName))
|
||||||
|
}); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Encode "Creator" parameter.
|
||||||
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
|
Name: "Creator",
|
||||||
|
Style: uri.QueryStyleForm,
|
||||||
|
Explode: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
|
return e.EncodeValue(conv.StringToString(params.Creator))
|
||||||
|
}); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Encode "GameID" parameter.
|
||||||
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
|
Name: "GameID",
|
||||||
|
Style: uri.QueryStyleForm,
|
||||||
|
Explode: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
|
return e.EncodeValue(conv.Int32ToString(params.GameID))
|
||||||
|
}); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u.RawQuery = q.Values().Encode()
|
||||||
|
|
||||||
stage = "EncodeRequest"
|
stage = "EncodeRequest"
|
||||||
r, err := ht.NewRequest(ctx, "POST", u)
|
r, err := ht.NewRequest(ctx, "POST", u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -748,15 +929,15 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
|
|||||||
stage = "EncodeQueryParams"
|
stage = "EncodeQueryParams"
|
||||||
q := uri.NewQueryEncoder()
|
q := uri.NewQueryEncoder()
|
||||||
{
|
{
|
||||||
// Encode "StatusMessage" parameter.
|
// Encode "ErrorMessage" parameter.
|
||||||
cfg := uri.QueryParameterEncodingConfig{
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
Name: "StatusMessage",
|
Name: "ErrorMessage",
|
||||||
Style: uri.QueryStyleForm,
|
Style: uri.QueryStyleForm,
|
||||||
Explode: true,
|
Explode: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
return e.EncodeValue(conv.StringToString(params.StatusMessage))
|
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return res, errors.Wrap(err, "encode query")
|
return res, errors.Wrap(err, "encode query")
|
||||||
}
|
}
|
||||||
@@ -785,6 +966,115 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
|
||||||
|
//
|
||||||
|
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
|
||||||
|
//
|
||||||
|
// POST /submissions/{SubmissionID}/status/validator-request-changes
|
||||||
|
func (c *Client) ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error {
|
||||||
|
_, err := c.sendActionSubmissionRequestChanges(ctx, params)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) (res *ActionSubmissionRequestChangesNoContent, err error) {
|
||||||
|
otelAttrs := []attribute.KeyValue{
|
||||||
|
otelogen.OperationID("actionSubmissionRequestChanges"),
|
||||||
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
|
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validator-request-changes"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, ActionSubmissionRequestChangesOperation,
|
||||||
|
trace.WithAttributes(otelAttrs...),
|
||||||
|
clientSpanKind,
|
||||||
|
)
|
||||||
|
// Track stage for error reporting.
|
||||||
|
var stage string
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, stage)
|
||||||
|
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
|
stage = "BuildURL"
|
||||||
|
u := uri.Clone(c.requestURL(ctx))
|
||||||
|
var pathParts [3]string
|
||||||
|
pathParts[0] = "/submissions/"
|
||||||
|
{
|
||||||
|
// Encode "SubmissionID" parameter.
|
||||||
|
e := uri.NewPathEncoder(uri.PathEncoderConfig{
|
||||||
|
Param: "SubmissionID",
|
||||||
|
Style: uri.PathStyleSimple,
|
||||||
|
Explode: false,
|
||||||
|
})
|
||||||
|
if err := func() error {
|
||||||
|
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
|
||||||
|
}(); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode path")
|
||||||
|
}
|
||||||
|
encoded, err := e.Result()
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode path")
|
||||||
|
}
|
||||||
|
pathParts[1] = encoded
|
||||||
|
}
|
||||||
|
pathParts[2] = "/status/validator-request-changes"
|
||||||
|
uri.AddPathParts(u, pathParts[:]...)
|
||||||
|
|
||||||
|
stage = "EncodeQueryParams"
|
||||||
|
q := uri.NewQueryEncoder()
|
||||||
|
{
|
||||||
|
// Encode "ErrorMessage" parameter.
|
||||||
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
|
Name: "ErrorMessage",
|
||||||
|
Style: uri.QueryStyleForm,
|
||||||
|
Explode: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
|
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
|
||||||
|
}); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u.RawQuery = q.Values().Encode()
|
||||||
|
|
||||||
|
stage = "EncodeRequest"
|
||||||
|
r, err := ht.NewRequest(ctx, "POST", u)
|
||||||
|
if err != nil {
|
||||||
|
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 := decodeActionSubmissionRequestChangesResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "decode response")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
|
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
|
||||||
//
|
//
|
||||||
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
||||||
@@ -854,6 +1144,66 @@ func (c *Client) sendActionSubmissionSubmitted(ctx context.Context, params Actio
|
|||||||
pathParts[2] = "/status/validator-submitted"
|
pathParts[2] = "/status/validator-submitted"
|
||||||
uri.AddPathParts(u, pathParts[:]...)
|
uri.AddPathParts(u, pathParts[:]...)
|
||||||
|
|
||||||
|
stage = "EncodeQueryParams"
|
||||||
|
q := uri.NewQueryEncoder()
|
||||||
|
{
|
||||||
|
// Encode "ModelVersion" parameter.
|
||||||
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
|
Name: "ModelVersion",
|
||||||
|
Style: uri.QueryStyleForm,
|
||||||
|
Explode: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
|
return e.EncodeValue(conv.Int64ToString(params.ModelVersion))
|
||||||
|
}); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Encode "DisplayName" parameter.
|
||||||
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
|
Name: "DisplayName",
|
||||||
|
Style: uri.QueryStyleForm,
|
||||||
|
Explode: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
|
return e.EncodeValue(conv.StringToString(params.DisplayName))
|
||||||
|
}); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Encode "Creator" parameter.
|
||||||
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
|
Name: "Creator",
|
||||||
|
Style: uri.QueryStyleForm,
|
||||||
|
Explode: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
|
return e.EncodeValue(conv.StringToString(params.Creator))
|
||||||
|
}); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Encode "GameID" parameter.
|
||||||
|
cfg := uri.QueryParameterEncodingConfig{
|
||||||
|
Name: "GameID",
|
||||||
|
Style: uri.QueryStyleForm,
|
||||||
|
Explode: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
|
||||||
|
return e.EncodeValue(conv.Int32ToString(params.GameID))
|
||||||
|
}); err != nil {
|
||||||
|
return res, errors.Wrap(err, "encode query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u.RawQuery = q.Values().Encode()
|
||||||
|
|
||||||
stage = "EncodeRequest"
|
stage = "EncodeRequest"
|
||||||
r, err := ht.NewRequest(ctx, "POST", u)
|
r, err := ht.NewRequest(ctx, "POST", u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -129,9 +129,9 @@ func (s *Server) handleActionMapfixAcceptedRequest(args [1]string, argsEscaped b
|
|||||||
In: "path",
|
In: "path",
|
||||||
}: params.MapfixID,
|
}: params.MapfixID,
|
||||||
{
|
{
|
||||||
Name: "StatusMessage",
|
Name: "ErrorMessage",
|
||||||
In: "query",
|
In: "query",
|
||||||
}: params.StatusMessage,
|
}: params.ErrorMessage,
|
||||||
},
|
},
|
||||||
Raw: r,
|
Raw: r,
|
||||||
}
|
}
|
||||||
@@ -183,6 +183,159 @@ func (s *Server) handleActionMapfixAcceptedRequest(args [1]string, argsEscaped b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleActionMapfixRequestChangesRequest handles actionMapfixRequestChanges operation.
|
||||||
|
//
|
||||||
|
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
|
||||||
|
//
|
||||||
|
// POST /mapfixes/{MapfixID}/status/validator-request-changes
|
||||||
|
func (s *Server) handleActionMapfixRequestChangesRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
|
||||||
|
statusWriter := &codeRecorder{ResponseWriter: w}
|
||||||
|
w = statusWriter
|
||||||
|
otelAttrs := []attribute.KeyValue{
|
||||||
|
otelogen.OperationID("actionMapfixRequestChanges"),
|
||||||
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
|
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-request-changes"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a span for this request.
|
||||||
|
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixRequestChangesOperation,
|
||||||
|
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: ActionMapfixRequestChangesOperation,
|
||||||
|
ID: "actionMapfixRequestChanges",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
params, err := decodeActionMapfixRequestChangesParams(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 *ActionMapfixRequestChangesNoContent
|
||||||
|
if m := s.cfg.Middleware; m != nil {
|
||||||
|
mreq := middleware.Request{
|
||||||
|
Context: ctx,
|
||||||
|
OperationName: ActionMapfixRequestChangesOperation,
|
||||||
|
OperationSummary: "(Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested",
|
||||||
|
OperationID: "actionMapfixRequestChanges",
|
||||||
|
Body: nil,
|
||||||
|
Params: middleware.Parameters{
|
||||||
|
{
|
||||||
|
Name: "MapfixID",
|
||||||
|
In: "path",
|
||||||
|
}: params.MapfixID,
|
||||||
|
{
|
||||||
|
Name: "ErrorMessage",
|
||||||
|
In: "query",
|
||||||
|
}: params.ErrorMessage,
|
||||||
|
},
|
||||||
|
Raw: r,
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
Request = struct{}
|
||||||
|
Params = ActionMapfixRequestChangesParams
|
||||||
|
Response = *ActionMapfixRequestChangesNoContent
|
||||||
|
)
|
||||||
|
response, err = middleware.HookMiddleware[
|
||||||
|
Request,
|
||||||
|
Params,
|
||||||
|
Response,
|
||||||
|
](
|
||||||
|
m,
|
||||||
|
mreq,
|
||||||
|
unpackActionMapfixRequestChangesParams,
|
||||||
|
func(ctx context.Context, request Request, params Params) (response Response, err error) {
|
||||||
|
err = s.h.ActionMapfixRequestChanges(ctx, params)
|
||||||
|
return response, err
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
err = s.h.ActionMapfixRequestChanges(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 := encodeActionMapfixRequestChangesResponse(response, w, span); err != nil {
|
||||||
|
defer recordError("EncodeResponse", err)
|
||||||
|
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
|
||||||
|
s.cfg.ErrorHandler(ctx, w, r, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handleActionMapfixSubmittedRequest handles actionMapfixSubmitted operation.
|
// handleActionMapfixSubmittedRequest handles actionMapfixSubmitted operation.
|
||||||
//
|
//
|
||||||
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
||||||
@@ -281,6 +434,22 @@ func (s *Server) handleActionMapfixSubmittedRequest(args [1]string, argsEscaped
|
|||||||
Name: "MapfixID",
|
Name: "MapfixID",
|
||||||
In: "path",
|
In: "path",
|
||||||
}: params.MapfixID,
|
}: params.MapfixID,
|
||||||
|
{
|
||||||
|
Name: "ModelVersion",
|
||||||
|
In: "query",
|
||||||
|
}: params.ModelVersion,
|
||||||
|
{
|
||||||
|
Name: "DisplayName",
|
||||||
|
In: "query",
|
||||||
|
}: params.DisplayName,
|
||||||
|
{
|
||||||
|
Name: "Creator",
|
||||||
|
In: "query",
|
||||||
|
}: params.Creator,
|
||||||
|
{
|
||||||
|
Name: "GameID",
|
||||||
|
In: "query",
|
||||||
|
}: params.GameID,
|
||||||
},
|
},
|
||||||
Raw: r,
|
Raw: r,
|
||||||
}
|
}
|
||||||
@@ -882,9 +1051,9 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
|
|||||||
In: "path",
|
In: "path",
|
||||||
}: params.SubmissionID,
|
}: params.SubmissionID,
|
||||||
{
|
{
|
||||||
Name: "StatusMessage",
|
Name: "ErrorMessage",
|
||||||
In: "query",
|
In: "query",
|
||||||
}: params.StatusMessage,
|
}: params.ErrorMessage,
|
||||||
},
|
},
|
||||||
Raw: r,
|
Raw: r,
|
||||||
}
|
}
|
||||||
@@ -936,6 +1105,159 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleActionSubmissionRequestChangesRequest handles actionSubmissionRequestChanges operation.
|
||||||
|
//
|
||||||
|
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
|
||||||
|
//
|
||||||
|
// POST /submissions/{SubmissionID}/status/validator-request-changes
|
||||||
|
func (s *Server) handleActionSubmissionRequestChangesRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
|
||||||
|
statusWriter := &codeRecorder{ResponseWriter: w}
|
||||||
|
w = statusWriter
|
||||||
|
otelAttrs := []attribute.KeyValue{
|
||||||
|
otelogen.OperationID("actionSubmissionRequestChanges"),
|
||||||
|
semconv.HTTPRequestMethodKey.String("POST"),
|
||||||
|
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validator-request-changes"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a span for this request.
|
||||||
|
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionRequestChangesOperation,
|
||||||
|
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: ActionSubmissionRequestChangesOperation,
|
||||||
|
ID: "actionSubmissionRequestChanges",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
params, err := decodeActionSubmissionRequestChangesParams(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 *ActionSubmissionRequestChangesNoContent
|
||||||
|
if m := s.cfg.Middleware; m != nil {
|
||||||
|
mreq := middleware.Request{
|
||||||
|
Context: ctx,
|
||||||
|
OperationName: ActionSubmissionRequestChangesOperation,
|
||||||
|
OperationSummary: "(Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested",
|
||||||
|
OperationID: "actionSubmissionRequestChanges",
|
||||||
|
Body: nil,
|
||||||
|
Params: middleware.Parameters{
|
||||||
|
{
|
||||||
|
Name: "SubmissionID",
|
||||||
|
In: "path",
|
||||||
|
}: params.SubmissionID,
|
||||||
|
{
|
||||||
|
Name: "ErrorMessage",
|
||||||
|
In: "query",
|
||||||
|
}: params.ErrorMessage,
|
||||||
|
},
|
||||||
|
Raw: r,
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
Request = struct{}
|
||||||
|
Params = ActionSubmissionRequestChangesParams
|
||||||
|
Response = *ActionSubmissionRequestChangesNoContent
|
||||||
|
)
|
||||||
|
response, err = middleware.HookMiddleware[
|
||||||
|
Request,
|
||||||
|
Params,
|
||||||
|
Response,
|
||||||
|
](
|
||||||
|
m,
|
||||||
|
mreq,
|
||||||
|
unpackActionSubmissionRequestChangesParams,
|
||||||
|
func(ctx context.Context, request Request, params Params) (response Response, err error) {
|
||||||
|
err = s.h.ActionSubmissionRequestChanges(ctx, params)
|
||||||
|
return response, err
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
err = s.h.ActionSubmissionRequestChanges(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 := encodeActionSubmissionRequestChangesResponse(response, w, span); err != nil {
|
||||||
|
defer recordError("EncodeResponse", err)
|
||||||
|
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
|
||||||
|
s.cfg.ErrorHandler(ctx, w, r, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handleActionSubmissionSubmittedRequest handles actionSubmissionSubmitted operation.
|
// handleActionSubmissionSubmittedRequest handles actionSubmissionSubmitted operation.
|
||||||
//
|
//
|
||||||
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
||||||
@@ -1034,6 +1356,22 @@ func (s *Server) handleActionSubmissionSubmittedRequest(args [1]string, argsEsca
|
|||||||
Name: "SubmissionID",
|
Name: "SubmissionID",
|
||||||
In: "path",
|
In: "path",
|
||||||
}: params.SubmissionID,
|
}: params.SubmissionID,
|
||||||
|
{
|
||||||
|
Name: "ModelVersion",
|
||||||
|
In: "query",
|
||||||
|
}: params.ModelVersion,
|
||||||
|
{
|
||||||
|
Name: "DisplayName",
|
||||||
|
In: "query",
|
||||||
|
}: params.DisplayName,
|
||||||
|
{
|
||||||
|
Name: "Creator",
|
||||||
|
In: "query",
|
||||||
|
}: params.Creator,
|
||||||
|
{
|
||||||
|
Name: "GameID",
|
||||||
|
In: "query",
|
||||||
|
}: params.GameID,
|
||||||
},
|
},
|
||||||
Raw: r,
|
Raw: r,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,9 +166,13 @@ func (s *MapfixCreate) encodeFields(e *jx.Encoder) {
|
|||||||
e.FieldStart("TargetAssetID")
|
e.FieldStart("TargetAssetID")
|
||||||
e.Int64(s.TargetAssetID)
|
e.Int64(s.TargetAssetID)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
e.FieldStart("Description")
|
||||||
|
e.Str(s.Description)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsonFieldsNameOfMapfixCreate = [8]string{
|
var jsonFieldsNameOfMapfixCreate = [9]string{
|
||||||
0: "OperationID",
|
0: "OperationID",
|
||||||
1: "AssetOwner",
|
1: "AssetOwner",
|
||||||
2: "DisplayName",
|
2: "DisplayName",
|
||||||
@@ -177,6 +181,7 @@ var jsonFieldsNameOfMapfixCreate = [8]string{
|
|||||||
5: "AssetID",
|
5: "AssetID",
|
||||||
6: "AssetVersion",
|
6: "AssetVersion",
|
||||||
7: "TargetAssetID",
|
7: "TargetAssetID",
|
||||||
|
8: "Description",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes MapfixCreate from json.
|
// Decode decodes MapfixCreate from json.
|
||||||
@@ -184,7 +189,7 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
|
|||||||
if s == nil {
|
if s == nil {
|
||||||
return errors.New("invalid: unable to decode MapfixCreate to nil")
|
return errors.New("invalid: unable to decode MapfixCreate to nil")
|
||||||
}
|
}
|
||||||
var requiredBitSet [1]uint8
|
var requiredBitSet [2]uint8
|
||||||
|
|
||||||
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
|
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
|
||||||
switch string(k) {
|
switch string(k) {
|
||||||
@@ -284,6 +289,18 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
|
|||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return errors.Wrap(err, "decode field \"TargetAssetID\"")
|
return errors.Wrap(err, "decode field \"TargetAssetID\"")
|
||||||
}
|
}
|
||||||
|
case "Description":
|
||||||
|
requiredBitSet[1] |= 1 << 0
|
||||||
|
if err := func() error {
|
||||||
|
v, err := d.Str()
|
||||||
|
s.Description = string(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return errors.Wrap(err, "decode field \"Description\"")
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
}
|
}
|
||||||
@@ -293,8 +310,9 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
|
|||||||
}
|
}
|
||||||
// Validate required fields.
|
// Validate required fields.
|
||||||
var failures []validate.FieldError
|
var failures []validate.FieldError
|
||||||
for i, mask := range [1]uint8{
|
for i, mask := range [2]uint8{
|
||||||
0b11111111,
|
0b11111111,
|
||||||
|
0b00000001,
|
||||||
} {
|
} {
|
||||||
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
||||||
// Mask only required fields and check equality to mask using XOR.
|
// Mask only required fields and check equality to mask using XOR.
|
||||||
@@ -1305,9 +1323,17 @@ func (s *SubmissionCreate) encodeFields(e *jx.Encoder) {
|
|||||||
e.FieldStart("AssetVersion")
|
e.FieldStart("AssetVersion")
|
||||||
e.Int64(s.AssetVersion)
|
e.Int64(s.AssetVersion)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
e.FieldStart("Status")
|
||||||
|
e.UInt32(s.Status)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
e.FieldStart("Roles")
|
||||||
|
e.UInt32(s.Roles)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsonFieldsNameOfSubmissionCreate = [7]string{
|
var jsonFieldsNameOfSubmissionCreate = [9]string{
|
||||||
0: "OperationID",
|
0: "OperationID",
|
||||||
1: "AssetOwner",
|
1: "AssetOwner",
|
||||||
2: "DisplayName",
|
2: "DisplayName",
|
||||||
@@ -1315,6 +1341,8 @@ var jsonFieldsNameOfSubmissionCreate = [7]string{
|
|||||||
4: "GameID",
|
4: "GameID",
|
||||||
5: "AssetID",
|
5: "AssetID",
|
||||||
6: "AssetVersion",
|
6: "AssetVersion",
|
||||||
|
7: "Status",
|
||||||
|
8: "Roles",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes SubmissionCreate from json.
|
// Decode decodes SubmissionCreate from json.
|
||||||
@@ -1322,7 +1350,7 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
|
|||||||
if s == nil {
|
if s == nil {
|
||||||
return errors.New("invalid: unable to decode SubmissionCreate to nil")
|
return errors.New("invalid: unable to decode SubmissionCreate to nil")
|
||||||
}
|
}
|
||||||
var requiredBitSet [1]uint8
|
var requiredBitSet [2]uint8
|
||||||
|
|
||||||
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
|
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
|
||||||
switch string(k) {
|
switch string(k) {
|
||||||
@@ -1410,6 +1438,30 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
|
|||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return errors.Wrap(err, "decode field \"AssetVersion\"")
|
return errors.Wrap(err, "decode field \"AssetVersion\"")
|
||||||
}
|
}
|
||||||
|
case "Status":
|
||||||
|
requiredBitSet[0] |= 1 << 7
|
||||||
|
if err := func() error {
|
||||||
|
v, err := d.UInt32()
|
||||||
|
s.Status = uint32(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return errors.Wrap(err, "decode field \"Status\"")
|
||||||
|
}
|
||||||
|
case "Roles":
|
||||||
|
requiredBitSet[1] |= 1 << 0
|
||||||
|
if err := func() error {
|
||||||
|
v, err := d.UInt32()
|
||||||
|
s.Roles = uint32(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return errors.Wrap(err, "decode field \"Roles\"")
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
}
|
}
|
||||||
@@ -1419,8 +1471,9 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
|
|||||||
}
|
}
|
||||||
// Validate required fields.
|
// Validate required fields.
|
||||||
var failures []validate.FieldError
|
var failures []validate.FieldError
|
||||||
for i, mask := range [1]uint8{
|
for i, mask := range [2]uint8{
|
||||||
0b01111111,
|
0b11111111,
|
||||||
|
0b00000001,
|
||||||
} {
|
} {
|
||||||
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
|
||||||
// Mask only required fields and check equality to mask using XOR.
|
// Mask only required fields and check equality to mask using XOR.
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ type OperationName = string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
|
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
|
||||||
|
ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges"
|
||||||
ActionMapfixSubmittedOperation OperationName = "ActionMapfixSubmitted"
|
ActionMapfixSubmittedOperation OperationName = "ActionMapfixSubmitted"
|
||||||
ActionMapfixUploadedOperation OperationName = "ActionMapfixUploaded"
|
ActionMapfixUploadedOperation OperationName = "ActionMapfixUploaded"
|
||||||
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
|
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
|
||||||
ActionOperationFailedOperation OperationName = "ActionOperationFailed"
|
ActionOperationFailedOperation OperationName = "ActionOperationFailed"
|
||||||
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
|
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
|
||||||
|
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
|
||||||
ActionSubmissionSubmittedOperation OperationName = "ActionSubmissionSubmitted"
|
ActionSubmissionSubmittedOperation OperationName = "ActionSubmissionSubmitted"
|
||||||
ActionSubmissionUploadedOperation OperationName = "ActionSubmissionUploaded"
|
ActionSubmissionUploadedOperation OperationName = "ActionSubmissionUploaded"
|
||||||
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
|
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import (
|
|||||||
// ActionMapfixAcceptedParams is parameters of actionMapfixAccepted operation.
|
// ActionMapfixAcceptedParams is parameters of actionMapfixAccepted operation.
|
||||||
type ActionMapfixAcceptedParams struct {
|
type ActionMapfixAcceptedParams struct {
|
||||||
// The unique identifier for a submission.
|
// The unique identifier for a submission.
|
||||||
MapfixID int64
|
MapfixID int64
|
||||||
StatusMessage string
|
ErrorMessage string
|
||||||
}
|
}
|
||||||
|
|
||||||
func unpackActionMapfixAcceptedParams(packed middleware.Parameters) (params ActionMapfixAcceptedParams) {
|
func unpackActionMapfixAcceptedParams(packed middleware.Parameters) (params ActionMapfixAcceptedParams) {
|
||||||
@@ -32,10 +32,10 @@ func unpackActionMapfixAcceptedParams(packed middleware.Parameters) (params Acti
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
key := middleware.ParameterKey{
|
key := middleware.ParameterKey{
|
||||||
Name: "StatusMessage",
|
Name: "ErrorMessage",
|
||||||
In: "query",
|
In: "query",
|
||||||
}
|
}
|
||||||
params.StatusMessage = packed[key].(string)
|
params.ErrorMessage = packed[key].(string)
|
||||||
}
|
}
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
@@ -104,10 +104,10 @@ func decodeActionMapfixAcceptedParams(args [1]string, argsEscaped bool, r *http.
|
|||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Decode query: StatusMessage.
|
// Decode query: ErrorMessage.
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
cfg := uri.QueryParameterDecodingConfig{
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
Name: "StatusMessage",
|
Name: "ErrorMessage",
|
||||||
Style: uri.QueryStyleForm,
|
Style: uri.QueryStyleForm,
|
||||||
Explode: true,
|
Explode: true,
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ func decodeActionMapfixAcceptedParams(args [1]string, argsEscaped bool, r *http.
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
params.StatusMessage = c
|
params.ErrorMessage = c
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -138,7 +138,7 @@ func decodeActionMapfixAcceptedParams(args [1]string, argsEscaped bool, r *http.
|
|||||||
Email: false,
|
Email: false,
|
||||||
Hostname: false,
|
Hostname: false,
|
||||||
Regex: nil,
|
Regex: nil,
|
||||||
}).Validate(string(params.StatusMessage)); err != nil {
|
}).Validate(string(params.ErrorMessage)); err != nil {
|
||||||
return errors.Wrap(err, "string")
|
return errors.Wrap(err, "string")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -151,7 +151,7 @@ func decodeActionMapfixAcceptedParams(args [1]string, argsEscaped bool, r *http.
|
|||||||
return nil
|
return nil
|
||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return params, &ogenerrors.DecodeParamError{
|
return params, &ogenerrors.DecodeParamError{
|
||||||
Name: "StatusMessage",
|
Name: "ErrorMessage",
|
||||||
In: "query",
|
In: "query",
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
@@ -159,13 +159,14 @@ func decodeActionMapfixAcceptedParams(args [1]string, argsEscaped bool, r *http.
|
|||||||
return params, nil
|
return params, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionMapfixSubmittedParams is parameters of actionMapfixSubmitted operation.
|
// ActionMapfixRequestChangesParams is parameters of actionMapfixRequestChanges operation.
|
||||||
type ActionMapfixSubmittedParams struct {
|
type ActionMapfixRequestChangesParams struct {
|
||||||
// The unique identifier for a submission.
|
// The unique identifier for a submission.
|
||||||
MapfixID int64
|
MapfixID int64
|
||||||
|
ErrorMessage string
|
||||||
}
|
}
|
||||||
|
|
||||||
func unpackActionMapfixSubmittedParams(packed middleware.Parameters) (params ActionMapfixSubmittedParams) {
|
func unpackActionMapfixRequestChangesParams(packed middleware.Parameters) (params ActionMapfixRequestChangesParams) {
|
||||||
{
|
{
|
||||||
key := middleware.ParameterKey{
|
key := middleware.ParameterKey{
|
||||||
Name: "MapfixID",
|
Name: "MapfixID",
|
||||||
@@ -173,10 +174,18 @@ func unpackActionMapfixSubmittedParams(packed middleware.Parameters) (params Act
|
|||||||
}
|
}
|
||||||
params.MapfixID = packed[key].(int64)
|
params.MapfixID = packed[key].(int64)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "ErrorMessage",
|
||||||
|
In: "query",
|
||||||
|
}
|
||||||
|
params.ErrorMessage = packed[key].(string)
|
||||||
|
}
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeActionMapfixSubmittedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixSubmittedParams, _ error) {
|
func decodeActionMapfixRequestChangesParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixRequestChangesParams, _ error) {
|
||||||
|
q := uri.NewQueryDecoder(r.URL.Query())
|
||||||
// Decode path: MapfixID.
|
// Decode path: MapfixID.
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
param := args[0]
|
param := args[0]
|
||||||
@@ -239,6 +248,384 @@ func decodeActionMapfixSubmittedParams(args [1]string, argsEscaped bool, r *http
|
|||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Decode query: ErrorMessage.
|
||||||
|
if err := func() error {
|
||||||
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
|
Name: "ErrorMessage",
|
||||||
|
Style: uri.QueryStyleForm,
|
||||||
|
Explode: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.HasParam(cfg); err == nil {
|
||||||
|
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
|
||||||
|
val, err := d.DecodeValue()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := conv.ToString(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
params.ErrorMessage = c
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := func() error {
|
||||||
|
if err := (validate.String{
|
||||||
|
MinLength: 0,
|
||||||
|
MinLengthSet: true,
|
||||||
|
MaxLength: 4096,
|
||||||
|
MaxLengthSet: true,
|
||||||
|
Email: false,
|
||||||
|
Hostname: false,
|
||||||
|
Regex: nil,
|
||||||
|
}).Validate(string(params.ErrorMessage)); err != nil {
|
||||||
|
return errors.Wrap(err, "string")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return params, &ogenerrors.DecodeParamError{
|
||||||
|
Name: "ErrorMessage",
|
||||||
|
In: "query",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionMapfixSubmittedParams is parameters of actionMapfixSubmitted operation.
|
||||||
|
type ActionMapfixSubmittedParams struct {
|
||||||
|
// The unique identifier for a submission.
|
||||||
|
MapfixID int64
|
||||||
|
ModelVersion int64
|
||||||
|
DisplayName string
|
||||||
|
Creator string
|
||||||
|
GameID int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func unpackActionMapfixSubmittedParams(packed middleware.Parameters) (params ActionMapfixSubmittedParams) {
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "MapfixID",
|
||||||
|
In: "path",
|
||||||
|
}
|
||||||
|
params.MapfixID = packed[key].(int64)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "ModelVersion",
|
||||||
|
In: "query",
|
||||||
|
}
|
||||||
|
params.ModelVersion = packed[key].(int64)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "DisplayName",
|
||||||
|
In: "query",
|
||||||
|
}
|
||||||
|
params.DisplayName = packed[key].(string)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "Creator",
|
||||||
|
In: "query",
|
||||||
|
}
|
||||||
|
params.Creator = packed[key].(string)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "GameID",
|
||||||
|
In: "query",
|
||||||
|
}
|
||||||
|
params.GameID = packed[key].(int32)
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeActionMapfixSubmittedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixSubmittedParams, _ error) {
|
||||||
|
q := uri.NewQueryDecoder(r.URL.Query())
|
||||||
|
// Decode path: MapfixID.
|
||||||
|
if err := func() error {
|
||||||
|
param := args[0]
|
||||||
|
if argsEscaped {
|
||||||
|
unescaped, err := url.PathUnescape(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unescape path")
|
||||||
|
}
|
||||||
|
param = unescaped
|
||||||
|
}
|
||||||
|
if len(param) > 0 {
|
||||||
|
d := uri.NewPathDecoder(uri.PathDecoderConfig{
|
||||||
|
Param: "MapfixID",
|
||||||
|
Value: param,
|
||||||
|
Style: uri.PathStyleSimple,
|
||||||
|
Explode: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := func() error {
|
||||||
|
val, err := d.DecodeValue()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := conv.ToInt64(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
params.MapfixID = c
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := func() error {
|
||||||
|
if err := (validate.Int{
|
||||||
|
MinSet: true,
|
||||||
|
Min: 0,
|
||||||
|
MaxSet: false,
|
||||||
|
Max: 0,
|
||||||
|
MinExclusive: false,
|
||||||
|
MaxExclusive: false,
|
||||||
|
MultipleOfSet: false,
|
||||||
|
MultipleOf: 0,
|
||||||
|
}).Validate(int64(params.MapfixID)); err != nil {
|
||||||
|
return errors.Wrap(err, "int")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return validate.ErrFieldRequired
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return params, &ogenerrors.DecodeParamError{
|
||||||
|
Name: "MapfixID",
|
||||||
|
In: "path",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decode query: ModelVersion.
|
||||||
|
if err := func() error {
|
||||||
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
|
Name: "ModelVersion",
|
||||||
|
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.ModelVersion = c
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := func() error {
|
||||||
|
if err := (validate.Int{
|
||||||
|
MinSet: true,
|
||||||
|
Min: 0,
|
||||||
|
MaxSet: false,
|
||||||
|
Max: 0,
|
||||||
|
MinExclusive: false,
|
||||||
|
MaxExclusive: false,
|
||||||
|
MultipleOfSet: false,
|
||||||
|
MultipleOf: 0,
|
||||||
|
}).Validate(int64(params.ModelVersion)); err != nil {
|
||||||
|
return errors.Wrap(err, "int")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return params, &ogenerrors.DecodeParamError{
|
||||||
|
Name: "ModelVersion",
|
||||||
|
In: "query",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decode query: DisplayName.
|
||||||
|
if err := func() error {
|
||||||
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
|
Name: "DisplayName",
|
||||||
|
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.DisplayName = c
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return 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(params.DisplayName)); 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: "DisplayName",
|
||||||
|
In: "query",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decode query: Creator.
|
||||||
|
if err := func() error {
|
||||||
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
|
Name: "Creator",
|
||||||
|
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.Creator = c
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return 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(params.Creator)); 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: "Creator",
|
||||||
|
In: "query",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decode query: GameID.
|
||||||
|
if err := func() error {
|
||||||
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
|
Name: "GameID",
|
||||||
|
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.ToInt32(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
params.GameID = c
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := func() error {
|
||||||
|
if err := (validate.Int{
|
||||||
|
MinSet: true,
|
||||||
|
Min: 0,
|
||||||
|
MaxSet: false,
|
||||||
|
Max: 0,
|
||||||
|
MinExclusive: false,
|
||||||
|
MaxExclusive: false,
|
||||||
|
MultipleOfSet: false,
|
||||||
|
MultipleOf: 0,
|
||||||
|
}).Validate(int64(params.GameID)); err != nil {
|
||||||
|
return errors.Wrap(err, "int")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return params, &ogenerrors.DecodeParamError{
|
||||||
|
Name: "GameID",
|
||||||
|
In: "query",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
return params, nil
|
return params, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,8 +942,8 @@ func decodeActionOperationFailedParams(args [1]string, argsEscaped bool, r *http
|
|||||||
// ActionSubmissionAcceptedParams is parameters of actionSubmissionAccepted operation.
|
// ActionSubmissionAcceptedParams is parameters of actionSubmissionAccepted operation.
|
||||||
type ActionSubmissionAcceptedParams struct {
|
type ActionSubmissionAcceptedParams struct {
|
||||||
// The unique identifier for a submission.
|
// The unique identifier for a submission.
|
||||||
SubmissionID int64
|
SubmissionID int64
|
||||||
StatusMessage string
|
ErrorMessage string
|
||||||
}
|
}
|
||||||
|
|
||||||
func unpackActionSubmissionAcceptedParams(packed middleware.Parameters) (params ActionSubmissionAcceptedParams) {
|
func unpackActionSubmissionAcceptedParams(packed middleware.Parameters) (params ActionSubmissionAcceptedParams) {
|
||||||
@@ -569,10 +956,10 @@ func unpackActionSubmissionAcceptedParams(packed middleware.Parameters) (params
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
key := middleware.ParameterKey{
|
key := middleware.ParameterKey{
|
||||||
Name: "StatusMessage",
|
Name: "ErrorMessage",
|
||||||
In: "query",
|
In: "query",
|
||||||
}
|
}
|
||||||
params.StatusMessage = packed[key].(string)
|
params.ErrorMessage = packed[key].(string)
|
||||||
}
|
}
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
@@ -641,10 +1028,10 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
|
|||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Decode query: StatusMessage.
|
// Decode query: ErrorMessage.
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
cfg := uri.QueryParameterDecodingConfig{
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
Name: "StatusMessage",
|
Name: "ErrorMessage",
|
||||||
Style: uri.QueryStyleForm,
|
Style: uri.QueryStyleForm,
|
||||||
Explode: true,
|
Explode: true,
|
||||||
}
|
}
|
||||||
@@ -661,7 +1048,7 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
params.StatusMessage = c
|
params.ErrorMessage = c
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -675,7 +1062,7 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
|
|||||||
Email: false,
|
Email: false,
|
||||||
Hostname: false,
|
Hostname: false,
|
||||||
Regex: nil,
|
Regex: nil,
|
||||||
}).Validate(string(params.StatusMessage)); err != nil {
|
}).Validate(string(params.ErrorMessage)); err != nil {
|
||||||
return errors.Wrap(err, "string")
|
return errors.Wrap(err, "string")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -688,7 +1075,7 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
|
|||||||
return nil
|
return nil
|
||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
return params, &ogenerrors.DecodeParamError{
|
return params, &ogenerrors.DecodeParamError{
|
||||||
Name: "StatusMessage",
|
Name: "ErrorMessage",
|
||||||
In: "query",
|
In: "query",
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
@@ -696,13 +1083,14 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
|
|||||||
return params, nil
|
return params, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionSubmissionSubmittedParams is parameters of actionSubmissionSubmitted operation.
|
// ActionSubmissionRequestChangesParams is parameters of actionSubmissionRequestChanges operation.
|
||||||
type ActionSubmissionSubmittedParams struct {
|
type ActionSubmissionRequestChangesParams struct {
|
||||||
// The unique identifier for a submission.
|
// The unique identifier for a submission.
|
||||||
SubmissionID int64
|
SubmissionID int64
|
||||||
|
ErrorMessage string
|
||||||
}
|
}
|
||||||
|
|
||||||
func unpackActionSubmissionSubmittedParams(packed middleware.Parameters) (params ActionSubmissionSubmittedParams) {
|
func unpackActionSubmissionRequestChangesParams(packed middleware.Parameters) (params ActionSubmissionRequestChangesParams) {
|
||||||
{
|
{
|
||||||
key := middleware.ParameterKey{
|
key := middleware.ParameterKey{
|
||||||
Name: "SubmissionID",
|
Name: "SubmissionID",
|
||||||
@@ -710,10 +1098,18 @@ func unpackActionSubmissionSubmittedParams(packed middleware.Parameters) (params
|
|||||||
}
|
}
|
||||||
params.SubmissionID = packed[key].(int64)
|
params.SubmissionID = packed[key].(int64)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "ErrorMessage",
|
||||||
|
In: "query",
|
||||||
|
}
|
||||||
|
params.ErrorMessage = packed[key].(string)
|
||||||
|
}
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeActionSubmissionSubmittedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionSubmittedParams, _ error) {
|
func decodeActionSubmissionRequestChangesParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionRequestChangesParams, _ error) {
|
||||||
|
q := uri.NewQueryDecoder(r.URL.Query())
|
||||||
// Decode path: SubmissionID.
|
// Decode path: SubmissionID.
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
param := args[0]
|
param := args[0]
|
||||||
@@ -776,6 +1172,384 @@ func decodeActionSubmissionSubmittedParams(args [1]string, argsEscaped bool, r *
|
|||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Decode query: ErrorMessage.
|
||||||
|
if err := func() error {
|
||||||
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
|
Name: "ErrorMessage",
|
||||||
|
Style: uri.QueryStyleForm,
|
||||||
|
Explode: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.HasParam(cfg); err == nil {
|
||||||
|
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
|
||||||
|
val, err := d.DecodeValue()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := conv.ToString(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
params.ErrorMessage = c
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := func() error {
|
||||||
|
if err := (validate.String{
|
||||||
|
MinLength: 0,
|
||||||
|
MinLengthSet: true,
|
||||||
|
MaxLength: 4096,
|
||||||
|
MaxLengthSet: true,
|
||||||
|
Email: false,
|
||||||
|
Hostname: false,
|
||||||
|
Regex: nil,
|
||||||
|
}).Validate(string(params.ErrorMessage)); err != nil {
|
||||||
|
return errors.Wrap(err, "string")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return params, &ogenerrors.DecodeParamError{
|
||||||
|
Name: "ErrorMessage",
|
||||||
|
In: "query",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionSubmissionSubmittedParams is parameters of actionSubmissionSubmitted operation.
|
||||||
|
type ActionSubmissionSubmittedParams struct {
|
||||||
|
// The unique identifier for a submission.
|
||||||
|
SubmissionID int64
|
||||||
|
ModelVersion int64
|
||||||
|
DisplayName string
|
||||||
|
Creator string
|
||||||
|
GameID int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func unpackActionSubmissionSubmittedParams(packed middleware.Parameters) (params ActionSubmissionSubmittedParams) {
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "SubmissionID",
|
||||||
|
In: "path",
|
||||||
|
}
|
||||||
|
params.SubmissionID = packed[key].(int64)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "ModelVersion",
|
||||||
|
In: "query",
|
||||||
|
}
|
||||||
|
params.ModelVersion = packed[key].(int64)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "DisplayName",
|
||||||
|
In: "query",
|
||||||
|
}
|
||||||
|
params.DisplayName = packed[key].(string)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "Creator",
|
||||||
|
In: "query",
|
||||||
|
}
|
||||||
|
params.Creator = packed[key].(string)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key := middleware.ParameterKey{
|
||||||
|
Name: "GameID",
|
||||||
|
In: "query",
|
||||||
|
}
|
||||||
|
params.GameID = packed[key].(int32)
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeActionSubmissionSubmittedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionSubmittedParams, _ error) {
|
||||||
|
q := uri.NewQueryDecoder(r.URL.Query())
|
||||||
|
// Decode path: SubmissionID.
|
||||||
|
if err := func() error {
|
||||||
|
param := args[0]
|
||||||
|
if argsEscaped {
|
||||||
|
unescaped, err := url.PathUnescape(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unescape path")
|
||||||
|
}
|
||||||
|
param = unescaped
|
||||||
|
}
|
||||||
|
if len(param) > 0 {
|
||||||
|
d := uri.NewPathDecoder(uri.PathDecoderConfig{
|
||||||
|
Param: "SubmissionID",
|
||||||
|
Value: param,
|
||||||
|
Style: uri.PathStyleSimple,
|
||||||
|
Explode: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := func() error {
|
||||||
|
val, err := d.DecodeValue()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := conv.ToInt64(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
params.SubmissionID = c
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := func() error {
|
||||||
|
if err := (validate.Int{
|
||||||
|
MinSet: true,
|
||||||
|
Min: 0,
|
||||||
|
MaxSet: false,
|
||||||
|
Max: 0,
|
||||||
|
MinExclusive: false,
|
||||||
|
MaxExclusive: false,
|
||||||
|
MultipleOfSet: false,
|
||||||
|
MultipleOf: 0,
|
||||||
|
}).Validate(int64(params.SubmissionID)); err != nil {
|
||||||
|
return errors.Wrap(err, "int")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return validate.ErrFieldRequired
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return params, &ogenerrors.DecodeParamError{
|
||||||
|
Name: "SubmissionID",
|
||||||
|
In: "path",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decode query: ModelVersion.
|
||||||
|
if err := func() error {
|
||||||
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
|
Name: "ModelVersion",
|
||||||
|
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.ModelVersion = c
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := func() error {
|
||||||
|
if err := (validate.Int{
|
||||||
|
MinSet: true,
|
||||||
|
Min: 0,
|
||||||
|
MaxSet: false,
|
||||||
|
Max: 0,
|
||||||
|
MinExclusive: false,
|
||||||
|
MaxExclusive: false,
|
||||||
|
MultipleOfSet: false,
|
||||||
|
MultipleOf: 0,
|
||||||
|
}).Validate(int64(params.ModelVersion)); err != nil {
|
||||||
|
return errors.Wrap(err, "int")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return params, &ogenerrors.DecodeParamError{
|
||||||
|
Name: "ModelVersion",
|
||||||
|
In: "query",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decode query: DisplayName.
|
||||||
|
if err := func() error {
|
||||||
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
|
Name: "DisplayName",
|
||||||
|
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.DisplayName = c
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return 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(params.DisplayName)); 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: "DisplayName",
|
||||||
|
In: "query",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decode query: Creator.
|
||||||
|
if err := func() error {
|
||||||
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
|
Name: "Creator",
|
||||||
|
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.Creator = c
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return 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(params.Creator)); 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: "Creator",
|
||||||
|
In: "query",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decode query: GameID.
|
||||||
|
if err := func() error {
|
||||||
|
cfg := uri.QueryParameterDecodingConfig{
|
||||||
|
Name: "GameID",
|
||||||
|
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.ToInt32(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
params.GameID = c
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := func() error {
|
||||||
|
if err := (validate.Int{
|
||||||
|
MinSet: true,
|
||||||
|
Min: 0,
|
||||||
|
MaxSet: false,
|
||||||
|
Max: 0,
|
||||||
|
MinExclusive: false,
|
||||||
|
MaxExclusive: false,
|
||||||
|
MultipleOfSet: false,
|
||||||
|
MultipleOf: 0,
|
||||||
|
}).Validate(int64(params.GameID)); err != nil {
|
||||||
|
return errors.Wrap(err, "int")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return params, &ogenerrors.DecodeParamError{
|
||||||
|
Name: "GameID",
|
||||||
|
In: "query",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
return params, nil
|
return params, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
"github.com/go-faster/errors"
|
"github.com/go-faster/errors"
|
||||||
"github.com/go-faster/jx"
|
"github.com/go-faster/jx"
|
||||||
"go.uber.org/multierr"
|
|
||||||
|
|
||||||
"github.com/ogen-go/ogen/ogenerrors"
|
"github.com/ogen-go/ogen/ogenerrors"
|
||||||
"github.com/ogen-go/ogen/validate"
|
"github.com/ogen-go/ogen/validate"
|
||||||
@@ -26,13 +25,13 @@ func (s *Server) decodeCreateMapfixRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
@@ -97,13 +96,13 @@ func (s *Server) decodeCreateScriptRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
@@ -168,13 +167,13 @@ func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
@@ -239,13 +238,13 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
|
|||||||
// Close in reverse order, to match defer behavior.
|
// Close in reverse order, to match defer behavior.
|
||||||
for i := len(closers) - 1; i >= 0; i-- {
|
for i := len(closers) - 1; i >= 0; i-- {
|
||||||
c := closers[i]
|
c := closers[i]
|
||||||
merr = multierr.Append(merr, c())
|
merr = errors.Join(merr, c())
|
||||||
}
|
}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
rerr = multierr.Append(rerr, close())
|
rerr = errors.Join(rerr, close())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||||
|
|||||||
@@ -75,6 +75,66 @@ func decodeActionMapfixAcceptedResponse(resp *http.Response) (res *ActionMapfixA
|
|||||||
return res, errors.Wrap(defRes, "error")
|
return res, errors.Wrap(defRes, "error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeActionMapfixRequestChangesResponse(resp *http.Response) (res *ActionMapfixRequestChangesNoContent, _ error) {
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case 204:
|
||||||
|
// Code 204.
|
||||||
|
return &ActionMapfixRequestChangesNoContent{}, nil
|
||||||
|
}
|
||||||
|
// Convenient error response.
|
||||||
|
defRes, err := func() (res *ErrorStatusCode, err error) {
|
||||||
|
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "parse media type")
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case ct == "application/json":
|
||||||
|
buf, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
d := jx.DecodeBytes(buf)
|
||||||
|
|
||||||
|
var response Error
|
||||||
|
if err := func() error {
|
||||||
|
if err := response.Decode(d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Skip(); err != io.EOF {
|
||||||
|
return errors.New("unexpected trailing data")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
err = &ogenerrors.DecodeBodyError{
|
||||||
|
ContentType: ct,
|
||||||
|
Body: buf,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
// Validate response.
|
||||||
|
if err := func() error {
|
||||||
|
if err := response.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return res, errors.Wrap(err, "validate")
|
||||||
|
}
|
||||||
|
return &ErrorStatusCode{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Response: response,
|
||||||
|
}, nil
|
||||||
|
default:
|
||||||
|
return res, validate.InvalidContentType(ct)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return res, errors.Wrap(defRes, "error")
|
||||||
|
}
|
||||||
|
|
||||||
func decodeActionMapfixSubmittedResponse(resp *http.Response) (res *ActionMapfixSubmittedNoContent, _ error) {
|
func decodeActionMapfixSubmittedResponse(resp *http.Response) (res *ActionMapfixSubmittedNoContent, _ error) {
|
||||||
switch resp.StatusCode {
|
switch resp.StatusCode {
|
||||||
case 204:
|
case 204:
|
||||||
@@ -375,6 +435,66 @@ func decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSub
|
|||||||
return res, errors.Wrap(defRes, "error")
|
return res, errors.Wrap(defRes, "error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeActionSubmissionRequestChangesResponse(resp *http.Response) (res *ActionSubmissionRequestChangesNoContent, _ error) {
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case 204:
|
||||||
|
// Code 204.
|
||||||
|
return &ActionSubmissionRequestChangesNoContent{}, nil
|
||||||
|
}
|
||||||
|
// Convenient error response.
|
||||||
|
defRes, err := func() (res *ErrorStatusCode, err error) {
|
||||||
|
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrap(err, "parse media type")
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case ct == "application/json":
|
||||||
|
buf, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
d := jx.DecodeBytes(buf)
|
||||||
|
|
||||||
|
var response Error
|
||||||
|
if err := func() error {
|
||||||
|
if err := response.Decode(d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Skip(); err != io.EOF {
|
||||||
|
return errors.New("unexpected trailing data")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
err = &ogenerrors.DecodeBodyError{
|
||||||
|
ContentType: ct,
|
||||||
|
Body: buf,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
// Validate response.
|
||||||
|
if err := func() error {
|
||||||
|
if err := response.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return res, errors.Wrap(err, "validate")
|
||||||
|
}
|
||||||
|
return &ErrorStatusCode{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Response: response,
|
||||||
|
}, nil
|
||||||
|
default:
|
||||||
|
return res, validate.InvalidContentType(ct)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return res, errors.Wrap(defRes, "error")
|
||||||
|
}
|
||||||
|
|
||||||
func decodeActionSubmissionSubmittedResponse(resp *http.Response) (res *ActionSubmissionSubmittedNoContent, _ error) {
|
func decodeActionSubmissionSubmittedResponse(resp *http.Response) (res *ActionSubmissionSubmittedNoContent, _ error) {
|
||||||
switch resp.StatusCode {
|
switch resp.StatusCode {
|
||||||
case 204:
|
case 204:
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ func encodeActionMapfixAcceptedResponse(response *ActionMapfixAcceptedNoContent,
|
|||||||
return nil
|
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 encodeActionMapfixSubmittedResponse(response *ActionMapfixSubmittedNoContent, w http.ResponseWriter, span trace.Span) error {
|
func encodeActionMapfixSubmittedResponse(response *ActionMapfixSubmittedNoContent, w http.ResponseWriter, span trace.Span) error {
|
||||||
w.WriteHeader(204)
|
w.WriteHeader(204)
|
||||||
span.SetStatus(codes.Ok, http.StatusText(204))
|
span.SetStatus(codes.Ok, http.StatusText(204))
|
||||||
@@ -55,6 +62,13 @@ func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeActionSubmissionRequestChangesResponse(response *ActionSubmissionRequestChangesNoContent, w http.ResponseWriter, span trace.Span) error {
|
||||||
|
w.WriteHeader(204)
|
||||||
|
span.SetStatus(codes.Ok, http.StatusText(204))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func encodeActionSubmissionSubmittedResponse(response *ActionSubmissionSubmittedNoContent, w http.ResponseWriter, span trace.Span) error {
|
func encodeActionSubmissionSubmittedResponse(response *ActionSubmissionSubmittedNoContent, w http.ResponseWriter, span trace.Span) error {
|
||||||
w.WriteHeader(204)
|
w.WriteHeader(204)
|
||||||
span.SetStatus(codes.Ok, http.StatusText(204))
|
span.SetStatus(codes.Ok, http.StatusText(204))
|
||||||
|
|||||||
@@ -147,6 +147,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'r': // Prefix: "request-changes"
|
||||||
|
|
||||||
|
if l := len("request-changes"); len(elem) >= l && elem[0:l] == "request-changes" {
|
||||||
|
elem = elem[l:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch r.Method {
|
||||||
|
case "POST":
|
||||||
|
s.handleActionMapfixRequestChangesRequest([1]string{
|
||||||
|
args[0],
|
||||||
|
}, elemIsEscaped, w, r)
|
||||||
|
default:
|
||||||
|
s.notAllowed(w, r, "POST")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
case 's': // Prefix: "submitted"
|
case 's': // Prefix: "submitted"
|
||||||
|
|
||||||
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
|
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
|
||||||
@@ -476,6 +498,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'r': // Prefix: "request-changes"
|
||||||
|
|
||||||
|
if l := len("request-changes"); len(elem) >= l && elem[0:l] == "request-changes" {
|
||||||
|
elem = elem[l:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch r.Method {
|
||||||
|
case "POST":
|
||||||
|
s.handleActionSubmissionRequestChangesRequest([1]string{
|
||||||
|
args[0],
|
||||||
|
}, elemIsEscaped, w, r)
|
||||||
|
default:
|
||||||
|
s.notAllowed(w, r, "POST")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
case 's': // Prefix: "submitted"
|
case 's': // Prefix: "submitted"
|
||||||
|
|
||||||
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
|
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
|
||||||
@@ -760,6 +804,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'r': // Prefix: "request-changes"
|
||||||
|
|
||||||
|
if l := len("request-changes"); len(elem) >= l && elem[0:l] == "request-changes" {
|
||||||
|
elem = elem[l:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch method {
|
||||||
|
case "POST":
|
||||||
|
r.name = ActionMapfixRequestChangesOperation
|
||||||
|
r.summary = "(Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested"
|
||||||
|
r.operationID = "actionMapfixRequestChanges"
|
||||||
|
r.pathPattern = "/mapfixes/{MapfixID}/status/validator-request-changes"
|
||||||
|
r.args = args
|
||||||
|
r.count = 1
|
||||||
|
return r, true
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case 's': // Prefix: "submitted"
|
case 's': // Prefix: "submitted"
|
||||||
|
|
||||||
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
|
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
|
||||||
@@ -1127,6 +1195,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'r': // Prefix: "request-changes"
|
||||||
|
|
||||||
|
if l := len("request-changes"); len(elem) >= l && elem[0:l] == "request-changes" {
|
||||||
|
elem = elem[l:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(elem) == 0 {
|
||||||
|
// Leaf node.
|
||||||
|
switch method {
|
||||||
|
case "POST":
|
||||||
|
r.name = ActionSubmissionRequestChangesOperation
|
||||||
|
r.summary = "(Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested"
|
||||||
|
r.operationID = "actionSubmissionRequestChanges"
|
||||||
|
r.pathPattern = "/submissions/{SubmissionID}/status/validator-request-changes"
|
||||||
|
r.args = args
|
||||||
|
r.count = 1
|
||||||
|
return r, true
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case 's': // Prefix: "submitted"
|
case 's': // Prefix: "submitted"
|
||||||
|
|
||||||
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
|
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ func (s *ErrorStatusCode) Error() string {
|
|||||||
// ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation.
|
// ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation.
|
||||||
type ActionMapfixAcceptedNoContent struct{}
|
type ActionMapfixAcceptedNoContent struct{}
|
||||||
|
|
||||||
|
// ActionMapfixRequestChangesNoContent is response for ActionMapfixRequestChanges operation.
|
||||||
|
type ActionMapfixRequestChangesNoContent struct{}
|
||||||
|
|
||||||
// ActionMapfixSubmittedNoContent is response for ActionMapfixSubmitted operation.
|
// ActionMapfixSubmittedNoContent is response for ActionMapfixSubmitted operation.
|
||||||
type ActionMapfixSubmittedNoContent struct{}
|
type ActionMapfixSubmittedNoContent struct{}
|
||||||
|
|
||||||
@@ -28,6 +31,9 @@ type ActionOperationFailedNoContent struct{}
|
|||||||
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
|
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
|
||||||
type ActionSubmissionAcceptedNoContent struct{}
|
type ActionSubmissionAcceptedNoContent struct{}
|
||||||
|
|
||||||
|
// ActionSubmissionRequestChangesNoContent is response for ActionSubmissionRequestChanges operation.
|
||||||
|
type ActionSubmissionRequestChangesNoContent struct{}
|
||||||
|
|
||||||
// ActionSubmissionSubmittedNoContent is response for ActionSubmissionSubmitted operation.
|
// ActionSubmissionSubmittedNoContent is response for ActionSubmissionSubmitted operation.
|
||||||
type ActionSubmissionSubmittedNoContent struct{}
|
type ActionSubmissionSubmittedNoContent struct{}
|
||||||
|
|
||||||
@@ -100,6 +106,7 @@ type MapfixCreate struct {
|
|||||||
AssetID int64 `json:"AssetID"`
|
AssetID int64 `json:"AssetID"`
|
||||||
AssetVersion int64 `json:"AssetVersion"`
|
AssetVersion int64 `json:"AssetVersion"`
|
||||||
TargetAssetID int64 `json:"TargetAssetID"`
|
TargetAssetID int64 `json:"TargetAssetID"`
|
||||||
|
Description string `json:"Description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOperationID returns the value of OperationID.
|
// GetOperationID returns the value of OperationID.
|
||||||
@@ -142,6 +149,11 @@ func (s *MapfixCreate) GetTargetAssetID() int64 {
|
|||||||
return s.TargetAssetID
|
return s.TargetAssetID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDescription returns the value of Description.
|
||||||
|
func (s *MapfixCreate) GetDescription() string {
|
||||||
|
return s.Description
|
||||||
|
}
|
||||||
|
|
||||||
// SetOperationID sets the value of OperationID.
|
// SetOperationID sets the value of OperationID.
|
||||||
func (s *MapfixCreate) SetOperationID(val int32) {
|
func (s *MapfixCreate) SetOperationID(val int32) {
|
||||||
s.OperationID = val
|
s.OperationID = val
|
||||||
@@ -182,6 +194,11 @@ func (s *MapfixCreate) SetTargetAssetID(val int64) {
|
|||||||
s.TargetAssetID = val
|
s.TargetAssetID = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDescription sets the value of Description.
|
||||||
|
func (s *MapfixCreate) SetDescription(val string) {
|
||||||
|
s.Description = val
|
||||||
|
}
|
||||||
|
|
||||||
// Ref: #/components/schemas/MapfixID
|
// Ref: #/components/schemas/MapfixID
|
||||||
type MapfixID struct {
|
type MapfixID struct {
|
||||||
MapfixID int64 `json:"MapfixID"`
|
MapfixID int64 `json:"MapfixID"`
|
||||||
@@ -577,6 +594,8 @@ type SubmissionCreate struct {
|
|||||||
GameID int32 `json:"GameID"`
|
GameID int32 `json:"GameID"`
|
||||||
AssetID int64 `json:"AssetID"`
|
AssetID int64 `json:"AssetID"`
|
||||||
AssetVersion int64 `json:"AssetVersion"`
|
AssetVersion int64 `json:"AssetVersion"`
|
||||||
|
Status uint32 `json:"Status"`
|
||||||
|
Roles uint32 `json:"Roles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOperationID returns the value of OperationID.
|
// GetOperationID returns the value of OperationID.
|
||||||
@@ -614,6 +633,16 @@ func (s *SubmissionCreate) GetAssetVersion() int64 {
|
|||||||
return s.AssetVersion
|
return s.AssetVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStatus returns the value of Status.
|
||||||
|
func (s *SubmissionCreate) GetStatus() uint32 {
|
||||||
|
return s.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoles returns the value of Roles.
|
||||||
|
func (s *SubmissionCreate) GetRoles() uint32 {
|
||||||
|
return s.Roles
|
||||||
|
}
|
||||||
|
|
||||||
// SetOperationID sets the value of OperationID.
|
// SetOperationID sets the value of OperationID.
|
||||||
func (s *SubmissionCreate) SetOperationID(val int32) {
|
func (s *SubmissionCreate) SetOperationID(val int32) {
|
||||||
s.OperationID = val
|
s.OperationID = val
|
||||||
@@ -649,6 +678,16 @@ func (s *SubmissionCreate) SetAssetVersion(val int64) {
|
|||||||
s.AssetVersion = val
|
s.AssetVersion = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetStatus sets the value of Status.
|
||||||
|
func (s *SubmissionCreate) SetStatus(val uint32) {
|
||||||
|
s.Status = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRoles sets the value of Roles.
|
||||||
|
func (s *SubmissionCreate) SetRoles(val uint32) {
|
||||||
|
s.Roles = val
|
||||||
|
}
|
||||||
|
|
||||||
// Ref: #/components/schemas/SubmissionID
|
// Ref: #/components/schemas/SubmissionID
|
||||||
type SubmissionID struct {
|
type SubmissionID struct {
|
||||||
SubmissionID int64 `json:"SubmissionID"`
|
SubmissionID int64 `json:"SubmissionID"`
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ type Handler interface {
|
|||||||
//
|
//
|
||||||
// POST /mapfixes/{MapfixID}/status/validator-failed
|
// POST /mapfixes/{MapfixID}/status/validator-failed
|
||||||
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
|
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
|
||||||
|
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
|
||||||
|
//
|
||||||
|
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
|
||||||
|
//
|
||||||
|
// POST /mapfixes/{MapfixID}/status/validator-request-changes
|
||||||
|
ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error
|
||||||
// ActionMapfixSubmitted implements actionMapfixSubmitted operation.
|
// ActionMapfixSubmitted implements actionMapfixSubmitted operation.
|
||||||
//
|
//
|
||||||
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
||||||
@@ -44,6 +50,12 @@ type Handler interface {
|
|||||||
//
|
//
|
||||||
// POST /submissions/{SubmissionID}/status/validator-failed
|
// POST /submissions/{SubmissionID}/status/validator-failed
|
||||||
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
|
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
|
||||||
|
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
|
||||||
|
//
|
||||||
|
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
|
||||||
|
//
|
||||||
|
// POST /submissions/{SubmissionID}/status/validator-request-changes
|
||||||
|
ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error
|
||||||
// ActionSubmissionSubmitted implements actionSubmissionSubmitted operation.
|
// ActionSubmissionSubmitted implements actionSubmissionSubmitted operation.
|
||||||
//
|
//
|
||||||
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
||||||
|
|||||||
@@ -22,6 +22,15 @@ func (UnimplementedHandler) ActionMapfixAccepted(ctx context.Context, params Act
|
|||||||
return ht.ErrNotImplemented
|
return ht.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
|
||||||
|
//
|
||||||
|
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
|
||||||
|
//
|
||||||
|
// POST /mapfixes/{MapfixID}/status/validator-request-changes
|
||||||
|
func (UnimplementedHandler) ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error {
|
||||||
|
return ht.ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
// ActionMapfixSubmitted implements actionMapfixSubmitted operation.
|
// ActionMapfixSubmitted implements actionMapfixSubmitted operation.
|
||||||
//
|
//
|
||||||
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
||||||
@@ -67,6 +76,15 @@ func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params
|
|||||||
return ht.ErrNotImplemented
|
return ht.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
|
||||||
|
//
|
||||||
|
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
|
||||||
|
//
|
||||||
|
// POST /submissions/{SubmissionID}/status/validator-request-changes
|
||||||
|
func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error {
|
||||||
|
return ht.ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
// ActionSubmissionSubmitted implements actionSubmissionSubmitted operation.
|
// ActionSubmissionSubmitted implements actionSubmissionSubmitted operation.
|
||||||
//
|
//
|
||||||
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
|
||||||
|
|||||||
@@ -227,6 +227,25 @@ func (s *MapfixCreate) Validate() error {
|
|||||||
Error: err,
|
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.Description)); err != nil {
|
||||||
|
return errors.Wrap(err, "string")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
failures = append(failures, validate.FieldError{
|
||||||
|
Name: "Description",
|
||||||
|
Error: err,
|
||||||
|
})
|
||||||
|
}
|
||||||
if len(failures) > 0 {
|
if len(failures) > 0 {
|
||||||
return &validate.Error{Fields: failures}
|
return &validate.Error{Fields: failures}
|
||||||
}
|
}
|
||||||
@@ -862,6 +881,26 @@ func (s *SubmissionCreate) Validate() error {
|
|||||||
Error: err,
|
Error: err,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if err := func() error {
|
||||||
|
if err := (validate.Int{
|
||||||
|
MinSet: true,
|
||||||
|
Min: 0,
|
||||||
|
MaxSet: true,
|
||||||
|
Max: 9,
|
||||||
|
MinExclusive: false,
|
||||||
|
MaxExclusive: false,
|
||||||
|
MultipleOfSet: false,
|
||||||
|
MultipleOf: 0,
|
||||||
|
}).Validate(int64(s.Status)); err != nil {
|
||||||
|
return errors.Wrap(err, "int")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
failures = append(failures, validate.FieldError{
|
||||||
|
Name: "Status",
|
||||||
|
Error: err,
|
||||||
|
})
|
||||||
|
}
|
||||||
if len(failures) > 0 {
|
if len(failures) > 0 {
|
||||||
return &validate.Error{Fields: failures}
|
return &validate.Error{Fields: failures}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ type AuditEventDataChangeName struct {
|
|||||||
NewName string `json:"new_name"`
|
NewName string `json:"new_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validator had an error
|
||||||
|
const AuditEventTypeError AuditEventType = 6
|
||||||
|
type AuditEventDataError struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
type AuditEvent struct {
|
type AuditEvent struct {
|
||||||
ID int64 `gorm:"primaryKey"`
|
ID int64 `gorm:"primaryKey"`
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
|
|||||||
@@ -39,5 +39,5 @@ type Mapfix struct {
|
|||||||
Completed bool // Has this version of the map been completed at least once on maptest
|
Completed bool // Has this version of the map been completed at least once on maptest
|
||||||
TargetAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
|
TargetAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
|
||||||
StatusID MapfixStatus
|
StatusID MapfixStatus
|
||||||
StatusMessage string
|
Description string // mapfix description
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,29 @@ type CreateSubmissionRequest struct {
|
|||||||
// operation_id is passed back in the response message
|
// operation_id is passed back in the response message
|
||||||
OperationID int32
|
OperationID int32
|
||||||
ModelID uint64
|
ModelID uint64
|
||||||
|
DisplayName string
|
||||||
|
Creator string
|
||||||
|
GameID uint32
|
||||||
|
Status uint32
|
||||||
|
Roles uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateMapfixRequest struct {
|
type CreateMapfixRequest struct {
|
||||||
OperationID int32
|
OperationID int32
|
||||||
ModelID uint64
|
ModelID uint64
|
||||||
TargetAssetID uint64
|
TargetAssetID uint64
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type CheckSubmissionRequest struct{
|
||||||
|
SubmissionID int64
|
||||||
|
ModelID uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckMapfixRequest struct{
|
||||||
|
MapfixID int64
|
||||||
|
ModelID uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidateSubmissionRequest struct {
|
type ValidateSubmissionRequest struct {
|
||||||
|
|||||||
@@ -40,5 +40,4 @@ type Submission struct {
|
|||||||
Completed bool // Has this version of the map been completed at least once on maptest
|
Completed bool // Has this version of the map been completed at least once on maptest
|
||||||
UploadedAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
|
UploadedAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
|
||||||
StatusID SubmissionStatus
|
StatusID SubmissionStatus
|
||||||
StatusMessage string
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"git.itzana.me/strafesnet/go-grpc/users"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
|
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/model"
|
"git.itzana.me/strafesnet/maps-service/pkg/model"
|
||||||
@@ -24,15 +25,24 @@ func (svc *Service) CreateMapfixAuditComment(ctx context.Context, req api.Create
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !has_role {
|
|
||||||
return ErrPermissionDeniedNeedRoleMapfixReview
|
|
||||||
}
|
|
||||||
|
|
||||||
userId, err := userInfo.GetUserID()
|
userId, err := userInfo.GetUserID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !has_role {
|
||||||
|
// Submitter has special permission to comment on their mapfix
|
||||||
|
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if mapfix.Submitter != userId {
|
||||||
|
return ErrPermissionDeniedNeedRoleMapfixReview
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data := []byte{}
|
data := []byte{}
|
||||||
_, err = req.Read(data)
|
_, err = req.Read(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -83,6 +93,27 @@ func (svc *Service) ListMapfixAuditEvents(ctx context.Context, params api.ListMa
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idMap := make(map[int64]bool)
|
||||||
|
for _, item := range items {
|
||||||
|
idMap[int64(item.User)] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var idList users.IdList
|
||||||
|
idList.ID = make([]int64,len(idMap))
|
||||||
|
for userId := range idMap {
|
||||||
|
idList.ID = append(idList.ID, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
userList, err := svc.Users.GetList(ctx, &idList)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userMap := make(map[int64]*users.UserResponse)
|
||||||
|
for _,user := range userList.Users {
|
||||||
|
userMap[user.ID] = user
|
||||||
|
}
|
||||||
|
|
||||||
var resp []api.AuditEvent
|
var resp []api.AuditEvent
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
EventData := api.AuditEventEventData{}
|
EventData := api.AuditEventEventData{}
|
||||||
@@ -90,10 +121,15 @@ func (svc *Service) ListMapfixAuditEvents(ctx context.Context, params api.ListMa
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
username := ""
|
||||||
|
if userMap[int64(item.User)] != nil {
|
||||||
|
username = userMap[int64(item.User)].Username
|
||||||
|
}
|
||||||
resp = append(resp, api.AuditEvent{
|
resp = append(resp, api.AuditEvent{
|
||||||
ID: item.ID,
|
ID: item.ID,
|
||||||
Date: item.CreatedAt.Unix(),
|
Date: item.CreatedAt.Unix(),
|
||||||
User: int64(item.User),
|
User: int64(item.User),
|
||||||
|
Username: username,
|
||||||
ResourceType: int32(item.ResourceType),
|
ResourceType: int32(item.ResourceType),
|
||||||
ResourceID: item.ResourceID,
|
ResourceID: item.ResourceID,
|
||||||
EventType: int32(item.EventType),
|
EventType: int32(item.EventType),
|
||||||
@@ -119,15 +155,24 @@ func (svc *Service) CreateSubmissionAuditComment(ctx context.Context, req api.Cr
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !has_role {
|
|
||||||
return ErrPermissionDeniedNeedRoleSubmissionReview
|
|
||||||
}
|
|
||||||
|
|
||||||
userId, err := userInfo.GetUserID()
|
userId, err := userInfo.GetUserID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !has_role {
|
||||||
|
// Submitter has special permission to comment on their submission
|
||||||
|
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if submission.Submitter != userId {
|
||||||
|
return ErrPermissionDeniedNeedRoleSubmissionReview
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data := []byte{}
|
data := []byte{}
|
||||||
_, err = req.Read(data)
|
_, err = req.Read(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -178,6 +223,27 @@ func (svc *Service) ListSubmissionAuditEvents(ctx context.Context, params api.Li
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idMap := make(map[int64]bool)
|
||||||
|
for _, item := range items {
|
||||||
|
idMap[int64(item.User)] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var idList users.IdList
|
||||||
|
idList.ID = make([]int64,len(idMap))
|
||||||
|
for userId := range idMap {
|
||||||
|
idList.ID = append(idList.ID, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
userList, err := svc.Users.GetList(ctx, &idList)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userMap := make(map[int64]*users.UserResponse)
|
||||||
|
for _,user := range userList.Users {
|
||||||
|
userMap[user.ID] = user
|
||||||
|
}
|
||||||
|
|
||||||
var resp []api.AuditEvent
|
var resp []api.AuditEvent
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
EventData := api.AuditEventEventData{}
|
EventData := api.AuditEventEventData{}
|
||||||
@@ -185,10 +251,15 @@ func (svc *Service) ListSubmissionAuditEvents(ctx context.Context, params api.Li
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
username := ""
|
||||||
|
if userMap[int64(item.User)] != nil {
|
||||||
|
username = userMap[int64(item.User)].Username
|
||||||
|
}
|
||||||
resp = append(resp, api.AuditEvent{
|
resp = append(resp, api.AuditEvent{
|
||||||
ID: item.ID,
|
ID: item.ID,
|
||||||
Date: item.CreatedAt.Unix(),
|
Date: item.CreatedAt.Unix(),
|
||||||
User: int64(item.User),
|
User: int64(item.User),
|
||||||
|
Username: username,
|
||||||
ResourceType: int32(item.ResourceType),
|
ResourceType: int32(item.ResourceType),
|
||||||
ResourceID: item.ResourceID,
|
ResourceID: item.ResourceID,
|
||||||
EventType: int32(item.EventType),
|
EventType: int32(item.EventType),
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
|
|||||||
|
|
||||||
// Check if TargetAssetID actually exists
|
// Check if TargetAssetID actually exists
|
||||||
{
|
{
|
||||||
_, err := svc.Client.Get(ctx, &maps.IdMessage{
|
_, err := svc.Maps.Get(ctx, &maps.IdMessage{
|
||||||
ID: request.TargetAssetID,
|
ID: request.TargetAssetID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -114,6 +114,7 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
|
|||||||
OperationID: operation.ID,
|
OperationID: operation.ID,
|
||||||
ModelID: ModelID,
|
ModelID: ModelID,
|
||||||
TargetAssetID: TargetAssetID,
|
TargetAssetID: TargetAssetID,
|
||||||
|
Description: request.Description,
|
||||||
}
|
}
|
||||||
|
|
||||||
j, err := json.Marshal(create_request)
|
j, err := json.Marshal(create_request)
|
||||||
@@ -154,7 +155,7 @@ func (svc *Service) GetMapfix(ctx context.Context, params api.GetMapfixParams) (
|
|||||||
Completed: mapfix.Completed,
|
Completed: mapfix.Completed,
|
||||||
TargetAssetID: int64(mapfix.TargetAssetID),
|
TargetAssetID: int64(mapfix.TargetAssetID),
|
||||||
StatusID: int32(mapfix.StatusID),
|
StatusID: int32(mapfix.StatusID),
|
||||||
StatusMessage: mapfix.StatusMessage,
|
Description: mapfix.Description,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +215,7 @@ func (svc *Service) ListMapfixes(ctx context.Context, params api.ListMapfixesPar
|
|||||||
Completed: item.Completed,
|
Completed: item.Completed,
|
||||||
TargetAssetID: int64(item.TargetAssetID),
|
TargetAssetID: int64(item.TargetAssetID),
|
||||||
StatusID: int32(item.StatusID),
|
StatusID: int32(item.StatusID),
|
||||||
|
Description: item.Description,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,7 +492,7 @@ func (svc *Service) ActionMapfixTriggerSubmit(ctx context.Context, params api.Ac
|
|||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
target_status := model.MapfixStatusSubmitted
|
target_status := model.MapfixStatusSubmitting
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id", target_status)
|
smap.Add("status_id", target_status)
|
||||||
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUnderConstruction, model.MapfixStatusChangesRequested}, smap)
|
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUnderConstruction, model.MapfixStatusChangesRequested}, smap)
|
||||||
@@ -498,6 +500,91 @@ func (svc *Service) ActionMapfixTriggerSubmit(ctx context.Context, params api.Ac
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validate_request := model.CheckMapfixRequest{
|
||||||
|
MapfixID: mapfix.ID,
|
||||||
|
ModelID: mapfix.AssetID,
|
||||||
|
}
|
||||||
|
|
||||||
|
j, err := json.Marshal(validate_request)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = svc.Nats.Publish("maptest.mapfixes.check", []byte(j))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
event_data := model.AuditEventDataAction{
|
||||||
|
TargetStatus: uint32(target_status),
|
||||||
|
}
|
||||||
|
|
||||||
|
EventData, err := json.Marshal(event_data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
|
||||||
|
ID: 0,
|
||||||
|
User: userId,
|
||||||
|
ResourceType: model.ResourceMapfix,
|
||||||
|
ResourceID: params.MapfixID,
|
||||||
|
EventType: model.AuditEventTypeAction,
|
||||||
|
EventData: EventData,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionMapfixBypassSubmit invokes actionMapfixBypassSubmit operation.
|
||||||
|
//
|
||||||
|
// Role Reviewer changes status from ChangesRequested -> Submitted.
|
||||||
|
//
|
||||||
|
// POST /mapfixes/{MapfixID}/status/bypass-submit
|
||||||
|
func (svc *Service) ActionMapfixBypassSubmit(ctx context.Context, params api.ActionMapfixBypassSubmitParams) 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
|
||||||
|
}
|
||||||
|
|
||||||
|
userId, err := userInfo.GetUserID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if caller is the submitter
|
||||||
|
is_submitter := userId == mapfix.Submitter
|
||||||
|
if is_submitter {
|
||||||
|
return ErrAcceptOwnMapfix
|
||||||
|
}
|
||||||
|
|
||||||
|
has_mapfix_review, err := userInfo.HasRoleMapfixReview()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has_mapfix_review {
|
||||||
|
return ErrPermissionDeniedNeedRoleMapfixReview
|
||||||
|
}
|
||||||
|
|
||||||
|
// transaction
|
||||||
|
target_status := model.MapfixStatusSubmitted
|
||||||
|
smap := datastore.Optional()
|
||||||
|
smap.Add("status_id", target_status)
|
||||||
|
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusChangesRequested}, smap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
event_data := model.AuditEventDataAction{
|
event_data := model.AuditEventDataAction{
|
||||||
TargetStatus: uint32(target_status),
|
TargetStatus: uint32(target_status),
|
||||||
}
|
}
|
||||||
@@ -558,7 +645,6 @@ func (svc *Service) ActionMapfixResetSubmitting(ctx context.Context, params api.
|
|||||||
target_status := model.MapfixStatusUnderConstruction
|
target_status := model.MapfixStatusUnderConstruction
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id", target_status)
|
smap.Add("status_id", target_status)
|
||||||
smap.Add("status_message", "Manually forced reset")
|
|
||||||
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap)
|
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -960,7 +1046,6 @@ func (svc *Service) ActionMapfixAccepted(ctx context.Context, params api.ActionM
|
|||||||
target_status := model.MapfixStatusAcceptedUnvalidated
|
target_status := model.MapfixStatusAcceptedUnvalidated
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id", target_status)
|
smap.Add("status_id", target_status)
|
||||||
smap.Add("status_message", "Manually forced reset")
|
|
||||||
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
|
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func (svc *Service) ListMaps(ctx context.Context, params api.ListMapsParams) ([]
|
|||||||
filter.GameID = ¶ms.GameID.Value
|
filter.GameID = ¶ms.GameID.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
mapList, err := svc.Client.List(ctx, &maps.ListRequest{
|
mapList, err := svc.Maps.List(ctx, &maps.ListRequest{
|
||||||
Filter: &filter,
|
Filter: &filter,
|
||||||
Page: &maps.Pagination{
|
Page: &maps.Pagination{
|
||||||
Size: params.Limit,
|
Size: params.Limit,
|
||||||
@@ -56,7 +56,7 @@ func (svc *Service) ListMaps(ctx context.Context, params api.ListMapsParams) ([]
|
|||||||
//
|
//
|
||||||
// GET /maps/{MapID}
|
// GET /maps/{MapID}
|
||||||
func (svc *Service) GetMap(ctx context.Context, params api.GetMapParams) (*api.Map, error) {
|
func (svc *Service) GetMap(ctx context.Context, params api.GetMapParams) (*api.Map, error) {
|
||||||
mapResponse, err := svc.Client.Get(ctx, &maps.IdMessage{
|
mapResponse, err := svc.Maps.Get(ctx, &maps.IdMessage{
|
||||||
ID: params.MapID,
|
ID: params.MapID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.itzana.me/strafesnet/go-grpc/maps"
|
"git.itzana.me/strafesnet/go-grpc/maps"
|
||||||
|
"git.itzana.me/strafesnet/go-grpc/users"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
|
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
@@ -32,7 +33,8 @@ var (
|
|||||||
type Service struct {
|
type Service struct {
|
||||||
DB datastore.Datastore
|
DB datastore.Datastore
|
||||||
Nats nats.JetStreamContext
|
Nats nats.JetStreamContext
|
||||||
Client maps.MapsServiceClient
|
Maps maps.MapsServiceClient
|
||||||
|
Users users.UsersServiceClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewError creates *ErrorStatusCode from error returned by handler.
|
// NewError creates *ErrorStatusCode from error returned by handler.
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ var(
|
|||||||
model.SubmissionStatusSubmitted,
|
model.SubmissionStatusSubmitted,
|
||||||
model.SubmissionStatusUnderConstruction,
|
model.SubmissionStatusUnderConstruction,
|
||||||
}
|
}
|
||||||
// limit mapfixes in the pipeline to one per target map
|
// limit submissions in the pipeline to one per target map
|
||||||
ActiveAcceptedSubmissionStatuses = []model.SubmissionStatus{
|
ActiveAcceptedSubmissionStatuses = []model.SubmissionStatus{
|
||||||
model.SubmissionStatusUploading,
|
model.SubmissionStatusUploading,
|
||||||
model.SubmissionStatusValidated,
|
model.SubmissionStatusValidated,
|
||||||
@@ -101,8 +101,89 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
|
|||||||
}
|
}
|
||||||
|
|
||||||
create_request := model.CreateSubmissionRequest{
|
create_request := model.CreateSubmissionRequest{
|
||||||
OperationID: operation.ID,
|
OperationID: operation.ID,
|
||||||
ModelID: ModelID,
|
ModelID: ModelID,
|
||||||
|
DisplayName: request.DisplayName,
|
||||||
|
Creator: request.Creator,
|
||||||
|
GameID: uint32(request.GameID),
|
||||||
|
Status: uint32(model.SubmissionStatusUnderConstruction),
|
||||||
|
Roles: uint32(RolesEmpty),
|
||||||
|
}
|
||||||
|
|
||||||
|
j, err := json.Marshal(create_request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = svc.Nats.Publish("maptest.submissions.create", []byte(j))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api.OperationID{
|
||||||
|
OperationID: operation.ID,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
// POST /submissions-admin
|
||||||
|
func (svc *Service) CreateSubmissionAdmin(ctx context.Context, request *api.SubmissionTriggerCreate) (*api.OperationID, error) {
|
||||||
|
// sanitization
|
||||||
|
if request.AssetID<0{
|
||||||
|
return nil, ErrNegativeID
|
||||||
|
}
|
||||||
|
var ModelID=uint64(request.AssetID);
|
||||||
|
|
||||||
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrUserInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
userId, err := userInfo.GetUserID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
roles, err := userInfo.GetRoles()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if caller has required role
|
||||||
|
has_role := roles & RolesSubmissionReview == RolesSubmissionReview
|
||||||
|
if !has_role {
|
||||||
|
return nil, ErrPermissionDeniedNeedRoleSubmissionReview
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if too many operations have been created recently
|
||||||
|
{
|
||||||
|
count, err := svc.DB.Operations().CountSince(ctx,
|
||||||
|
int64(userId),
|
||||||
|
time.Now().Add(-CreateSubmissionRecencyWindow),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if CreateSubmissionRateLimit < count {
|
||||||
|
return nil, ErrCreateSubmissionRateLimit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operation, err := svc.DB.Operations().Create(ctx, model.Operation{
|
||||||
|
Owner: userId,
|
||||||
|
StatusID: model.OperationStatusCreated,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
create_request := model.CreateSubmissionRequest{
|
||||||
|
OperationID: operation.ID,
|
||||||
|
ModelID: ModelID,
|
||||||
|
DisplayName: request.DisplayName,
|
||||||
|
Creator: request.Creator,
|
||||||
|
GameID: uint32(request.GameID),
|
||||||
|
Status: uint32(model.SubmissionStatusChangesRequested),
|
||||||
|
Roles: uint32(roles),
|
||||||
}
|
}
|
||||||
|
|
||||||
j, err := json.Marshal(create_request)
|
j, err := json.Marshal(create_request)
|
||||||
@@ -143,7 +224,6 @@ func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionP
|
|||||||
Completed: submission.Completed,
|
Completed: submission.Completed,
|
||||||
UploadedAssetID: api.NewOptInt64(int64(submission.UploadedAssetID)),
|
UploadedAssetID: api.NewOptInt64(int64(submission.UploadedAssetID)),
|
||||||
StatusID: int32(submission.StatusID),
|
StatusID: int32(submission.StatusID),
|
||||||
StatusMessage: submission.StatusMessage,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,16 +585,109 @@ func (svc *Service) ActionSubmissionTriggerSubmit(ctx context.Context, params ap
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if caller is the submitter
|
// check if caller is the submitter
|
||||||
has_role := userId == submission.Submitter
|
is_submitter := userId == submission.Submitter
|
||||||
if !has_role {
|
// neither = deny
|
||||||
return ErrPermissionDeniedNotSubmitter
|
if !is_submitter {
|
||||||
|
has_submission_review, err := userInfo.HasRoleSubmissionReview()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has_submission_review {
|
||||||
|
return ErrPermissionDeniedNotSubmitter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transaction
|
||||||
|
target_status := model.SubmissionStatusSubmitting
|
||||||
|
smap := datastore.Optional()
|
||||||
|
smap.Add("status_id", target_status)
|
||||||
|
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUnderConstruction, model.SubmissionStatusChangesRequested}, smap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_request := model.CheckSubmissionRequest{
|
||||||
|
SubmissionID: submission.ID,
|
||||||
|
ModelID: submission.AssetID,
|
||||||
|
}
|
||||||
|
|
||||||
|
j, err := json.Marshal(validate_request)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = svc.Nats.Publish("maptest.submissions.check", []byte(j))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
event_data := model.AuditEventDataAction{
|
||||||
|
TargetStatus: uint32(target_status),
|
||||||
|
}
|
||||||
|
|
||||||
|
EventData, err := json.Marshal(event_data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
|
||||||
|
ID: 0,
|
||||||
|
User: userId,
|
||||||
|
ResourceType: model.ResourceSubmission,
|
||||||
|
ResourceID: params.SubmissionID,
|
||||||
|
EventType: model.AuditEventTypeAction,
|
||||||
|
EventData: EventData,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionSubmissionBypassSubmit invokes actionSubmissionBypassSubmit operation.
|
||||||
|
//
|
||||||
|
// Role Reviewer changes status from ChangesRequested -> Submitted.
|
||||||
|
//
|
||||||
|
// POST /submissions/{SubmissionID}/status/bypass-submit
|
||||||
|
func (svc *Service) ActionSubmissionBypassSubmit(ctx context.Context, params api.ActionSubmissionBypassSubmitParams) error {
|
||||||
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
||||||
|
if !ok {
|
||||||
|
return ErrUserInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// read submission (this could be done with a transaction WHERE clause)
|
||||||
|
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userId, err := userInfo.GetUserID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if caller is the submitter
|
||||||
|
is_submitter := userId == submission.Submitter
|
||||||
|
if is_submitter {
|
||||||
|
return ErrAcceptOwnSubmission
|
||||||
|
}
|
||||||
|
|
||||||
|
has_submission_review, err := userInfo.HasRoleSubmissionReview()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has_submission_review {
|
||||||
|
return ErrPermissionDeniedNeedRoleSubmissionReview
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction
|
// transaction
|
||||||
target_status := model.SubmissionStatusSubmitted
|
target_status := model.SubmissionStatusSubmitted
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id", target_status)
|
smap.Add("status_id", target_status)
|
||||||
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUnderConstruction, model.SubmissionStatusChangesRequested}, smap)
|
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusChangesRequested}, smap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -579,7 +752,6 @@ func (svc *Service) ActionSubmissionResetSubmitting(ctx context.Context, params
|
|||||||
target_status := model.SubmissionStatusUnderConstruction
|
target_status := model.SubmissionStatusUnderConstruction
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id", target_status)
|
smap.Add("status_id", target_status)
|
||||||
smap.Add("status_message", "Manually forced reset")
|
|
||||||
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap)
|
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -971,7 +1143,6 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params api.Act
|
|||||||
target_status := model.SubmissionStatusAcceptedUnvalidated
|
target_status := model.SubmissionStatusAcceptedUnvalidated
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id", target_status)
|
smap.Add("status_id", target_status)
|
||||||
smap.Add("status_message", "Manually forced reset")
|
|
||||||
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
|
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -1046,7 +1217,7 @@ func (svc *Service) ReleaseSubmissions(ctx context.Context, request []api.Releas
|
|||||||
date := request[i].Date.Unix()
|
date := request[i].Date.Unix()
|
||||||
var GameID = int32(submission.GameID)
|
var GameID = int32(submission.GameID)
|
||||||
// create each map with go-grpc
|
// create each map with go-grpc
|
||||||
_, err := svc.Client.Create(ctx, &maps.MapRequest{
|
_, err := svc.Maps.Create(ctx, &maps.MapRequest{
|
||||||
ID: int64(submission.UploadedAssetID),
|
ID: int64(submission.UploadedAssetID),
|
||||||
DisplayName: &submission.DisplayName,
|
DisplayName: &submission.DisplayName,
|
||||||
Creator: &submission.Creator,
|
Creator: &submission.Creator,
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func (svc *Service) UpdateMapfixValidatedModel(ctx context.Context, params inter
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionMapfixValidate invokes actionMapfixValidate operation.
|
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Submitting -> Submitted.
|
// Role Validator changes status from Submitting -> Submitted.
|
||||||
//
|
//
|
||||||
@@ -84,6 +84,10 @@ func (svc *Service) ActionMapfixSubmitted(ctx context.Context, params internal.A
|
|||||||
target_status := model.MapfixStatusSubmitted
|
target_status := model.MapfixStatusSubmitted
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id", target_status)
|
smap.Add("status_id", target_status)
|
||||||
|
smap.Add("asset_version", params.ModelVersion)
|
||||||
|
smap.Add("display_name", params.DisplayName)
|
||||||
|
smap.Add("creator", params.Creator)
|
||||||
|
smap.Add("game_id", params.GameID)
|
||||||
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap)
|
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -113,6 +117,68 @@ func (svc *Service) ActionMapfixSubmitted(ctx context.Context, params internal.A
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
|
||||||
|
//
|
||||||
|
// (Internal endpoint) Role Validator changes status from Submitting -> RequestChanges.
|
||||||
|
//
|
||||||
|
// POST /mapfixes/{MapfixID}/status/validator-request-changes
|
||||||
|
func (svc *Service) ActionMapfixRequestChanges(ctx context.Context, params internal.ActionMapfixRequestChangesParams) error {
|
||||||
|
// transaction
|
||||||
|
target_status := model.MapfixStatusChangesRequested
|
||||||
|
smap := datastore.Optional()
|
||||||
|
smap.Add("status_id", target_status)
|
||||||
|
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
event_data := model.AuditEventDataError{
|
||||||
|
Error: params.ErrorMessage,
|
||||||
|
}
|
||||||
|
|
||||||
|
EventData, err := json.Marshal(event_data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
|
||||||
|
ID: 0,
|
||||||
|
User: ValidtorUserID,
|
||||||
|
ResourceType: model.ResourceMapfix,
|
||||||
|
ResourceID: params.MapfixID,
|
||||||
|
EventType: model.AuditEventTypeError,
|
||||||
|
EventData: EventData,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event_data := model.AuditEventDataAction{
|
||||||
|
TargetStatus: uint32(target_status),
|
||||||
|
}
|
||||||
|
|
||||||
|
EventData, err := json.Marshal(event_data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
|
||||||
|
ID: 0,
|
||||||
|
User: ValidtorUserID,
|
||||||
|
ResourceType: model.ResourceMapfix,
|
||||||
|
ResourceID: params.MapfixID,
|
||||||
|
EventType: model.AuditEventTypeAction,
|
||||||
|
EventData: EventData,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ActionMapfixValidate invokes actionMapfixValidate operation.
|
// ActionMapfixValidate invokes actionMapfixValidate operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Validating -> Validated.
|
// Role Validator changes status from Validating -> Validated.
|
||||||
@@ -135,12 +201,36 @@ func (svc *Service) ActionMapfixAccepted(ctx context.Context, params internal.Ac
|
|||||||
target_status := model.MapfixStatusAcceptedUnvalidated
|
target_status := model.MapfixStatusAcceptedUnvalidated
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id", target_status)
|
smap.Add("status_id", target_status)
|
||||||
smap.Add("status_message", params.StatusMessage)
|
|
||||||
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
|
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//push an error audit event
|
||||||
|
{
|
||||||
|
event_data := model.AuditEventDataError{
|
||||||
|
Error: params.ErrorMessage,
|
||||||
|
}
|
||||||
|
|
||||||
|
EventData, err := json.Marshal(event_data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
|
||||||
|
ID: 0,
|
||||||
|
User: ValidtorUserID,
|
||||||
|
ResourceType: model.ResourceMapfix,
|
||||||
|
ResourceID: params.MapfixID,
|
||||||
|
EventType: model.AuditEventTypeError,
|
||||||
|
EventData: EventData,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// push an action audit event
|
||||||
event_data := model.AuditEventDataAction{
|
event_data := model.AuditEventDataAction{
|
||||||
TargetStatus: uint32(target_status),
|
TargetStatus: uint32(target_status),
|
||||||
}
|
}
|
||||||
@@ -260,6 +350,7 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *internal.MapfixCr
|
|||||||
Completed: false,
|
Completed: false,
|
||||||
TargetAssetID: TargetAssetID,
|
TargetAssetID: TargetAssetID,
|
||||||
StatusID: model.MapfixStatusUnderConstruction,
|
StatusID: model.MapfixStatusUnderConstruction,
|
||||||
|
Description: request.Description,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
|
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
|
||||||
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
|
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
|
||||||
"git.itzana.me/strafesnet/maps-service/pkg/model"
|
"git.itzana.me/strafesnet/maps-service/pkg/model"
|
||||||
|
"git.itzana.me/strafesnet/maps-service/pkg/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
var(
|
var(
|
||||||
@@ -73,7 +74,7 @@ func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params i
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
|
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Submitting -> Submitted.
|
// Role Validator changes status from Submitting -> Submitted.
|
||||||
//
|
//
|
||||||
@@ -83,6 +84,10 @@ func (svc *Service) ActionSubmissionSubmitted(ctx context.Context, params intern
|
|||||||
target_status := model.SubmissionStatusSubmitted
|
target_status := model.SubmissionStatusSubmitted
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id", target_status)
|
smap.Add("status_id", target_status)
|
||||||
|
smap.Add("asset_version", params.ModelVersion)
|
||||||
|
smap.Add("display_name", params.DisplayName)
|
||||||
|
smap.Add("creator", params.Creator)
|
||||||
|
smap.Add("game_id", params.GameID)
|
||||||
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap)
|
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -112,6 +117,70 @@ func (svc *Service) ActionSubmissionSubmitted(ctx context.Context, params intern
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
|
||||||
|
//
|
||||||
|
// (Internal endpoint) Role Validator changes status from Submitting -> RequestChanges.
|
||||||
|
//
|
||||||
|
// POST /submissions/{SubmissionID}/status/validator-request-changes
|
||||||
|
func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params internal.ActionSubmissionRequestChangesParams) error {
|
||||||
|
// transaction
|
||||||
|
target_status := model.SubmissionStatusChangesRequested
|
||||||
|
smap := datastore.Optional()
|
||||||
|
smap.Add("status_id", target_status)
|
||||||
|
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//push an error audit event
|
||||||
|
{
|
||||||
|
event_data := model.AuditEventDataError{
|
||||||
|
Error: params.ErrorMessage,
|
||||||
|
}
|
||||||
|
|
||||||
|
EventData, err := json.Marshal(event_data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
|
||||||
|
ID: 0,
|
||||||
|
User: ValidtorUserID,
|
||||||
|
ResourceType: model.ResourceSubmission,
|
||||||
|
ResourceID: params.SubmissionID,
|
||||||
|
EventType: model.AuditEventTypeError,
|
||||||
|
EventData: EventData,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// push an action audit event
|
||||||
|
event_data := model.AuditEventDataAction{
|
||||||
|
TargetStatus: uint32(target_status),
|
||||||
|
}
|
||||||
|
|
||||||
|
EventData, err := json.Marshal(event_data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
|
||||||
|
ID: 0,
|
||||||
|
User: ValidtorUserID,
|
||||||
|
ResourceType: model.ResourceSubmission,
|
||||||
|
ResourceID: params.SubmissionID,
|
||||||
|
EventType: model.AuditEventTypeAction,
|
||||||
|
EventData: EventData,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
|
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
|
||||||
//
|
//
|
||||||
// Role Validator changes status from Validating -> Validated.
|
// Role Validator changes status from Validating -> Validated.
|
||||||
@@ -161,12 +230,37 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params interna
|
|||||||
target_status := model.SubmissionStatusAcceptedUnvalidated
|
target_status := model.SubmissionStatusAcceptedUnvalidated
|
||||||
smap := datastore.Optional()
|
smap := datastore.Optional()
|
||||||
smap.Add("status_id", target_status)
|
smap.Add("status_id", target_status)
|
||||||
smap.Add("status_message", params.StatusMessage)
|
|
||||||
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
|
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//push an error audit event
|
||||||
|
{
|
||||||
|
event_data := model.AuditEventDataError{
|
||||||
|
Error: params.ErrorMessage,
|
||||||
|
}
|
||||||
|
|
||||||
|
EventData, err := json.Marshal(event_data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
|
||||||
|
ID: 0,
|
||||||
|
User: ValidtorUserID,
|
||||||
|
ResourceType: model.ResourceSubmission,
|
||||||
|
ResourceID: params.SubmissionID,
|
||||||
|
EventType: model.AuditEventTypeError,
|
||||||
|
EventData: EventData,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// push an action audit event
|
||||||
event_data := model.AuditEventDataAction{
|
event_data := model.AuditEventDataAction{
|
||||||
TargetStatus: uint32(target_status),
|
TargetStatus: uint32(target_status),
|
||||||
}
|
}
|
||||||
@@ -244,6 +338,8 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *internal.Subm
|
|||||||
var Submitter=uint64(request.AssetOwner);
|
var Submitter=uint64(request.AssetOwner);
|
||||||
var AssetID=uint64(request.AssetID);
|
var AssetID=uint64(request.AssetID);
|
||||||
var AssetVersion=uint64(request.AssetVersion);
|
var AssetVersion=uint64(request.AssetVersion);
|
||||||
|
var Status=model.SubmissionStatus(request.Status);
|
||||||
|
var roles=service.Roles(request.Roles);
|
||||||
|
|
||||||
// Check if an active submission with the same asset id exists
|
// Check if an active submission with the same asset id exists
|
||||||
{
|
{
|
||||||
@@ -269,8 +365,11 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *internal.Subm
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if user owns asset
|
// check if user owns asset
|
||||||
// TODO: allow bypass by admin
|
is_submitter := operation.Owner == Submitter
|
||||||
if operation.Owner != Submitter {
|
// check if user is map admin
|
||||||
|
has_submission_review := roles & service.RolesSubmissionReview == service.RolesSubmissionReview
|
||||||
|
// if neither, u not allowed
|
||||||
|
if !is_submitter && !has_submission_review {
|
||||||
return nil, ErrNotAssetOwner
|
return nil, ErrNotAssetOwner
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +382,7 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *internal.Subm
|
|||||||
AssetID: AssetID,
|
AssetID: AssetID,
|
||||||
AssetVersion: AssetVersion,
|
AssetVersion: AssetVersion,
|
||||||
Completed: false,
|
Completed: false,
|
||||||
StatusID: model.SubmissionStatusUnderConstruction,
|
StatusID: Status,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -5,14 +5,16 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
submissions-api = { path = "api", features = ["internal"], default-features = false, registry = "strafesnet" }
|
submissions-api = { path = "api", features = ["internal"], default-features = false, registry = "strafesnet" }
|
||||||
async-nats = "0.40.0"
|
async-nats = "0.41.0"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
rbx_asset = { version = "0.4.3", registry = "strafesnet" }
|
rbx_asset = { version = "0.4.5", registry = "strafesnet" }
|
||||||
rbx_binary = { version = "0.7.4", registry = "strafesnet"}
|
rbx_binary = "1.0.0"
|
||||||
rbx_dom_weak = { version = "2.9.0", registry = "strafesnet"}
|
rbx_dom_weak = "3.0.0"
|
||||||
rbx_reflection_database = { version = "0.2.12", registry = "strafesnet"}
|
rbx_reflection_database = "1.0.3"
|
||||||
rbx_xml = { version = "0.13.3", registry = "strafesnet"}
|
rbx_xml = "1.0.0"
|
||||||
serde = { version = "1.0.215", features = ["derive"] }
|
serde = { version = "1.0.215", features = ["derive"] }
|
||||||
serde_json = "1.0.133"
|
serde_json = "1.0.133"
|
||||||
siphasher = "1.0.1"
|
siphasher = "1.0.1"
|
||||||
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread", "signal"] }
|
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread", "signal"] }
|
||||||
|
heck = "0.5.0"
|
||||||
|
lazy-regex = "3.4.1"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "submissions-api"
|
name = "submissions-api"
|
||||||
version = "0.7.0"
|
version = "0.7.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = ["strafesnet"]
|
publish = ["strafesnet"]
|
||||||
repository = "https://git.itzana.me/StrafesNET/maps-service"
|
repository = "https://git.itzana.me/StrafesNET/maps-service"
|
||||||
|
|||||||
@@ -44,4 +44,8 @@ impl Context{
|
|||||||
.body(body)
|
.body(body)
|
||||||
.send().await
|
.send().await
|
||||||
}
|
}
|
||||||
|
pub async fn delete(&self,url:impl reqwest::IntoUrl)->Result<reqwest::Response,reqwest::Error>{
|
||||||
|
self.client.delete(url)
|
||||||
|
.send().await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ impl Context{
|
|||||||
).await.map_err(Error::Response)?
|
).await.map_err(Error::Response)?
|
||||||
.json().await.map_err(Error::ReqwestJson)
|
.json().await.map_err(Error::ReqwestJson)
|
||||||
}
|
}
|
||||||
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
|
pub async fn get_scripts(&self,config:GetScriptsRequest<'_>)->Result<Vec<ScriptResponse>,Error>{
|
||||||
let url_raw=format!("{}/scripts",self.0.base_url);
|
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 url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ impl Context{
|
|||||||
).await.map_err(Error::Response)?
|
).await.map_err(Error::Response)?
|
||||||
.json().await.map_err(Error::ReqwestJson)
|
.json().await.map_err(Error::ReqwestJson)
|
||||||
}
|
}
|
||||||
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
|
pub async fn get_script_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptResponse>,SingleItemError>{
|
||||||
let scripts=self.get_scripts(GetScriptsRequest{
|
let scripts=self.get_scripts(GetScriptsRequest{
|
||||||
Page:1,
|
Page:1,
|
||||||
Limit:2,
|
Limit:2,
|
||||||
@@ -61,7 +61,7 @@ impl Context{
|
|||||||
}
|
}
|
||||||
Ok(scripts.into_iter().next())
|
Ok(scripts.into_iter().next())
|
||||||
}
|
}
|
||||||
pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{
|
pub async fn create_script(&self,config:CreateScriptRequest<'_>)->Result<ScriptIDResponse,Error>{
|
||||||
let url_raw=format!("{}/scripts",self.0.base_url);
|
let url_raw=format!("{}/scripts",self.0.base_url);
|
||||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
|
||||||
@@ -72,7 +72,17 @@ impl Context{
|
|||||||
).await.map_err(Error::Response)?
|
).await.map_err(Error::Response)?
|
||||||
.json().await.map_err(Error::ReqwestJson)
|
.json().await.map_err(Error::ReqwestJson)
|
||||||
}
|
}
|
||||||
pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{
|
pub async fn delete_script(&self,config:GetScriptRequest)->Result<(),Error>{
|
||||||
|
let url_raw=format!("{}/scripts/{}",self.0.base_url,config.ScriptID.0);
|
||||||
|
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
|
||||||
|
response_ok(
|
||||||
|
self.0.delete(url).await.map_err(Error::Reqwest)?
|
||||||
|
).await.map_err(Error::Response)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub async fn get_script_policies(&self,config:GetScriptPoliciesRequest<'_>)->Result<Vec<ScriptPolicyResponse>,Error>{
|
||||||
let url_raw=format!("{}/script-policy",self.0.base_url);
|
let url_raw=format!("{}/script-policy",self.0.base_url);
|
||||||
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
|
||||||
@@ -96,7 +106,7 @@ impl Context{
|
|||||||
).await.map_err(Error::Response)?
|
).await.map_err(Error::Response)?
|
||||||
.json().await.map_err(Error::ReqwestJson)
|
.json().await.map_err(Error::ReqwestJson)
|
||||||
}
|
}
|
||||||
pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
|
pub async fn get_script_policy_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
|
||||||
let policies=self.get_script_policies(GetScriptPoliciesRequest{
|
let policies=self.get_script_policies(GetScriptPoliciesRequest{
|
||||||
Page:1,
|
Page:1,
|
||||||
Limit:2,
|
Limit:2,
|
||||||
@@ -130,6 +140,16 @@ impl Context{
|
|||||||
self.0.post(url,body).await.map_err(Error::Reqwest)?
|
self.0.post(url,body).await.map_err(Error::Reqwest)?
|
||||||
).await.map_err(Error::Response)?;
|
).await.map_err(Error::Response)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub async fn delete_script_policy(&self,config:GetScriptPolicyRequest)->Result<(),Error>{
|
||||||
|
let url_raw=format!("{}/script-policy/{}",self.0.base_url,config.ScriptPolicyID.0);
|
||||||
|
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
|
||||||
|
response_ok(
|
||||||
|
self.0.delete(url).await.map_err(Error::Reqwest)?
|
||||||
|
).await.map_err(Error::Response)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ impl Context{
|
|||||||
).await.map_err(Error::Response)?
|
).await.map_err(Error::Response)?
|
||||||
.json().await.map_err(Error::ReqwestJson)
|
.json().await.map_err(Error::ReqwestJson)
|
||||||
}
|
}
|
||||||
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
|
pub async fn get_scripts(&self,config:GetScriptsRequest<'_>)->Result<Vec<ScriptResponse>,Error>{
|
||||||
let url_raw=format!("{}/scripts",self.0.base_url);
|
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 url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ impl Context{
|
|||||||
).await.map_err(Error::Response)?
|
).await.map_err(Error::Response)?
|
||||||
.json().await.map_err(Error::ReqwestJson)
|
.json().await.map_err(Error::ReqwestJson)
|
||||||
}
|
}
|
||||||
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
|
pub async fn get_script_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptResponse>,SingleItemError>{
|
||||||
let scripts=self.get_scripts(GetScriptsRequest{
|
let scripts=self.get_scripts(GetScriptsRequest{
|
||||||
Page:1,
|
Page:1,
|
||||||
Limit:2,
|
Limit:2,
|
||||||
@@ -91,7 +91,7 @@ impl Context{
|
|||||||
}
|
}
|
||||||
Ok(scripts.into_iter().next())
|
Ok(scripts.into_iter().next())
|
||||||
}
|
}
|
||||||
pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{
|
pub async fn create_script(&self,config:CreateScriptRequest<'_>)->Result<ScriptIDResponse,Error>{
|
||||||
let url_raw=format!("{}/scripts",self.0.base_url);
|
let url_raw=format!("{}/scripts",self.0.base_url);
|
||||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ impl Context{
|
|||||||
).await.map_err(Error::Response)?
|
).await.map_err(Error::Response)?
|
||||||
.json().await.map_err(Error::ReqwestJson)
|
.json().await.map_err(Error::ReqwestJson)
|
||||||
}
|
}
|
||||||
pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{
|
pub async fn get_script_policies(&self,config:GetScriptPoliciesRequest<'_>)->Result<Vec<ScriptPolicyResponse>,Error>{
|
||||||
let url_raw=format!("{}/script-policy",self.0.base_url);
|
let url_raw=format!("{}/script-policy",self.0.base_url);
|
||||||
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ impl Context{
|
|||||||
).await.map_err(Error::Response)?
|
).await.map_err(Error::Response)?
|
||||||
.json().await.map_err(Error::ReqwestJson)
|
.json().await.map_err(Error::ReqwestJson)
|
||||||
}
|
}
|
||||||
pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
|
pub async fn get_script_policy_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
|
||||||
let policies=self.get_script_policies(GetScriptPoliciesRequest{
|
let policies=self.get_script_policies(GetScriptPoliciesRequest{
|
||||||
Page:1,
|
Page:1,
|
||||||
Limit:2,
|
Limit:2,
|
||||||
@@ -150,7 +150,7 @@ impl Context{
|
|||||||
).await.map_err(Error::Response)?
|
).await.map_err(Error::Response)?
|
||||||
.json().await.map_err(Error::ReqwestJson)
|
.json().await.map_err(Error::ReqwestJson)
|
||||||
}
|
}
|
||||||
pub async fn create_submission<'a>(&self,config:CreateSubmissionRequest<'a>)->Result<SubmissionIDResponse,Error>{
|
pub async fn create_submission(&self,config:CreateSubmissionRequest<'_>)->Result<SubmissionIDResponse,Error>{
|
||||||
let url_raw=format!("{}/submissions",self.0.base_url);
|
let url_raw=format!("{}/submissions",self.0.base_url);
|
||||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
|
||||||
@@ -162,6 +162,15 @@ impl Context{
|
|||||||
.json().await.map_err(Error::ReqwestJson)
|
.json().await.map_err(Error::ReqwestJson)
|
||||||
}
|
}
|
||||||
// simple submission endpoints
|
// simple submission endpoints
|
||||||
|
action!("submissions",action_submission_request_changes,config,ActionSubmissionRequestChangesRequest,"status/validator-request-changes",config.SubmissionID,
|
||||||
|
("ErrorMessage",config.ErrorMessage.as_str())
|
||||||
|
);
|
||||||
|
action!("submissions",action_submission_submitted,config,ActionSubmissionSubmittedRequest,"status/validator-submitted",config.SubmissionID,
|
||||||
|
("ModelVersion",config.ModelVersion.to_string().as_str())
|
||||||
|
("DisplayName",config.DisplayName.as_str())
|
||||||
|
("Creator",config.Creator.as_str())
|
||||||
|
("GameID",config.GameID.to_string().as_str())
|
||||||
|
);
|
||||||
action!("submissions",action_submission_validated,config,SubmissionID,"status/validator-validated",config.0,);
|
action!("submissions",action_submission_validated,config,SubmissionID,"status/validator-validated",config.0,);
|
||||||
action!("submissions",update_submission_validated_model,config,UpdateSubmissionModelRequest,"validated-model",config.SubmissionID,
|
action!("submissions",update_submission_validated_model,config,UpdateSubmissionModelRequest,"validated-model",config.SubmissionID,
|
||||||
("ValidatedModelID",config.ModelID.to_string().as_str())
|
("ValidatedModelID",config.ModelID.to_string().as_str())
|
||||||
@@ -171,9 +180,9 @@ impl Context{
|
|||||||
("UploadedAssetID",config.UploadedAssetID.to_string().as_str())
|
("UploadedAssetID",config.UploadedAssetID.to_string().as_str())
|
||||||
);
|
);
|
||||||
action!("submissions",action_submission_accepted,config,ActionSubmissionAcceptedRequest,"status/validator-failed",config.SubmissionID,
|
action!("submissions",action_submission_accepted,config,ActionSubmissionAcceptedRequest,"status/validator-failed",config.SubmissionID,
|
||||||
("StatusMessage",config.StatusMessage.as_str())
|
("ErrorMessage",config.ErrorMessage.as_str())
|
||||||
);
|
);
|
||||||
pub async fn create_mapfix<'a>(&self,config:CreateMapfixRequest<'a>)->Result<MapfixIDResponse,Error>{
|
pub async fn create_mapfix(&self,config:CreateMapfixRequest<'_>)->Result<MapfixIDResponse,Error>{
|
||||||
let url_raw=format!("{}/mapfixes",self.0.base_url);
|
let url_raw=format!("{}/mapfixes",self.0.base_url);
|
||||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
|
||||||
@@ -185,6 +194,15 @@ impl Context{
|
|||||||
.json().await.map_err(Error::ReqwestJson)
|
.json().await.map_err(Error::ReqwestJson)
|
||||||
}
|
}
|
||||||
// simple mapfixes endpoints
|
// simple mapfixes endpoints
|
||||||
|
action!("mapfixes",action_mapfix_request_changes,config,ActionMapfixRequestChangesRequest,"status/validator-request-changes",config.MapfixID,
|
||||||
|
("ErrorMessage",config.ErrorMessage.as_str())
|
||||||
|
);
|
||||||
|
action!("mapfixes",action_mapfix_submitted,config,ActionMapfixSubmittedRequest,"status/validator-submitted",config.MapfixID,
|
||||||
|
("ModelVersion",config.ModelVersion.to_string().as_str())
|
||||||
|
("DisplayName",config.DisplayName.as_str())
|
||||||
|
("Creator",config.Creator.as_str())
|
||||||
|
("GameID",config.GameID.to_string().as_str())
|
||||||
|
);
|
||||||
action!("mapfixes",action_mapfix_validated,config,MapfixID,"status/validator-validated",config.0,);
|
action!("mapfixes",action_mapfix_validated,config,MapfixID,"status/validator-validated",config.0,);
|
||||||
action!("mapfixes",update_mapfix_validated_model,config,UpdateMapfixModelRequest,"validated-model",config.MapfixID,
|
action!("mapfixes",update_mapfix_validated_model,config,UpdateMapfixModelRequest,"validated-model",config.MapfixID,
|
||||||
("ValidatedModelID",config.ModelID.to_string().as_str())
|
("ValidatedModelID",config.ModelID.to_string().as_str())
|
||||||
@@ -192,7 +210,7 @@ impl Context{
|
|||||||
);
|
);
|
||||||
action!("mapfixes",action_mapfix_uploaded,config,ActionMapfixUploadedRequest,"status/validator-uploaded",config.MapfixID,);
|
action!("mapfixes",action_mapfix_uploaded,config,ActionMapfixUploadedRequest,"status/validator-uploaded",config.MapfixID,);
|
||||||
action!("mapfixes",action_mapfix_accepted,config,ActionMapfixAcceptedRequest,"status/validator-failed",config.MapfixID,
|
action!("mapfixes",action_mapfix_accepted,config,ActionMapfixAcceptedRequest,"status/validator-failed",config.MapfixID,
|
||||||
("StatusMessage",config.StatusMessage.as_str())
|
("ErrorMessage",config.ErrorMessage.as_str())
|
||||||
);
|
);
|
||||||
// simple operation endpoint
|
// simple operation endpoint
|
||||||
action!("operations",action_operation_failed,config,ActionOperationFailedRequest,"status/operation-failed",config.OperationID,
|
action!("operations",action_operation_failed,config,ActionOperationFailedRequest,"status/operation-failed",config.OperationID,
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ impl std::error::Error for SingleItemError{}
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StatusCodeWithUrlAndBody{
|
pub struct UrlAndBody{
|
||||||
pub status_code:reqwest::StatusCode,
|
|
||||||
pub url:url::Url,
|
pub url:url::Url,
|
||||||
pub body:String,
|
pub body:String,
|
||||||
}
|
}
|
||||||
@@ -36,7 +35,10 @@ pub struct StatusCodeWithUrlAndBody{
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ResponseError{
|
pub enum ResponseError{
|
||||||
Reqwest(reqwest::Error),
|
Reqwest(reqwest::Error),
|
||||||
StatusCodeWithUrlAndBody(StatusCodeWithUrlAndBody),
|
Details{
|
||||||
|
status_code:reqwest::StatusCode,
|
||||||
|
url_and_body:Box<UrlAndBody>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for ResponseError{
|
impl std::fmt::Display for ResponseError{
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
@@ -53,11 +55,10 @@ pub async fn response_ok(response:reqwest::Response)->Result<reqwest::Response,R
|
|||||||
let url=response.url().to_owned();
|
let url=response.url().to_owned();
|
||||||
let bytes=response.bytes().await.map_err(ResponseError::Reqwest)?;
|
let bytes=response.bytes().await.map_err(ResponseError::Reqwest)?;
|
||||||
let body=String::from_utf8_lossy(&bytes).to_string();
|
let body=String::from_utf8_lossy(&bytes).to_string();
|
||||||
Err(ResponseError::StatusCodeWithUrlAndBody(StatusCodeWithUrlAndBody{
|
Err(ResponseError::Details{
|
||||||
status_code,
|
status_code,
|
||||||
url,
|
url_and_body:Box::new(UrlAndBody{url,body})
|
||||||
body,
|
})
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +74,7 @@ pub struct CreateMapfixRequest<'a>{
|
|||||||
pub AssetID:u64,
|
pub AssetID:u64,
|
||||||
pub AssetVersion:u64,
|
pub AssetVersion:u64,
|
||||||
pub TargetAssetID:u64,
|
pub TargetAssetID:u64,
|
||||||
|
pub Description:&'a str,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(Clone,Debug,serde::Deserialize)]
|
#[derive(Clone,Debug,serde::Deserialize)]
|
||||||
@@ -90,6 +92,8 @@ pub struct CreateSubmissionRequest<'a>{
|
|||||||
pub GameID:i32,
|
pub GameID:i32,
|
||||||
pub AssetID:u64,
|
pub AssetID:u64,
|
||||||
pub AssetVersion:u64,
|
pub AssetVersion:u64,
|
||||||
|
pub Status:u32,
|
||||||
|
pub Roles:u32,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(Clone,Debug,serde::Deserialize)]
|
#[derive(Clone,Debug,serde::Deserialize)]
|
||||||
@@ -169,6 +173,10 @@ pub enum Policy{
|
|||||||
Replace=4,
|
Replace=4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(nonstandard_style)]
|
||||||
|
pub struct GetScriptPolicyRequest{
|
||||||
|
pub ScriptPolicyID:ScriptPolicyID,
|
||||||
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(Clone,Debug,serde::Serialize)]
|
#[derive(Clone,Debug,serde::Serialize)]
|
||||||
pub struct GetScriptPoliciesRequest<'a>{
|
pub struct GetScriptPoliciesRequest<'a>{
|
||||||
@@ -222,6 +230,23 @@ pub struct UpdateSubmissionModelRequest{
|
|||||||
pub ModelVersion:u64,
|
pub ModelVersion:u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(nonstandard_style)]
|
||||||
|
#[derive(Clone,Debug)]
|
||||||
|
pub struct ActionSubmissionSubmittedRequest{
|
||||||
|
pub SubmissionID:i64,
|
||||||
|
pub ModelVersion:u64,
|
||||||
|
pub DisplayName:String,
|
||||||
|
pub Creator:String,
|
||||||
|
pub GameID:u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(nonstandard_style)]
|
||||||
|
#[derive(Clone,Debug)]
|
||||||
|
pub struct ActionSubmissionRequestChangesRequest{
|
||||||
|
pub SubmissionID:i64,
|
||||||
|
pub ErrorMessage:String,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone,Debug)]
|
||||||
pub struct ActionSubmissionUploadedRequest{
|
pub struct ActionSubmissionUploadedRequest{
|
||||||
@@ -233,7 +258,7 @@ pub struct ActionSubmissionUploadedRequest{
|
|||||||
#[derive(Clone,Debug)]
|
#[derive(Clone,Debug)]
|
||||||
pub struct ActionSubmissionAcceptedRequest{
|
pub struct ActionSubmissionAcceptedRequest{
|
||||||
pub SubmissionID:i64,
|
pub SubmissionID:i64,
|
||||||
pub StatusMessage:String,
|
pub ErrorMessage:String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug,serde::Deserialize)]
|
#[derive(Clone,Copy,Debug,serde::Deserialize)]
|
||||||
@@ -247,6 +272,23 @@ pub struct UpdateMapfixModelRequest{
|
|||||||
pub ModelVersion:u64,
|
pub ModelVersion:u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(nonstandard_style)]
|
||||||
|
#[derive(Clone,Debug)]
|
||||||
|
pub struct ActionMapfixSubmittedRequest{
|
||||||
|
pub MapfixID:i64,
|
||||||
|
pub ModelVersion:u64,
|
||||||
|
pub DisplayName:String,
|
||||||
|
pub Creator:String,
|
||||||
|
pub GameID:u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(nonstandard_style)]
|
||||||
|
#[derive(Clone,Debug)]
|
||||||
|
pub struct ActionMapfixRequestChangesRequest{
|
||||||
|
pub MapfixID:i64,
|
||||||
|
pub ErrorMessage:String,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone,Debug)]
|
||||||
pub struct ActionMapfixUploadedRequest{
|
pub struct ActionMapfixUploadedRequest{
|
||||||
@@ -257,7 +299,7 @@ pub struct ActionMapfixUploadedRequest{
|
|||||||
#[derive(Clone,Debug)]
|
#[derive(Clone,Debug)]
|
||||||
pub struct ActionMapfixAcceptedRequest{
|
pub struct ActionMapfixAcceptedRequest{
|
||||||
pub MapfixID:i64,
|
pub MapfixID:i64,
|
||||||
pub StatusMessage:String,
|
pub ErrorMessage:String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug,serde::Deserialize)]
|
#[derive(Clone,Copy,Debug,serde::Deserialize)]
|
||||||
|
|||||||
9
validation/cli/Cargo.toml
Normal file
9
validation/cli/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "cli"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5.39", features = ["derive"] }
|
||||||
|
maps-validation = { path = ".." }
|
||||||
|
rbx_binary = "1.0.0"
|
||||||
73
validation/cli/src/main.rs
Normal file
73
validation/cli/src/main.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
use clap::{Args,Parser,Subcommand};
|
||||||
|
use std::path::{Path,PathBuf};
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(author,version,about,long_about=None)]
|
||||||
|
#[command(propagate_version=true)]
|
||||||
|
struct Cli{
|
||||||
|
#[command(subcommand)]
|
||||||
|
command:Commands,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum Commands{
|
||||||
|
Check(CheckCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
struct CheckCommand{
|
||||||
|
files:Vec<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main(){
|
||||||
|
let cli=Cli::parse();
|
||||||
|
match cli.command{
|
||||||
|
Commands::Check(command)=>command.run().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum CheckError{
|
||||||
|
Io(std::io::Error),
|
||||||
|
Binary(maps_validation::rbx_util::ReadDomError),
|
||||||
|
CheckDom(maps_validation::rbx_util::GetRootInstanceError),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check(path:&Path)->Result<(),CheckError>{
|
||||||
|
let name=path.file_name().unwrap_or_default().to_str().unwrap_or_default();
|
||||||
|
let file=std::fs::read(path).map_err(CheckError::Io)?;
|
||||||
|
|
||||||
|
let dom=maps_validation::rbx_util::read_dom(file.as_slice()).map_err(CheckError::Binary)?;
|
||||||
|
|
||||||
|
let check=maps_validation::message_handler::MessageHandler::check_dom(&dom).map_err(CheckError::CheckDom)?;
|
||||||
|
match check.result(){
|
||||||
|
Ok(_map_info)=>(),//println!("good {}",map_info.display_name),
|
||||||
|
Err(Ok(check_list))=>println!("bad {name} Error: {}",check_list.summary()),
|
||||||
|
Err(Err(e))=>println!("ugly {name} Error: {e}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CheckCommand{
|
||||||
|
fn run(self)->Result<(),()>{
|
||||||
|
let mut handles=Vec::new();
|
||||||
|
for path in self.files{
|
||||||
|
if path.is_file(){
|
||||||
|
handles.push(std::thread::spawn(move||{
|
||||||
|
if let Err(e)=check(path.as_path()){
|
||||||
|
let name=path.file_name().unwrap_or_default().to_str().unwrap_or_default();
|
||||||
|
println!("ugly {name} Error: {e:?}");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for handle in handles{
|
||||||
|
handle.join().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
855
validation/src/check.rs
Normal file
855
validation/src/check.rs
Normal file
@@ -0,0 +1,855 @@
|
|||||||
|
use std::collections::{HashSet,HashMap};
|
||||||
|
use crate::download::download_asset_version;
|
||||||
|
use crate::rbx_util::{class_is_a,get_mapinfo,get_root_instance,read_dom,ReadDomError,GameID,ParseGameIDError,MapInfo,GetRootInstanceError,StringValueError};
|
||||||
|
|
||||||
|
use heck::{ToSnakeCase,ToTitleCase};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error{
|
||||||
|
ModelInfoDownload(rbx_asset::cloud::GetError),
|
||||||
|
CreatorTypeMustBeUser,
|
||||||
|
Download(crate::download::Error),
|
||||||
|
ModelFileDecode(ReadDomError),
|
||||||
|
GetRootInstance(GetRootInstanceError),
|
||||||
|
ToJsonValue(serde_json::Error),
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for Error{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for Error{}
|
||||||
|
|
||||||
|
#[allow(nonstandard_style)]
|
||||||
|
pub struct CheckRequest{
|
||||||
|
pub ModelID:u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::nats_types::CheckMapfixRequest> for CheckRequest{
|
||||||
|
fn from(value:crate::nats_types::CheckMapfixRequest)->Self{
|
||||||
|
Self{
|
||||||
|
ModelID:value.ModelID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<crate::nats_types::CheckSubmissionRequest> for CheckRequest{
|
||||||
|
fn from(value:crate::nats_types::CheckSubmissionRequest)->Self{
|
||||||
|
Self{
|
||||||
|
ModelID:value.ModelID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
||||||
|
struct ModeID(u64);
|
||||||
|
impl ModeID{
|
||||||
|
const MAIN:Self=Self(0);
|
||||||
|
const BONUS:Self=Self(1);
|
||||||
|
}
|
||||||
|
enum Zone{
|
||||||
|
Start,
|
||||||
|
Finish,
|
||||||
|
Anticheat,
|
||||||
|
}
|
||||||
|
struct ModeElement{
|
||||||
|
zone:Zone,
|
||||||
|
mode_id:ModeID,
|
||||||
|
}
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum IDParseError{
|
||||||
|
NoCaptures,
|
||||||
|
ParseInt(core::num::ParseIntError),
|
||||||
|
}
|
||||||
|
// Parse a Zone from a part name
|
||||||
|
impl std::str::FromStr for ModeElement{
|
||||||
|
type Err=IDParseError;
|
||||||
|
fn from_str(s:&str)->Result<Self,Self::Err>{
|
||||||
|
match s{
|
||||||
|
"MapStart"=>Ok(Self{zone:Zone::Start,mode_id:ModeID::MAIN}),
|
||||||
|
"MapFinish"=>Ok(Self{zone:Zone::Finish,mode_id:ModeID::MAIN}),
|
||||||
|
"MapAnticheat"=>Ok(Self{zone:Zone::Anticheat,mode_id:ModeID::MAIN}),
|
||||||
|
"BonusStart"=>Ok(Self{zone:Zone::Start,mode_id:ModeID::BONUS}),
|
||||||
|
"BonusFinish"=>Ok(Self{zone:Zone::Finish,mode_id:ModeID::BONUS}),
|
||||||
|
"BonusAnticheat"=>Ok(Self{zone:Zone::Anticheat,mode_id:ModeID::BONUS}),
|
||||||
|
other=>{
|
||||||
|
let everything_pattern=lazy_regex::lazy_regex!(r"^Bonus(\d+)Start$|^BonusStart(\d+)$|^Bonus(\d+)Finish$|^BonusFinish(\d+)$|^Bonus(\d+)Anticheat$|^BonusAnticheat(\d+)$");
|
||||||
|
if let Some(captures)=everything_pattern.captures(other){
|
||||||
|
if let Some(mode_id)=captures.get(1).or(captures.get(2)){
|
||||||
|
return Ok(Self{
|
||||||
|
zone:Zone::Start,
|
||||||
|
mode_id:ModeID(mode_id.as_str().parse().map_err(IDParseError::ParseInt)?),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(mode_id)=captures.get(3).or(captures.get(4)){
|
||||||
|
return Ok(Self{
|
||||||
|
zone:Zone::Finish,
|
||||||
|
mode_id:ModeID(mode_id.as_str().parse().map_err(IDParseError::ParseInt)?),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(mode_id)=captures.get(5).or(captures.get(6)){
|
||||||
|
return Ok(Self{
|
||||||
|
zone:Zone::Anticheat,
|
||||||
|
mode_id:ModeID(mode_id.as_str().parse().map_err(IDParseError::ParseInt)?),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(IDParseError::NoCaptures)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for ModeElement{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
match self{
|
||||||
|
ModeElement{zone:Zone::Start,mode_id:ModeID::MAIN}=>write!(f,"MapStart"),
|
||||||
|
ModeElement{zone:Zone::Start,mode_id:ModeID::BONUS}=>write!(f,"BonusStart"),
|
||||||
|
ModeElement{zone:Zone::Start,mode_id:ModeID(mode_id)}=>write!(f,"Bonus{mode_id}Start"),
|
||||||
|
ModeElement{zone:Zone::Finish,mode_id:ModeID::MAIN}=>write!(f,"MapFinish"),
|
||||||
|
ModeElement{zone:Zone::Finish,mode_id:ModeID::BONUS}=>write!(f,"BonusFinish"),
|
||||||
|
ModeElement{zone:Zone::Finish,mode_id:ModeID(mode_id)}=>write!(f,"Bonus{mode_id}Finish"),
|
||||||
|
ModeElement{zone:Zone::Anticheat,mode_id:ModeID::MAIN}=>write!(f,"MapAnticheat"),
|
||||||
|
ModeElement{zone:Zone::Anticheat,mode_id:ModeID::BONUS}=>write!(f,"BonusAnticheat"),
|
||||||
|
ModeElement{zone:Zone::Anticheat,mode_id:ModeID(mode_id)}=>write!(f,"Bonus{mode_id}Anticheat"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
||||||
|
struct StageID(u64);
|
||||||
|
impl StageID{
|
||||||
|
const FIRST:Self=Self(1);
|
||||||
|
}
|
||||||
|
enum StageElementBehaviour{
|
||||||
|
Teleport,
|
||||||
|
Spawn,
|
||||||
|
}
|
||||||
|
struct StageElement{
|
||||||
|
stage_id:StageID,
|
||||||
|
behaviour:StageElementBehaviour,
|
||||||
|
}
|
||||||
|
// Parse a SpawnTeleport from a part name
|
||||||
|
impl std::str::FromStr for StageElement{
|
||||||
|
type Err=IDParseError;
|
||||||
|
fn from_str(s:&str)->Result<Self,Self::Err>{
|
||||||
|
// Trigger ForceTrigger Teleport ForceTeleport SpawnAt ForceSpawnAt
|
||||||
|
let bonus_start_pattern=lazy_regex::lazy_regex!(r"^(?:Force)?(Teleport|SpawnAt|Trigger)(\d+)$");
|
||||||
|
if let Some(captures)=bonus_start_pattern.captures(s){
|
||||||
|
return Ok(StageElement{
|
||||||
|
behaviour:StageElementBehaviour::Teleport,
|
||||||
|
stage_id:StageID(captures[1].parse().map_err(IDParseError::ParseInt)?),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Spawn
|
||||||
|
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^Spawn(\d+)$");
|
||||||
|
if let Some(captures)=bonus_finish_pattern.captures(s){
|
||||||
|
return Ok(StageElement{
|
||||||
|
behaviour:StageElementBehaviour::Spawn,
|
||||||
|
stage_id:StageID(captures[1].parse().map_err(IDParseError::ParseInt)?),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(IDParseError::NoCaptures)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for StageElement{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
match self{
|
||||||
|
StageElement{behaviour:StageElementBehaviour::Spawn,stage_id:StageID(stage_id)}=>write!(f,"Spawn{stage_id}"),
|
||||||
|
StageElement{behaviour:StageElementBehaviour::Teleport,stage_id:StageID(stage_id)}=>write!(f,"Teleport{stage_id}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
||||||
|
struct WormholeID(u64);
|
||||||
|
enum WormholeBehaviour{
|
||||||
|
In,
|
||||||
|
Out,
|
||||||
|
}
|
||||||
|
struct WormholeElement{
|
||||||
|
behaviour:WormholeBehaviour,
|
||||||
|
wormhole_id:WormholeID,
|
||||||
|
}
|
||||||
|
// Parse a Wormhole from a part name
|
||||||
|
impl std::str::FromStr for WormholeElement{
|
||||||
|
type Err=IDParseError;
|
||||||
|
fn from_str(s:&str)->Result<Self,Self::Err>{
|
||||||
|
let bonus_start_pattern=lazy_regex::lazy_regex!(r"^WormholeIn(\d+)$");
|
||||||
|
if let Some(captures)=bonus_start_pattern.captures(s){
|
||||||
|
return Ok(Self{
|
||||||
|
behaviour:WormholeBehaviour::In,
|
||||||
|
wormhole_id:WormholeID(captures[1].parse().map_err(IDParseError::ParseInt)?),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^WormholeOut(\d+)$");
|
||||||
|
if let Some(captures)=bonus_finish_pattern.captures(s){
|
||||||
|
return Ok(Self{
|
||||||
|
behaviour:WormholeBehaviour::Out,
|
||||||
|
wormhole_id:WormholeID(captures[1].parse().map_err(IDParseError::ParseInt)?),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(IDParseError::NoCaptures)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for WormholeElement{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
match self{
|
||||||
|
WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id:WormholeID(wormhole_id)}=>write!(f,"WormholeIn{wormhole_id}"),
|
||||||
|
WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id:WormholeID(wormhole_id)}=>write!(f,"WormholeOut{wormhole_id}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Count various map elements
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Counts<'a>{
|
||||||
|
mode_start_counts:HashMap<ModeID,Vec<&'a str>>,
|
||||||
|
mode_finish_counts:HashMap<ModeID,Vec<&'a str>>,
|
||||||
|
mode_anticheat_counts:HashMap<ModeID,Vec<&'a str>>,
|
||||||
|
teleport_counts:HashMap<StageID,Vec<&'a str>>,
|
||||||
|
spawn_counts:HashMap<StageID,u64>,
|
||||||
|
wormhole_in_counts:HashMap<WormholeID,u64>,
|
||||||
|
wormhole_out_counts:HashMap<WormholeID,u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ModelInfo<'a>{
|
||||||
|
model_class:&'a str,
|
||||||
|
model_name:&'a str,
|
||||||
|
map_info:MapInfo<'a>,
|
||||||
|
counts:Counts<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_model_info<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&'a rbx_dom_weak::Instance)->ModelInfo<'a>{
|
||||||
|
// extract model info
|
||||||
|
let map_info=get_mapinfo(dom,model_instance);
|
||||||
|
|
||||||
|
// count objects (default count is 0)
|
||||||
|
let mut counts=Counts::default();
|
||||||
|
for instance in dom.descendants_of(model_instance.referent()){
|
||||||
|
if class_is_a(instance.class.as_str(),"BasePart"){
|
||||||
|
// Zones
|
||||||
|
match instance.name.parse(){
|
||||||
|
Ok(ModeElement{zone:Zone::Start,mode_id})=>counts.mode_start_counts.entry(mode_id).or_default().push(instance.name.as_str()),
|
||||||
|
Ok(ModeElement{zone:Zone::Finish,mode_id})=>counts.mode_finish_counts.entry(mode_id).or_default().push(instance.name.as_str()),
|
||||||
|
Ok(ModeElement{zone:Zone::Anticheat,mode_id})=>counts.mode_anticheat_counts.entry(mode_id).or_default().push(instance.name.as_str()),
|
||||||
|
Err(_)=>(),
|
||||||
|
}
|
||||||
|
// Spawns & Teleports
|
||||||
|
match instance.name.parse(){
|
||||||
|
Ok(StageElement{behaviour:StageElementBehaviour::Teleport,stage_id})=>counts.teleport_counts.entry(stage_id).or_default().push(instance.name.as_str()),
|
||||||
|
Ok(StageElement{behaviour:StageElementBehaviour::Spawn,stage_id})=>*counts.spawn_counts.entry(stage_id).or_insert(0)+=1,
|
||||||
|
Err(_)=>(),
|
||||||
|
}
|
||||||
|
// Wormholes
|
||||||
|
match instance.name.parse(){
|
||||||
|
Ok(WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id})=>*counts.wormhole_in_counts.entry(wormhole_id).or_insert(0)+=1,
|
||||||
|
Ok(WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id})=>*counts.wormhole_out_counts.entry(wormhole_id).or_insert(0)+=1,
|
||||||
|
Err(_)=>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelInfo{
|
||||||
|
model_class:model_instance.class.as_str(),
|
||||||
|
model_name:model_instance.name.as_str(),
|
||||||
|
map_info,
|
||||||
|
counts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if an observed string matches an expected string
|
||||||
|
pub struct StringCheck<'a,T,Str>(Result<T,StringCheckContext<'a,Str>>);
|
||||||
|
pub struct StringCheckContext<'a,Str>{
|
||||||
|
observed:&'a str,
|
||||||
|
expected:Str,
|
||||||
|
}
|
||||||
|
impl<'a,Str> StringCheckContext<'a,Str>
|
||||||
|
where
|
||||||
|
&'a str:PartialEq<Str>,
|
||||||
|
{
|
||||||
|
/// Compute the StringCheck, passing through the provided value on success.
|
||||||
|
fn check<T>(self,value:T)->StringCheck<'a,T,Str>{
|
||||||
|
if self.observed==self.expected{
|
||||||
|
StringCheck(Ok(value))
|
||||||
|
}else{
|
||||||
|
StringCheck(Err(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<Str:std::fmt::Display> std::fmt::Display for StringCheckContext<'_,Str>{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"expected: {}, observed: {}",self.expected,self.observed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a string is empty
|
||||||
|
pub struct StringEmpty;
|
||||||
|
impl std::fmt::Display for StringEmpty{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"Empty string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn check_empty(value:&str)->Result<&str,StringEmpty>{
|
||||||
|
(!value.is_empty()).then_some(value).ok_or(StringEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for duplicate objects
|
||||||
|
pub struct DuplicateCheckContext<ID,T>(HashMap<ID,T>);
|
||||||
|
pub struct DuplicateCheck<ID,T>(Result<(),DuplicateCheckContext<ID,T>>);
|
||||||
|
impl<ID,T> DuplicateCheckContext<ID,T>{
|
||||||
|
/// Compute the DuplicateCheck using the contents predicate.
|
||||||
|
fn check(self,f:impl Fn(&T)->bool)->DuplicateCheck<ID,T>{
|
||||||
|
let Self(mut set)=self;
|
||||||
|
// remove correct entries
|
||||||
|
set.retain(|_,c|f(c));
|
||||||
|
// if any entries remain, they are incorrect
|
||||||
|
if set.is_empty(){
|
||||||
|
DuplicateCheck(Ok(()))
|
||||||
|
}else{
|
||||||
|
DuplicateCheck(Err(Self(set)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that there are no items which do not have a matching item in a reference set
|
||||||
|
pub struct SetDifferenceCheckContextAllowNone<ID,T>{
|
||||||
|
extra:HashMap<ID,T>,
|
||||||
|
}
|
||||||
|
// Check that there is at least one matching item for each item in a reference set, and no extra items
|
||||||
|
pub struct SetDifferenceCheckContextAtLeastOne<ID,T>{
|
||||||
|
extra:HashMap<ID,T>,
|
||||||
|
missing:HashSet<ID>,
|
||||||
|
}
|
||||||
|
pub struct SetDifferenceCheck<Context>(Result<(),Context>);
|
||||||
|
impl<ID,T> SetDifferenceCheckContextAllowNone<ID,T>{
|
||||||
|
fn new(initial_set:HashMap<ID,T>)->Self{
|
||||||
|
Self{
|
||||||
|
extra:initial_set,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<ID:Eq+std::hash::Hash,T> SetDifferenceCheckContextAllowNone<ID,T>{
|
||||||
|
/// Compute the SetDifferenceCheck result for the specified reference set.
|
||||||
|
fn check<U>(mut self,reference_set:&HashMap<ID,U>)->SetDifferenceCheck<Self>{
|
||||||
|
// remove correct entries
|
||||||
|
for id in reference_set.keys(){
|
||||||
|
self.extra.remove(id);
|
||||||
|
}
|
||||||
|
// if any entries remain, they are incorrect
|
||||||
|
if self.extra.is_empty(){
|
||||||
|
SetDifferenceCheck(Ok(()))
|
||||||
|
}else{
|
||||||
|
SetDifferenceCheck(Err(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<ID,T> SetDifferenceCheckContextAtLeastOne<ID,T>{
|
||||||
|
fn new(initial_set:HashMap<ID,T>)->Self{
|
||||||
|
Self{
|
||||||
|
extra:initial_set,
|
||||||
|
missing:HashSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<ID:Copy+Eq+std::hash::Hash,T> SetDifferenceCheckContextAtLeastOne<ID,T>{
|
||||||
|
/// Compute the SetDifferenceCheck result for the specified reference set.
|
||||||
|
fn check<U>(mut self,reference_set:&HashMap<ID,U>)->SetDifferenceCheck<Self>{
|
||||||
|
// remove correct entries
|
||||||
|
for id in reference_set.keys(){
|
||||||
|
if self.extra.remove(id).is_none(){
|
||||||
|
// the set did not contain a required item. This is a fail
|
||||||
|
self.missing.insert(*id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if any entries remain, they are incorrect
|
||||||
|
if self.extra.is_empty()&&self.missing.is_empty(){
|
||||||
|
SetDifferenceCheck(Ok(()))
|
||||||
|
}else{
|
||||||
|
SetDifferenceCheck(Err(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Info lifted out of a fully compliant map
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MapInfoOwned{
|
||||||
|
pub display_name:String,
|
||||||
|
pub creator:String,
|
||||||
|
pub game_id:GameID,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Named dummy types for readability
|
||||||
|
struct Exists;
|
||||||
|
struct Absent;
|
||||||
|
|
||||||
|
/// The result of every map check.
|
||||||
|
pub struct MapCheck<'a>{
|
||||||
|
// === METADATA CHECKS ===
|
||||||
|
// The root must be of class Model
|
||||||
|
model_class:StringCheck<'a,(),&'static str>,
|
||||||
|
// Model's name must be in snake case
|
||||||
|
model_name:StringCheck<'a,(),String>,
|
||||||
|
// Map must have a StringValue named DisplayName.
|
||||||
|
// Value must not be empty, must be in title case.
|
||||||
|
display_name:Result<Result<StringCheck<'a,&'a str,String>,StringEmpty>,StringValueError>,
|
||||||
|
// Map must have a StringValue named Creator.
|
||||||
|
// Value must not be empty.
|
||||||
|
creator:Result<Result<&'a str,StringEmpty>,StringValueError>,
|
||||||
|
// The prefix of the model's name must match the game it was submitted for.
|
||||||
|
// bhop_ for bhop, and surf_ for surf
|
||||||
|
game_id:Result<GameID,ParseGameIDError>,
|
||||||
|
|
||||||
|
// === MODE CHECKS ===
|
||||||
|
// MapStart must exist
|
||||||
|
mapstart:Result<Exists,Absent>,
|
||||||
|
// No duplicate map starts (including bonuses)
|
||||||
|
mode_start_counts:DuplicateCheck<ModeID,Vec<&'a str>>,
|
||||||
|
// At least one finish zone for each start zone, and no finishes with no start
|
||||||
|
mode_finish_counts:SetDifferenceCheck<SetDifferenceCheckContextAtLeastOne<ModeID,Vec<&'a str>>>,
|
||||||
|
// Check for dangling MapAnticheat zones (no associated MapStart)
|
||||||
|
mode_anticheat_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<ModeID,Vec<&'a str>>>,
|
||||||
|
// Spawn1 must exist
|
||||||
|
spawn1:Result<Exists,Absent>,
|
||||||
|
// Check for dangling Teleport# (no associated Spawn#)
|
||||||
|
teleport_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<StageID,Vec<&'a str>>>,
|
||||||
|
// No duplicate Spawn#
|
||||||
|
spawn_counts:DuplicateCheck<StageID,u64>,
|
||||||
|
// Check for dangling WormholeIn# (no associated WormholeOut#)
|
||||||
|
wormhole_in_counts:SetDifferenceCheck<SetDifferenceCheckContextAtLeastOne<WormholeID,u64>>,
|
||||||
|
// No duplicate WormholeOut# (duplicate WormholeIn# ok)
|
||||||
|
// No dangling WormholeOut#
|
||||||
|
wormhole_out_counts:DuplicateCheck<WormholeID,u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ModelInfo<'a>{
|
||||||
|
fn check(self)->MapCheck<'a>{
|
||||||
|
// Check class is exactly "Model"
|
||||||
|
let model_class=StringCheckContext{
|
||||||
|
observed:self.model_class,
|
||||||
|
expected:"Model",
|
||||||
|
}.check(());
|
||||||
|
|
||||||
|
// Check model name is snake case
|
||||||
|
let model_name=StringCheckContext{
|
||||||
|
observed:self.model_name,
|
||||||
|
expected:self.model_name.to_snake_case(),
|
||||||
|
}.check(());
|
||||||
|
|
||||||
|
// Check display name is not empty and has title case
|
||||||
|
let display_name=self.map_info.display_name.map(|display_name|{
|
||||||
|
check_empty(display_name).map(|display_name|StringCheckContext{
|
||||||
|
observed:display_name,
|
||||||
|
expected:display_name.to_title_case(),
|
||||||
|
}.check(display_name))
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check Creator is not empty
|
||||||
|
let creator=self.map_info.creator.map(check_empty);
|
||||||
|
|
||||||
|
// Check GameID (model name was prefixed with bhop_ surf_ etc)
|
||||||
|
let game_id=self.map_info.game_id;
|
||||||
|
|
||||||
|
// MapStart must exist
|
||||||
|
let mapstart=if self.counts.mode_start_counts.contains_key(&ModeID::MAIN){
|
||||||
|
Ok(Exists)
|
||||||
|
}else{
|
||||||
|
Err(Absent)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Spawn1 must exist
|
||||||
|
let spawn1=if self.counts.spawn_counts.contains_key(&StageID::FIRST){
|
||||||
|
Ok(Exists)
|
||||||
|
}else{
|
||||||
|
Err(Absent)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check that at least one finish zone exists for each start zone.
|
||||||
|
// This also checks that there are no finish zones without a corresponding start zone.
|
||||||
|
let mode_finish_counts=SetDifferenceCheckContextAtLeastOne::new(self.counts.mode_finish_counts)
|
||||||
|
.check(&self.counts.mode_start_counts);
|
||||||
|
|
||||||
|
// Check that there are no anticheat zones without a corresponding start zone.
|
||||||
|
// Modes are allowed to have 0 anticheat zones.
|
||||||
|
let mode_anticheat_counts=SetDifferenceCheckContextAllowNone::new(self.counts.mode_anticheat_counts)
|
||||||
|
.check(&self.counts.mode_start_counts);
|
||||||
|
|
||||||
|
// There must be exactly one start zone for every mode in the map.
|
||||||
|
let mode_start_counts=DuplicateCheckContext(self.counts.mode_start_counts).check(|c|1<c.len());
|
||||||
|
|
||||||
|
// Check that there are no Teleports without a corresponding Spawn.
|
||||||
|
// Spawns are allowed to have 0 Teleports.
|
||||||
|
let teleport_counts=SetDifferenceCheckContextAllowNone::new(self.counts.teleport_counts)
|
||||||
|
.check(&self.counts.spawn_counts);
|
||||||
|
|
||||||
|
// There must be exactly one of any perticular spawn id in the map.
|
||||||
|
let spawn_counts=DuplicateCheckContext(self.counts.spawn_counts).check(|&c|1<c);
|
||||||
|
|
||||||
|
// Check that at least one WormholeIn exists for each WormholeOut.
|
||||||
|
// This also checks that there are no WormholeIn without a corresponding WormholeOut.
|
||||||
|
let wormhole_in_counts=SetDifferenceCheckContextAtLeastOne::new(self.counts.wormhole_in_counts)
|
||||||
|
.check(&self.counts.wormhole_out_counts);
|
||||||
|
|
||||||
|
// There must be exactly one of any perticular wormhole out id in the map.
|
||||||
|
let wormhole_out_counts=DuplicateCheckContext(self.counts.wormhole_out_counts).check(|&c|1<c);
|
||||||
|
|
||||||
|
MapCheck{
|
||||||
|
model_class,
|
||||||
|
model_name,
|
||||||
|
display_name,
|
||||||
|
creator,
|
||||||
|
game_id,
|
||||||
|
mapstart,
|
||||||
|
mode_start_counts,
|
||||||
|
mode_finish_counts,
|
||||||
|
mode_anticheat_counts,
|
||||||
|
spawn1,
|
||||||
|
teleport_counts,
|
||||||
|
spawn_counts,
|
||||||
|
wormhole_in_counts,
|
||||||
|
wormhole_out_counts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapCheck<'_>{
|
||||||
|
pub fn result(self)->Result<MapInfoOwned,Result<MapCheckList,serde_json::Error>>{
|
||||||
|
match self{
|
||||||
|
MapCheck{
|
||||||
|
model_class:StringCheck(Ok(())),
|
||||||
|
model_name:StringCheck(Ok(())),
|
||||||
|
display_name:Ok(Ok(StringCheck(Ok(display_name)))),
|
||||||
|
creator:Ok(Ok(creator)),
|
||||||
|
game_id:Ok(game_id),
|
||||||
|
mapstart:Ok(Exists),
|
||||||
|
mode_start_counts:DuplicateCheck(Ok(())),
|
||||||
|
mode_finish_counts:SetDifferenceCheck(Ok(())),
|
||||||
|
mode_anticheat_counts:SetDifferenceCheck(Ok(())),
|
||||||
|
spawn1:Ok(Exists),
|
||||||
|
teleport_counts:SetDifferenceCheck(Ok(())),
|
||||||
|
spawn_counts:DuplicateCheck(Ok(())),
|
||||||
|
wormhole_in_counts:SetDifferenceCheck(Ok(())),
|
||||||
|
wormhole_out_counts:DuplicateCheck(Ok(())),
|
||||||
|
}=>{
|
||||||
|
Ok(MapInfoOwned{
|
||||||
|
display_name:display_name.to_owned(),
|
||||||
|
creator:creator.to_owned(),
|
||||||
|
game_id,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
other=>Err(other.itemize()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Separated<F>{
|
||||||
|
f:F,
|
||||||
|
separator:&'static str,
|
||||||
|
}
|
||||||
|
impl<F> Separated<F>{
|
||||||
|
fn new(separator:&'static str,f:F)->Self{
|
||||||
|
Self{separator,f}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<F,I,D> std::fmt::Display for Separated<F>
|
||||||
|
where
|
||||||
|
D:std::fmt::Display,
|
||||||
|
I:IntoIterator<Item=D>,
|
||||||
|
F:Fn()->I,
|
||||||
|
{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
let mut it=(self.f)().into_iter();
|
||||||
|
if let Some(first)=it.next(){
|
||||||
|
write!(f,"{first}")?;
|
||||||
|
for item in it{
|
||||||
|
write!(f,"{}{item}",self.separator)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Duplicates<D>{
|
||||||
|
display:D,
|
||||||
|
duplicates:usize,
|
||||||
|
}
|
||||||
|
impl<D> Duplicates<D>{
|
||||||
|
fn new(display:D,duplicates:usize)->Self{
|
||||||
|
Self{
|
||||||
|
display,
|
||||||
|
duplicates,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<D:std::fmt::Display> std::fmt::Display for Duplicates<D>{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{} ({} duplicates)",self.display,self.duplicates)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
pub struct CheckSummary{
|
||||||
|
name:&'static str,
|
||||||
|
summary:String,
|
||||||
|
pub passed:bool,
|
||||||
|
details:serde_json::Value,
|
||||||
|
}
|
||||||
|
impl CheckSummary{
|
||||||
|
const fn passed(name:&'static str)->Self{
|
||||||
|
Self{
|
||||||
|
name,
|
||||||
|
summary:String::new(),
|
||||||
|
passed:true,
|
||||||
|
details:serde_json::Value::Null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
macro_rules! summary{
|
||||||
|
($name:literal,$summary:expr,$details:expr)=>{
|
||||||
|
CheckSummary{
|
||||||
|
name:$name,
|
||||||
|
summary:$summary,
|
||||||
|
passed:false,
|
||||||
|
details:serde_json::to_value($details)?,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! summary_format{
|
||||||
|
($name:literal,$fmt:literal,$details:expr)=>{
|
||||||
|
CheckSummary{
|
||||||
|
name:$name,
|
||||||
|
summary:format!($fmt),
|
||||||
|
passed:false,
|
||||||
|
details:serde_json::to_value($details)?,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate an error message for each observed issue separated by newlines.
|
||||||
|
// This defines MapCheck.to_string() which is used in MapCheck.result()
|
||||||
|
impl MapCheck<'_>{
|
||||||
|
fn itemize(&self)->Result<MapCheckList,serde_json::Error>{
|
||||||
|
let model_class=match &self.model_class{
|
||||||
|
StringCheck(Ok(()))=>CheckSummary::passed("ModelClass"),
|
||||||
|
StringCheck(Err(context))=>summary_format!("ModelClass","Invalid model class: {context}",()),
|
||||||
|
};
|
||||||
|
let model_name=match &self.model_name{
|
||||||
|
StringCheck(Ok(()))=>CheckSummary::passed("ModelName"),
|
||||||
|
StringCheck(Err(context))=>summary_format!("ModelName","Model name must have snake_case: {context}",()),
|
||||||
|
};
|
||||||
|
let display_name=match &self.display_name{
|
||||||
|
Ok(Ok(StringCheck(Ok(_))))=>CheckSummary::passed("DisplayName"),
|
||||||
|
Ok(Ok(StringCheck(Err(context))))=>summary_format!("DisplayName","DisplayName must have Title Case: {context}",()),
|
||||||
|
Ok(Err(context))=>summary_format!("DisplayName","Invalid DisplayName: {context}",()),
|
||||||
|
Err(StringValueError::ObjectNotFound)=>summary!("DisplayName","Missing DisplayName StringValue".to_owned(),()),
|
||||||
|
Err(StringValueError::ValueNotSet)=>summary!("DisplayName","DisplayName Value not set".to_owned(),()),
|
||||||
|
Err(StringValueError::NonStringValue)=>summary!("DisplayName","DisplayName Value is not a String".to_owned(),()),
|
||||||
|
};
|
||||||
|
let creator=match &self.creator{
|
||||||
|
Ok(Ok(_))=>CheckSummary::passed("Creator"),
|
||||||
|
Ok(Err(context))=>summary_format!("Creator","Invalid Creator: {context}",()),
|
||||||
|
Err(StringValueError::ObjectNotFound)=>summary!("Creator","Missing Creator StringValue".to_owned(),()),
|
||||||
|
Err(StringValueError::ValueNotSet)=>summary!("Creator","Creator Value not set".to_owned(),()),
|
||||||
|
Err(StringValueError::NonStringValue)=>summary!("Creator","Creator Value is not a String".to_owned(),()),
|
||||||
|
};
|
||||||
|
let game_id=match &self.game_id{
|
||||||
|
Ok(_)=>CheckSummary::passed("GameID"),
|
||||||
|
Err(ParseGameIDError)=>summary!("GameID","Model name must be prefixed with bhop_ surf_ or flytrials_".to_owned(),()),
|
||||||
|
};
|
||||||
|
let mapstart=match &self.mapstart{
|
||||||
|
Ok(Exists)=>CheckSummary::passed("MapStart"),
|
||||||
|
Err(Absent)=>summary_format!("MapStart","Model has no MapStart",()),
|
||||||
|
};
|
||||||
|
let duplicate_start=match &self.mode_start_counts{
|
||||||
|
DuplicateCheck(Ok(()))=>CheckSummary::passed("DuplicateStart"),
|
||||||
|
DuplicateCheck(Err(DuplicateCheckContext(context)))=>{
|
||||||
|
let context=Separated::new(", ",||context.iter().map(|(&mode_id,names)|
|
||||||
|
Duplicates::new(ModeElement{zone:Zone::Start,mode_id},names.len())
|
||||||
|
));
|
||||||
|
summary_format!("DuplicateStart","Duplicate start zones: {context}",())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (extra_finish,missing_finish)=match &self.mode_finish_counts{
|
||||||
|
SetDifferenceCheck(Ok(()))=>(CheckSummary::passed("ExtraFinish"),CheckSummary::passed("MissingFinish")),
|
||||||
|
SetDifferenceCheck(Err(context))=>(
|
||||||
|
if context.extra.is_empty(){
|
||||||
|
CheckSummary::passed("ExtraFinish")
|
||||||
|
}else{
|
||||||
|
let plural=if context.extra.len()==1{"zone"}else{"zones"};
|
||||||
|
let context=Separated::new(", ",||context.extra.iter().map(|(&mode_id,_names)|
|
||||||
|
ModeElement{zone:Zone::Finish,mode_id}
|
||||||
|
));
|
||||||
|
summary_format!("ExtraFinish","No matching start zone for finish {plural}: {context}",())
|
||||||
|
},
|
||||||
|
if context.missing.is_empty(){
|
||||||
|
CheckSummary::passed("MissingFinish")
|
||||||
|
}else{
|
||||||
|
let plural=if context.missing.len()==1{"zone"}else{"zones"};
|
||||||
|
let context=Separated::new(", ",||context.missing.iter().map(|&mode_id|
|
||||||
|
ModeElement{zone:Zone::Finish,mode_id}
|
||||||
|
));
|
||||||
|
summary_format!("MissingFinish","Missing finish {plural}: {context}",())
|
||||||
|
}
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let dangling_anticheat=match &self.mode_anticheat_counts{
|
||||||
|
SetDifferenceCheck(Ok(()))=>CheckSummary::passed("DanglingAnticheat"),
|
||||||
|
SetDifferenceCheck(Err(context))=>{
|
||||||
|
if context.extra.is_empty(){
|
||||||
|
CheckSummary::passed("DanglingAnticheat")
|
||||||
|
}else{
|
||||||
|
let plural=if context.extra.len()==1{"zone"}else{"zones"};
|
||||||
|
let context=Separated::new(", ",||context.extra.iter().map(|(&mode_id,_names)|
|
||||||
|
ModeElement{zone:Zone::Anticheat,mode_id}
|
||||||
|
));
|
||||||
|
summary_format!("DanglingAnticheat","No matching start zone for anticheat {plural}: {context}",())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let spawn1=match &self.spawn1{
|
||||||
|
Ok(Exists)=>CheckSummary::passed("Spawn1"),
|
||||||
|
Err(Absent)=>summary_format!("Spawn1","Model has no Spawn1",()),
|
||||||
|
};
|
||||||
|
let dangling_teleport=match &self.teleport_counts{
|
||||||
|
SetDifferenceCheck(Ok(()))=>CheckSummary::passed("DanglingTeleport"),
|
||||||
|
SetDifferenceCheck(Err(context))=>{
|
||||||
|
let unique_names:HashSet<_>=context.extra.values().flat_map(|names|names.iter().copied()).collect();
|
||||||
|
let plural=if unique_names.len()==1{"object"}else{"objects"};
|
||||||
|
let context=Separated::new(", ",||&unique_names);
|
||||||
|
summary_format!("DanglingTeleport","No matching Spawn for {plural}: {context}",())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let duplicate_spawns=match &self.spawn_counts{
|
||||||
|
DuplicateCheck(Ok(()))=>CheckSummary::passed("DuplicateSpawn"),
|
||||||
|
DuplicateCheck(Err(DuplicateCheckContext(context)))=>{
|
||||||
|
let context=Separated::new(", ",||context.iter().map(|(&stage_id,&names)|
|
||||||
|
Duplicates::new(StageElement{behaviour:StageElementBehaviour::Spawn,stage_id},names as usize)
|
||||||
|
));
|
||||||
|
summary_format!("DuplicateSpawn","Duplicate Spawn: {context}",())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (extra_wormhole_in,missing_wormhole_in)=match &self.wormhole_in_counts{
|
||||||
|
SetDifferenceCheck(Ok(()))=>(CheckSummary::passed("ExtraWormholeIn"),CheckSummary::passed("MissingWormholeIn")),
|
||||||
|
SetDifferenceCheck(Err(context))=>(
|
||||||
|
if context.extra.is_empty(){
|
||||||
|
CheckSummary::passed("ExtraWormholeIn")
|
||||||
|
}else{
|
||||||
|
let context=Separated::new(", ",||context.extra.iter().map(|(&wormhole_id,_names)|
|
||||||
|
WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id}
|
||||||
|
));
|
||||||
|
summary_format!("ExtraWormholeIn","WormholeIn with no matching WormholeOut: {context}",())
|
||||||
|
},
|
||||||
|
if context.missing.is_empty(){
|
||||||
|
CheckSummary::passed("MissingWormholeIn")
|
||||||
|
}else{
|
||||||
|
// This counts WormholeIn objects, but
|
||||||
|
// flipped logic is easier to understand
|
||||||
|
let context=Separated::new(", ",||context.missing.iter().map(|&wormhole_id|
|
||||||
|
WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id}
|
||||||
|
));
|
||||||
|
summary_format!("MissingWormholeIn","WormholeOut with no matching WormholeIn: {context}",())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let duplicate_wormhole_out=match &self.wormhole_out_counts{
|
||||||
|
DuplicateCheck(Ok(()))=>CheckSummary::passed("DuplicateWormholeOut"),
|
||||||
|
DuplicateCheck(Err(DuplicateCheckContext(context)))=>{
|
||||||
|
let context=Separated::new(", ",||context.iter().map(|(&wormhole_id,&names)|
|
||||||
|
Duplicates::new(WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id},names as usize)
|
||||||
|
));
|
||||||
|
summary_format!("DuplicateWormholeOut","Duplicate WormholeOut: {context}",())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(MapCheckList{checks:Box::new([
|
||||||
|
model_class,
|
||||||
|
model_name,
|
||||||
|
display_name,
|
||||||
|
creator,
|
||||||
|
game_id,
|
||||||
|
mapstart,
|
||||||
|
duplicate_start,
|
||||||
|
extra_finish,
|
||||||
|
missing_finish,
|
||||||
|
dangling_anticheat,
|
||||||
|
spawn1,
|
||||||
|
dangling_teleport,
|
||||||
|
duplicate_spawns,
|
||||||
|
extra_wormhole_in,
|
||||||
|
missing_wormhole_in,
|
||||||
|
duplicate_wormhole_out,
|
||||||
|
])})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
pub struct MapCheckList{
|
||||||
|
pub checks:Box<[CheckSummary;16]>,
|
||||||
|
}
|
||||||
|
impl MapCheckList{
|
||||||
|
pub fn summary(&self)->String{
|
||||||
|
Separated::new("; ",||self.checks.iter().filter_map(|check|
|
||||||
|
(!check.passed).then_some(check.summary.as_str())
|
||||||
|
)).to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Summary{
|
||||||
|
pub summary:String,
|
||||||
|
pub json:serde_json::Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CheckReportAndVersion{
|
||||||
|
pub status:Result<MapInfoOwned,Summary>,
|
||||||
|
pub version:u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::message_handler::MessageHandler{
|
||||||
|
pub async fn check_inner(&self,check_info:CheckRequest)->Result<CheckReportAndVersion,Error>{
|
||||||
|
// discover asset creator and latest version
|
||||||
|
let info=self.cloud_context.get_asset_info(
|
||||||
|
rbx_asset::cloud::GetAssetLatestRequest{asset_id:check_info.ModelID}
|
||||||
|
).await.map_err(Error::ModelInfoDownload)?;
|
||||||
|
|
||||||
|
// reject models created by a group
|
||||||
|
let rbx_asset::cloud::Creator::userId(_user_id)=info.creationContext.creator else{
|
||||||
|
return Err(Error::CreatorTypeMustBeUser);
|
||||||
|
};
|
||||||
|
|
||||||
|
// parse model version string
|
||||||
|
let version=info.revisionId;
|
||||||
|
|
||||||
|
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
|
||||||
|
asset_id:check_info.ModelID,
|
||||||
|
version,
|
||||||
|
}).await.map_err(Error::Download)?;
|
||||||
|
|
||||||
|
// decode dom (slow!)
|
||||||
|
let dom=maybe_gzip.read_with(read_dom,read_dom).map_err(Error::ModelFileDecode)?;
|
||||||
|
|
||||||
|
let map_check=Self::check_dom(&dom).map_err(Error::GetRootInstance)?;
|
||||||
|
|
||||||
|
// check the report, generate an error message if it fails the check
|
||||||
|
let status=match map_check.result(){
|
||||||
|
Ok(map_info)=>Ok(map_info),
|
||||||
|
Err(Ok(summary))=>Err(Summary{
|
||||||
|
summary:summary.summary(),
|
||||||
|
json:serde_json::to_value(&summary).map_err(Error::ToJsonValue)?,
|
||||||
|
}),
|
||||||
|
Err(Err(e))=>return Err(Error::ToJsonValue(e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(CheckReportAndVersion{status,version})
|
||||||
|
}
|
||||||
|
pub fn check_dom(dom:&rbx_dom_weak::WeakDom)->Result<MapCheck,GetRootInstanceError>{
|
||||||
|
// extract the root instance
|
||||||
|
let model_instance=get_root_instance(&dom)?;
|
||||||
|
|
||||||
|
// extract information from the model
|
||||||
|
let model_info=get_model_info(&dom,model_instance);
|
||||||
|
|
||||||
|
// convert the model information into a structured report
|
||||||
|
let map_check=model_info.check();
|
||||||
|
|
||||||
|
Ok(map_check)
|
||||||
|
}
|
||||||
|
}
|
||||||
56
validation/src/check_mapfix.rs
Normal file
56
validation/src/check_mapfix.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
use crate::check::CheckReportAndVersion;
|
||||||
|
use crate::nats_types::CheckMapfixRequest;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error{
|
||||||
|
Check(crate::check::Error),
|
||||||
|
ApiActionMapfixCheck(submissions_api::Error),
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for Error{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for Error{}
|
||||||
|
|
||||||
|
impl crate::message_handler::MessageHandler{
|
||||||
|
pub async fn check_mapfix(&self,check_info:CheckMapfixRequest)->Result<(),Error>{
|
||||||
|
let mapfix_id=check_info.MapfixID;
|
||||||
|
let check_result=self.check_inner(check_info.into()).await;
|
||||||
|
|
||||||
|
// update the mapfix depending on the result
|
||||||
|
match check_result{
|
||||||
|
Ok(CheckReportAndVersion{status:Ok(map_info),version})=>self.api.action_mapfix_submitted(
|
||||||
|
submissions_api::types::ActionMapfixSubmittedRequest{
|
||||||
|
MapfixID:mapfix_id,
|
||||||
|
ModelVersion:version,
|
||||||
|
DisplayName:map_info.display_name,
|
||||||
|
Creator:map_info.creator,
|
||||||
|
GameID:map_info.game_id as u32,
|
||||||
|
}
|
||||||
|
).await.map_err(Error::ApiActionMapfixCheck)?,
|
||||||
|
// update the mapfix model status to request changes
|
||||||
|
Ok(CheckReportAndVersion{status:Err(report),..})=>self.api.action_mapfix_request_changes(
|
||||||
|
submissions_api::types::ActionMapfixRequestChangesRequest{
|
||||||
|
MapfixID:mapfix_id,
|
||||||
|
ErrorMessage:report.summary,
|
||||||
|
}
|
||||||
|
).await.map_err(Error::ApiActionMapfixCheck)?,
|
||||||
|
// update the mapfix model status to request changes
|
||||||
|
Err(e)=>{
|
||||||
|
// log error
|
||||||
|
println!("[check_mapfix] Error: {e}");
|
||||||
|
|
||||||
|
self.api.action_mapfix_request_changes(
|
||||||
|
submissions_api::types::ActionMapfixRequestChangesRequest{
|
||||||
|
MapfixID:mapfix_id,
|
||||||
|
ErrorMessage:e.to_string(),
|
||||||
|
}
|
||||||
|
).await.map_err(Error::ApiActionMapfixCheck)?;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
57
validation/src/check_submission.rs
Normal file
57
validation/src/check_submission.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use crate::check::CheckReportAndVersion;
|
||||||
|
use crate::nats_types::CheckSubmissionRequest;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error{
|
||||||
|
Check(crate::check::Error),
|
||||||
|
ApiActionSubmissionCheck(submissions_api::Error),
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for Error{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for Error{}
|
||||||
|
|
||||||
|
impl crate::message_handler::MessageHandler{
|
||||||
|
pub async fn check_submission(&self,check_info:CheckSubmissionRequest)->Result<(),Error>{
|
||||||
|
let submission_id=check_info.SubmissionID;
|
||||||
|
let check_result=self.check_inner(check_info.into()).await;
|
||||||
|
|
||||||
|
// update the submission depending on the result
|
||||||
|
match check_result{
|
||||||
|
// update the submission model status to submitted
|
||||||
|
Ok(CheckReportAndVersion{status:Ok(map_info),version})=>self.api.action_submission_submitted(
|
||||||
|
submissions_api::types::ActionSubmissionSubmittedRequest{
|
||||||
|
SubmissionID:submission_id,
|
||||||
|
ModelVersion:version,
|
||||||
|
DisplayName:map_info.display_name,
|
||||||
|
Creator:map_info.creator,
|
||||||
|
GameID:map_info.game_id as u32,
|
||||||
|
}
|
||||||
|
).await.map_err(Error::ApiActionSubmissionCheck)?,
|
||||||
|
// update the submission model status to request changes
|
||||||
|
Ok(CheckReportAndVersion{status:Err(report),..})=>self.api.action_submission_request_changes(
|
||||||
|
submissions_api::types::ActionSubmissionRequestChangesRequest{
|
||||||
|
SubmissionID:submission_id,
|
||||||
|
ErrorMessage:report.summary,
|
||||||
|
}
|
||||||
|
).await.map_err(Error::ApiActionSubmissionCheck)?,
|
||||||
|
// update the submission model status to request changes
|
||||||
|
Err(e)=>{
|
||||||
|
// log error
|
||||||
|
println!("[check_submission] Error: {e}");
|
||||||
|
|
||||||
|
self.api.action_submission_request_changes(
|
||||||
|
submissions_api::types::ActionSubmissionRequestChangesRequest{
|
||||||
|
SubmissionID:submission_id,
|
||||||
|
ErrorMessage:e.to_string(),
|
||||||
|
}
|
||||||
|
).await.map_err(Error::ApiActionSubmissionCheck)?;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,14 @@
|
|||||||
use crate::rbx_util::{get_mapinfo,read_dom,MapInfo,ReadDomError,GetMapInfoError,ParseGameIDError};
|
use crate::download::download_asset_version;
|
||||||
|
use crate::rbx_util::{get_root_instance,get_mapinfo,read_dom,MapInfo,ReadDomError,GetRootInstanceError,GameID};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error{
|
pub enum Error{
|
||||||
CreatorTypeMustBeUser,
|
CreatorTypeMustBeUser,
|
||||||
ModelInfoDownload(rbx_asset::cloud::GetError),
|
ModelInfoDownload(rbx_asset::cloud::GetError),
|
||||||
ModelLocationDownload(rbx_asset::cloud::GetError),
|
Download(crate::download::Error),
|
||||||
NonFreeModel,
|
|
||||||
ModelFileDownload(rbx_asset::cloud::GetError),
|
|
||||||
ParseUserID(core::num::ParseIntError),
|
|
||||||
ParseModelVersion(core::num::ParseIntError),
|
|
||||||
ModelFileDecode(ReadDomError),
|
ModelFileDecode(ReadDomError),
|
||||||
GetMapInfo(GetMapInfoError),
|
GetRootInstance(GetRootInstanceError),
|
||||||
ParseGameID(ParseGameIDError),
|
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for Error{
|
impl std::fmt::Display for Error{
|
||||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
@@ -27,10 +23,10 @@ pub struct CreateRequest{
|
|||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
pub struct CreateResult{
|
pub struct CreateResult{
|
||||||
pub AssetOwner:i64,
|
pub AssetOwner:u64,
|
||||||
pub DisplayName:String,
|
pub DisplayName:Option<String>,
|
||||||
pub Creator:String,
|
pub Creator:Option<String>,
|
||||||
pub GameID:i32,
|
pub GameID:Option<GameID>,
|
||||||
pub AssetVersion:u64,
|
pub AssetVersion:u64,
|
||||||
}
|
}
|
||||||
impl crate::message_handler::MessageHandler{
|
impl crate::message_handler::MessageHandler{
|
||||||
@@ -41,45 +37,36 @@ impl crate::message_handler::MessageHandler{
|
|||||||
).await.map_err(Error::ModelInfoDownload)?;
|
).await.map_err(Error::ModelInfoDownload)?;
|
||||||
|
|
||||||
// reject models created by a group
|
// reject models created by a group
|
||||||
let rbx_asset::cloud::Creator::userId(user_id_string)=info.creationContext.creator else{
|
let rbx_asset::cloud::Creator::userId(user_id)=info.creationContext.creator else{
|
||||||
return Err(Error::CreatorTypeMustBeUser);
|
return Err(Error::CreatorTypeMustBeUser);
|
||||||
};
|
};
|
||||||
|
|
||||||
// parse user string and model version string
|
let asset_version=info.revisionId;
|
||||||
let user_id:u64=user_id_string.parse().map_err(Error::ParseUserID)?;
|
|
||||||
let asset_version=info.revisionId.parse().map_err(Error::ParseModelVersion)?;
|
|
||||||
|
|
||||||
// download the location of the map model
|
|
||||||
let location=self.cloud_context.get_asset_version_location(rbx_asset::cloud::GetAssetVersionRequest{
|
|
||||||
asset_id:create_info.ModelID,
|
|
||||||
version:asset_version,
|
|
||||||
}).await.map_err(Error::ModelLocationDownload)?;
|
|
||||||
|
|
||||||
// if the location does not exist, you are not allowed to donwload it
|
|
||||||
let Some(location)=location.location else{
|
|
||||||
return Err(Error::NonFreeModel);
|
|
||||||
};
|
|
||||||
|
|
||||||
// download the map model
|
// download the map model
|
||||||
let model_data=self.cloud_context.get_asset(&location).await.map_err(Error::ModelFileDownload)?;
|
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
|
||||||
|
asset_id:create_info.ModelID,
|
||||||
|
version:asset_version,
|
||||||
|
}).await.map_err(Error::Download)?;
|
||||||
|
|
||||||
// decode dom (slow!)
|
// decode dom (slow!)
|
||||||
let dom=read_dom(std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?;
|
let dom=maybe_gzip.read_with(read_dom,read_dom).map_err(Error::ModelFileDecode)?;
|
||||||
|
|
||||||
|
// extract the root instance
|
||||||
|
let model_instance=get_root_instance(&dom).map_err(Error::GetRootInstance)?;
|
||||||
|
|
||||||
// parse create fields out of asset
|
// parse create fields out of asset
|
||||||
let MapInfo{
|
let MapInfo{
|
||||||
display_name,
|
display_name,
|
||||||
creator,
|
creator,
|
||||||
game_id,
|
game_id,
|
||||||
}=get_mapinfo(&dom).map_err(Error::GetMapInfo)?;
|
}=get_mapinfo(&dom,model_instance);
|
||||||
|
|
||||||
let game_id=game_id.map_err(Error::ParseGameID)?;
|
|
||||||
|
|
||||||
Ok(CreateResult{
|
Ok(CreateResult{
|
||||||
AssetOwner:user_id as i64,
|
AssetOwner:user_id,
|
||||||
DisplayName:display_name.unwrap_or_default().to_owned(),
|
DisplayName:display_name.ok().map(ToOwned::to_owned),
|
||||||
Creator:creator.unwrap_or_default().to_owned(),
|
Creator:creator.ok().map(ToOwned::to_owned),
|
||||||
GameID:game_id as i32,
|
GameID:game_id.ok(),
|
||||||
AssetVersion:asset_version,
|
AssetVersion:asset_version,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,15 @@ impl crate::message_handler::MessageHandler{
|
|||||||
// call create on api
|
// call create on api
|
||||||
self.api.create_mapfix(submissions_api::types::CreateMapfixRequest{
|
self.api.create_mapfix(submissions_api::types::CreateMapfixRequest{
|
||||||
OperationID:create_info.OperationID,
|
OperationID:create_info.OperationID,
|
||||||
AssetOwner:create_request.AssetOwner,
|
AssetOwner:create_request.AssetOwner as i64,
|
||||||
DisplayName:create_request.DisplayName.as_str(),
|
DisplayName:create_request.DisplayName.as_deref().unwrap_or_default(),
|
||||||
Creator:create_request.Creator.as_str(),
|
Creator:create_request.Creator.as_deref().unwrap_or_default(),
|
||||||
GameID:create_request.GameID,
|
// not great TODO: make this great
|
||||||
|
GameID:create_request.GameID.unwrap_or(crate::rbx_util::GameID::Bhop) as i32,
|
||||||
AssetID:create_info.ModelID,
|
AssetID:create_info.ModelID,
|
||||||
AssetVersion:create_request.AssetVersion,
|
AssetVersion:create_request.AssetVersion,
|
||||||
TargetAssetID:create_info.TargetAssetID,
|
TargetAssetID:create_info.TargetAssetID,
|
||||||
|
Description:create_info.Description.as_str(),
|
||||||
}).await.map_err(Error::ApiActionMapfixCreate)?;
|
}).await.map_err(Error::ApiActionMapfixCreate)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -41,9 +43,12 @@ impl crate::message_handler::MessageHandler{
|
|||||||
let create_result=self.create_mapfix_inner(create_info).await;
|
let create_result=self.create_mapfix_inner(create_info).await;
|
||||||
|
|
||||||
if let Err(e)=create_result{
|
if let Err(e)=create_result{
|
||||||
|
// log error
|
||||||
|
println!("[create_mapfix] Error: {e}");
|
||||||
|
|
||||||
self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{
|
self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{
|
||||||
OperationID:operation_id,
|
OperationID:operation_id,
|
||||||
StatusMessage:format!("{e}"),
|
StatusMessage:e.to_string(),
|
||||||
}).await?;
|
}).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::nats_types::CreateSubmissionRequest;
|
use crate::nats_types::CreateSubmissionRequest;
|
||||||
use crate::create::CreateRequest;
|
use crate::create::CreateRequest;
|
||||||
|
use crate::rbx_util::GameID;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -19,15 +20,33 @@ impl crate::message_handler::MessageHandler{
|
|||||||
let create_request=self.create_inner(CreateRequest{
|
let create_request=self.create_inner(CreateRequest{
|
||||||
ModelID:create_info.ModelID,
|
ModelID:create_info.ModelID,
|
||||||
}).await.map_err(Error::Create)?;
|
}).await.map_err(Error::Create)?;
|
||||||
|
|
||||||
|
// grab values from submission form, otherwise try to fill blanks from map data
|
||||||
|
let display_name=if create_info.DisplayName.is_empty(){
|
||||||
|
create_request.DisplayName.as_deref().unwrap_or_default()
|
||||||
|
}else{
|
||||||
|
create_info.DisplayName.as_str()
|
||||||
|
};
|
||||||
|
|
||||||
|
let creator=if create_info.Creator.is_empty(){
|
||||||
|
create_request.Creator.as_deref().unwrap_or_default()
|
||||||
|
}else{
|
||||||
|
create_info.Creator.as_str()
|
||||||
|
};
|
||||||
|
|
||||||
|
let game_id=create_info.GameID.try_into().ok().or(create_request.GameID).unwrap_or(GameID::Bhop);
|
||||||
|
|
||||||
// call create on api
|
// call create on api
|
||||||
self.api.create_submission(submissions_api::types::CreateSubmissionRequest{
|
self.api.create_submission(submissions_api::types::CreateSubmissionRequest{
|
||||||
OperationID:create_info.OperationID,
|
OperationID:create_info.OperationID,
|
||||||
AssetOwner:create_request.AssetOwner,
|
AssetOwner:create_request.AssetOwner as i64,
|
||||||
DisplayName:create_request.DisplayName.as_str(),
|
DisplayName:display_name,
|
||||||
Creator:create_request.Creator.as_str(),
|
Creator:creator,
|
||||||
GameID:create_request.GameID,
|
GameID:game_id as i32,
|
||||||
AssetID:create_info.ModelID,
|
AssetID:create_info.ModelID,
|
||||||
AssetVersion:create_request.AssetVersion,
|
AssetVersion:create_request.AssetVersion,
|
||||||
|
Status:create_info.Status,
|
||||||
|
Roles:create_info.Roles,
|
||||||
}).await.map_err(Error::ApiActionSubmissionCreate)?;
|
}).await.map_err(Error::ApiActionSubmissionCreate)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -38,9 +57,12 @@ impl crate::message_handler::MessageHandler{
|
|||||||
let create_result=self.create_submission_inner(create_info).await;
|
let create_result=self.create_submission_inner(create_info).await;
|
||||||
|
|
||||||
if let Err(e)=create_result{
|
if let Err(e)=create_result{
|
||||||
|
// log error
|
||||||
|
println!("[create_submission] Error: {e}");
|
||||||
|
|
||||||
self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{
|
self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{
|
||||||
OperationID:operation_id,
|
OperationID:operation_id,
|
||||||
StatusMessage:format!("{e}"),
|
StatusMessage:e.to_string(),
|
||||||
}).await?;
|
}).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
26
validation/src/download.rs
Normal file
26
validation/src/download.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error{
|
||||||
|
ModelLocationDownload(rbx_asset::cloud::GetError),
|
||||||
|
NonFreeModel,
|
||||||
|
ModelFileDownload(rbx_asset::cloud::GetError),
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for Error{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for Error{}
|
||||||
|
|
||||||
|
pub async fn download_asset_version(cloud_context:&rbx_asset::cloud::Context,request:rbx_asset::cloud::GetAssetVersionRequest)->Result<rbx_asset::types::MaybeGzippedBytes,Error>{
|
||||||
|
// download the location of the map model
|
||||||
|
let location=cloud_context.get_asset_version_location(request).await.map_err(Error::ModelLocationDownload)?;
|
||||||
|
|
||||||
|
// if the location does not exist, you are not allowed to download it
|
||||||
|
let location=location.location.ok_or(Error::NonFreeModel)?;
|
||||||
|
|
||||||
|
// download the map model
|
||||||
|
let maybe_gzip=cloud_context.get_asset(&location).await.map_err(Error::ModelFileDownload)?;
|
||||||
|
|
||||||
|
Ok(maybe_gzip)
|
||||||
|
}
|
||||||
16
validation/src/lib.rs
Normal file
16
validation/src/lib.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
pub mod rbx_util;
|
||||||
|
pub mod message_handler;
|
||||||
|
pub mod nats_types;
|
||||||
|
pub mod types;
|
||||||
|
pub mod download;
|
||||||
|
pub mod check;
|
||||||
|
pub mod check_mapfix;
|
||||||
|
pub mod check_submission;
|
||||||
|
pub mod create;
|
||||||
|
pub mod create_mapfix;
|
||||||
|
pub mod create_submission;
|
||||||
|
pub mod upload_mapfix;
|
||||||
|
pub mod upload_submission;
|
||||||
|
pub mod validator;
|
||||||
|
pub mod validate_mapfix;
|
||||||
|
pub mod validate_submission;
|
||||||
@@ -4,6 +4,10 @@ mod rbx_util;
|
|||||||
mod message_handler;
|
mod message_handler;
|
||||||
mod nats_types;
|
mod nats_types;
|
||||||
mod types;
|
mod types;
|
||||||
|
mod download;
|
||||||
|
mod check;
|
||||||
|
mod check_mapfix;
|
||||||
|
mod check_submission;
|
||||||
mod create;
|
mod create;
|
||||||
mod create_mapfix;
|
mod create_mapfix;
|
||||||
mod create_submission;
|
mod create_submission;
|
||||||
@@ -39,7 +43,7 @@ async fn main()->Result<(),StartupError>{
|
|||||||
"None"=>None,
|
"None"=>None,
|
||||||
_=>Some(s.parse().expect("ROBLOX_GROUP_ID int parse")),
|
_=>Some(s.parse().expect("ROBLOX_GROUP_ID int parse")),
|
||||||
},
|
},
|
||||||
Err(e)=>Err(e).expect("ROBLOX_GROUP_ID env required"),
|
Err(e)=>panic!("{e}: ROBLOX_GROUP_ID env required"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// create / upload models through STRAFESNET_CI2 account
|
// create / upload models through STRAFESNET_CI2 account
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ pub enum HandleMessageError{
|
|||||||
UnknownSubject(String),
|
UnknownSubject(String),
|
||||||
CreateMapfix(submissions_api::Error),
|
CreateMapfix(submissions_api::Error),
|
||||||
CreateSubmission(submissions_api::Error),
|
CreateSubmission(submissions_api::Error),
|
||||||
|
CheckMapfix(crate::check_mapfix::Error),
|
||||||
|
CheckSubmission(crate::check_submission::Error),
|
||||||
UploadMapfix(crate::upload_mapfix::Error),
|
UploadMapfix(crate::upload_mapfix::Error),
|
||||||
UploadSubmission(crate::upload_submission::Error),
|
UploadSubmission(crate::upload_submission::Error),
|
||||||
ValidateMapfix(crate::validate_mapfix::Error),
|
ValidateMapfix(crate::validate_mapfix::Error),
|
||||||
@@ -52,6 +54,8 @@ impl MessageHandler{
|
|||||||
match message.subject.as_str(){
|
match message.subject.as_str(){
|
||||||
"maptest.mapfixes.create"=>self.create_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::CreateMapfix),
|
"maptest.mapfixes.create"=>self.create_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::CreateMapfix),
|
||||||
"maptest.submissions.create"=>self.create_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::CreateSubmission),
|
"maptest.submissions.create"=>self.create_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::CreateSubmission),
|
||||||
|
"maptest.mapfixes.check"=>self.check_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::CheckMapfix),
|
||||||
|
"maptest.submissions.check"=>self.check_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::CheckSubmission),
|
||||||
"maptest.mapfixes.upload"=>self.upload_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadMapfix),
|
"maptest.mapfixes.upload"=>self.upload_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadMapfix),
|
||||||
"maptest.submissions.upload"=>self.upload_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadSubmission),
|
"maptest.submissions.upload"=>self.upload_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadSubmission),
|
||||||
"maptest.mapfixes.validate"=>self.validate_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateMapfix),
|
"maptest.mapfixes.validate"=>self.validate_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateMapfix),
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ pub struct CreateSubmissionRequest{
|
|||||||
// operation_id is passed back in the response message
|
// operation_id is passed back in the response message
|
||||||
pub OperationID:i32,
|
pub OperationID:i32,
|
||||||
pub ModelID:u64,
|
pub ModelID:u64,
|
||||||
|
pub DisplayName:String,
|
||||||
|
pub Creator:String,
|
||||||
|
pub GameID:u32,
|
||||||
|
// initial status is passed back on create
|
||||||
|
pub Status:u32,
|
||||||
|
pub Roles:u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
@@ -18,6 +24,21 @@ pub struct CreateMapfixRequest{
|
|||||||
pub OperationID:i32,
|
pub OperationID:i32,
|
||||||
pub ModelID:u64,
|
pub ModelID:u64,
|
||||||
pub TargetAssetID:u64,
|
pub TargetAssetID:u64,
|
||||||
|
pub Description:String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(nonstandard_style)]
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct CheckSubmissionRequest{
|
||||||
|
pub SubmissionID:i64,
|
||||||
|
pub ModelID:u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(nonstandard_style)]
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct CheckMapfixRequest{
|
||||||
|
pub MapfixID:i64,
|
||||||
|
pub ModelID:u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ pub enum ReadDomError{
|
|||||||
Binary(rbx_binary::DecodeError),
|
Binary(rbx_binary::DecodeError),
|
||||||
Xml(rbx_xml::DecodeError),
|
Xml(rbx_xml::DecodeError),
|
||||||
Read(std::io::Error),
|
Read(std::io::Error),
|
||||||
Seek(std::io::Error),
|
UnknownFormat(Vec<u8>),
|
||||||
UnknownFormat([u8;8]),
|
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for ReadDomError{
|
impl std::fmt::Display for ReadDomError{
|
||||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
@@ -15,46 +14,33 @@ impl std::fmt::Display for ReadDomError{
|
|||||||
}
|
}
|
||||||
impl std::error::Error for ReadDomError{}
|
impl std::error::Error for ReadDomError{}
|
||||||
|
|
||||||
pub fn read_dom<R:std::io::Read+std::io::Seek>(mut input:R)->Result<rbx_dom_weak::WeakDom,ReadDomError>{
|
pub fn read_dom<R:std::io::Read>(input:R)->Result<rbx_dom_weak::WeakDom,ReadDomError>{
|
||||||
let mut first_8=[0u8;8];
|
let mut buf=std::io::BufReader::new(input);
|
||||||
std::io::Read::read_exact(&mut input,&mut first_8).map_err(ReadDomError::Read)?;
|
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadDomError::Read)?;
|
||||||
std::io::Seek::rewind(&mut input).map_err(ReadDomError::Seek)?;
|
match peek.get(0..8){
|
||||||
match &first_8[0..4]{
|
Some(b"<roblox!")=>rbx_binary::from_reader(buf).map_err(ReadDomError::Binary),
|
||||||
b"<rob"=>{
|
Some(b"<roblox ")=>rbx_xml::from_reader_default(buf).map_err(ReadDomError::Xml),
|
||||||
match &first_8[4..8]{
|
_=>Err(ReadDomError::UnknownFormat(peek.to_owned())),
|
||||||
b"lox!"=>rbx_binary::from_reader(input).map_err(ReadDomError::Binary),
|
|
||||||
b"lox "=>rbx_xml::from_reader(input,rbx_xml::DecodeOptions::default()).map_err(ReadDomError::Xml),
|
|
||||||
_=>Err(ReadDomError::UnknownFormat(first_8)),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_=>Err(ReadDomError::UnknownFormat(first_8)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
|
||||||
|
rbx_dom_weak::ustr(s)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn class_is_a(class:&str,superclass:&str)->bool{
|
pub fn class_is_a(class:&str,superclass:&str)->bool{
|
||||||
if class==superclass{
|
let db=rbx_reflection_database::get();
|
||||||
return true
|
let (Some(class),Some(superclass))=(db.classes.get(class),db.classes.get(superclass))else{
|
||||||
}
|
return false;
|
||||||
let class_descriptor=rbx_reflection_database::get().classes.get(class);
|
};
|
||||||
if let Some(descriptor)=&class_descriptor{
|
db.has_superclass(class,superclass)
|
||||||
if let Some(class_super)=&descriptor.superclass{
|
|
||||||
return class_is_a(&class_super,superclass)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_first_child_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str,class:&str)->Option<&'a rbx_dom_weak::Instance>{
|
fn find_first_child_name_and_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str,class:&str)->Option<&'a rbx_dom_weak::Instance>{
|
||||||
for &referent in instance.children(){
|
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.name==name&&inst.class==class)
|
||||||
if let Some(c)=dom.get_by_ref(referent){
|
|
||||||
if c.name==name&&class_is_a(c.class.as_str(),class) {
|
|
||||||
return Some(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum GameID{
|
pub enum GameID{
|
||||||
Bhop=1,
|
Bhop=1,
|
||||||
Surf=2,
|
Surf=2,
|
||||||
@@ -74,7 +60,19 @@ impl std::str::FromStr for GameID{
|
|||||||
if s.starts_with("flytrials_"){
|
if s.starts_with("flytrials_"){
|
||||||
return Ok(GameID::FlyTrials);
|
return Ok(GameID::FlyTrials);
|
||||||
}
|
}
|
||||||
return Err(ParseGameIDError);
|
Err(ParseGameIDError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct GameIDError;
|
||||||
|
impl TryFrom<u32> for GameID{
|
||||||
|
type Error=GameIDError;
|
||||||
|
fn try_from(value:u32)->Result<Self,Self::Error>{
|
||||||
|
match value{
|
||||||
|
1=>Ok(GameID::Bhop),
|
||||||
|
2=>Ok(GameID::Surf),
|
||||||
|
5=>Ok(GameID::FlyTrials),
|
||||||
|
_=>Err(GameIDError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,6 +82,7 @@ pub struct MapInfo<'a>{
|
|||||||
pub game_id:Result<GameID,ParseGameIDError>,
|
pub game_id:Result<GameID,ParseGameIDError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum StringValueError{
|
pub enum StringValueError{
|
||||||
ObjectNotFound,
|
ObjectNotFound,
|
||||||
ValueNotSet,
|
ValueNotSet,
|
||||||
@@ -92,7 +91,7 @@ pub enum StringValueError{
|
|||||||
|
|
||||||
fn string_value(instance:Option<&rbx_dom_weak::Instance>)->Result<&str,StringValueError>{
|
fn string_value(instance:Option<&rbx_dom_weak::Instance>)->Result<&str,StringValueError>{
|
||||||
let instance=instance.ok_or(StringValueError::ObjectNotFound)?;
|
let instance=instance.ok_or(StringValueError::ObjectNotFound)?;
|
||||||
let value=instance.properties.get("Value").ok_or(StringValueError::ValueNotSet)?;
|
let value=instance.properties.get(&static_ustr("Value")).ok_or(StringValueError::ValueNotSet)?;
|
||||||
match value{
|
match value{
|
||||||
rbx_dom_weak::types::Variant::String(value)=>Ok(value),
|
rbx_dom_weak::types::Variant::String(value)=>Ok(value),
|
||||||
_=>Err(StringValueError::NonStringValue),
|
_=>Err(StringValueError::NonStringValue),
|
||||||
@@ -100,20 +99,24 @@ fn string_value(instance:Option<&rbx_dom_weak::Instance>)->Result<&str,StringVal
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum GetMapInfoError{
|
pub enum GetRootInstanceError{
|
||||||
ModelFileRootMustHaveOneChild,
|
ModelFileRootMustHaveOneChild,
|
||||||
ModelFileChildRefIsNil,
|
ModelFileChildRefIsNil,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mapinfo(dom:&rbx_dom_weak::WeakDom)->Result<MapInfo,GetMapInfoError>{
|
pub fn get_root_instance(dom:&rbx_dom_weak::WeakDom)->Result<&rbx_dom_weak::Instance,GetRootInstanceError>{
|
||||||
let &[map_ref]=dom.root().children()else{
|
let &[map_ref]=dom.root().children()else{
|
||||||
return Err(GetMapInfoError::ModelFileRootMustHaveOneChild);
|
return Err(GetRootInstanceError::ModelFileRootMustHaveOneChild);
|
||||||
};
|
};
|
||||||
let model_instance=dom.get_by_ref(map_ref).ok_or(GetMapInfoError::ModelFileChildRefIsNil)?;
|
let model_instance=dom.get_by_ref(map_ref).ok_or(GetRootInstanceError::ModelFileChildRefIsNil)?;
|
||||||
|
|
||||||
Ok(MapInfo{
|
Ok(model_instance)
|
||||||
display_name:string_value(find_first_child_class(dom,model_instance,"DisplayName","StringValue")),
|
}
|
||||||
creator:string_value(find_first_child_class(dom,model_instance,"Creator","StringValue")),
|
|
||||||
game_id:model_instance.name.parse(),
|
pub fn get_mapinfo<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&rbx_dom_weak::Instance)->MapInfo<'a>{
|
||||||
})
|
MapInfo{
|
||||||
|
display_name:string_value(find_first_child_name_and_class(dom,model_instance,"DisplayName","StringValue")),
|
||||||
|
creator:string_value(find_first_child_name_and_class(dom,model_instance,"Creator","StringValue")),
|
||||||
|
game_id:model_instance.name.parse(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
use crate::download::download_asset_version;
|
||||||
use crate::nats_types::UploadMapfixRequest;
|
use crate::nats_types::UploadMapfixRequest;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error{
|
pub enum Error{
|
||||||
GetLocation(rbx_asset::cloud::GetError),
|
Download(crate::download::Error),
|
||||||
NonFreeModel,
|
IO(std::io::Error),
|
||||||
Get(rbx_asset::cloud::GetError),
|
|
||||||
Json(serde_json::Error),
|
Json(serde_json::Error),
|
||||||
Upload(rbx_asset::cookie::UploadError),
|
Upload(rbx_asset::cookie::UploadError),
|
||||||
ApiActionMapfixUploaded(submissions_api::Error),
|
ApiActionMapfixUploaded(submissions_api::Error),
|
||||||
@@ -19,19 +19,14 @@ impl std::error::Error for Error{}
|
|||||||
|
|
||||||
impl crate::message_handler::MessageHandler{
|
impl crate::message_handler::MessageHandler{
|
||||||
pub async fn upload_mapfix(&self,upload_info:UploadMapfixRequest)->Result<(),Error>{
|
pub async fn upload_mapfix(&self,upload_info:UploadMapfixRequest)->Result<(),Error>{
|
||||||
// download the location of the map model
|
// download the map model
|
||||||
let location=self.cloud_context.get_asset_version_location(rbx_asset::cloud::GetAssetVersionRequest{
|
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
|
||||||
asset_id:upload_info.ModelID,
|
asset_id:upload_info.ModelID,
|
||||||
version:upload_info.ModelVersion,
|
version:upload_info.ModelVersion,
|
||||||
}).await.map_err(Error::GetLocation)?;
|
}).await.map_err(Error::Download)?;
|
||||||
|
|
||||||
// if the location does not exist, you are not allowed to donwload it
|
// transparently handle gzipped models
|
||||||
let Some(location)=location.location else{
|
let model_data=maybe_gzip.to_vec().map_err(Error::IO)?;
|
||||||
return Err(Error::NonFreeModel);
|
|
||||||
};
|
|
||||||
|
|
||||||
// download the map model
|
|
||||||
let model_data=self.cloud_context.get_asset(&location).await.map_err(Error::Get)?;
|
|
||||||
|
|
||||||
// upload the map to the strafesnet group
|
// upload the map to the strafesnet group
|
||||||
let _upload_response=self.cookie_context.upload(rbx_asset::cookie::UploadRequest{
|
let _upload_response=self.cookie_context.upload(rbx_asset::cookie::UploadRequest{
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
use crate::download::download_asset_version;
|
||||||
use crate::nats_types::UploadSubmissionRequest;
|
use crate::nats_types::UploadSubmissionRequest;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error{
|
pub enum Error{
|
||||||
GetLocation(rbx_asset::cloud::GetError),
|
Download(crate::download::Error),
|
||||||
NonFreeModel,
|
IO(std::io::Error),
|
||||||
Get(rbx_asset::cloud::GetError),
|
|
||||||
Json(serde_json::Error),
|
Json(serde_json::Error),
|
||||||
Create(rbx_asset::cookie::CreateError),
|
Create(rbx_asset::cookie::CreateError),
|
||||||
SystemTime(std::time::SystemTimeError),
|
SystemTime(std::time::SystemTimeError),
|
||||||
@@ -20,19 +20,14 @@ impl std::error::Error for Error{}
|
|||||||
|
|
||||||
impl crate::message_handler::MessageHandler{
|
impl crate::message_handler::MessageHandler{
|
||||||
pub async fn upload_submission(&self,upload_info:UploadSubmissionRequest)->Result<(),Error>{
|
pub async fn upload_submission(&self,upload_info:UploadSubmissionRequest)->Result<(),Error>{
|
||||||
// download the location of the map model
|
// download the map model
|
||||||
let location=self.cloud_context.get_asset_version_location(rbx_asset::cloud::GetAssetVersionRequest{
|
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
|
||||||
asset_id:upload_info.ModelID,
|
asset_id:upload_info.ModelID,
|
||||||
version:upload_info.ModelVersion,
|
version:upload_info.ModelVersion,
|
||||||
}).await.map_err(Error::GetLocation)?;
|
}).await.map_err(Error::Download)?;
|
||||||
|
|
||||||
// if the location does not exist, you are not allowed to donwload it
|
// transparently handle gzipped models
|
||||||
let Some(location)=location.location else{
|
let model_data=maybe_gzip.to_vec().map_err(Error::IO)?;
|
||||||
return Err(Error::NonFreeModel);
|
|
||||||
};
|
|
||||||
|
|
||||||
// download the map model
|
|
||||||
let model_data=self.cloud_context.get_asset(&location).await.map_err(Error::Get)?;
|
|
||||||
|
|
||||||
// upload the map to the strafesnet group
|
// upload the map to the strafesnet group
|
||||||
let upload_response=self.cookie_context.create(rbx_asset::cookie::CreateRequest{
|
let upload_response=self.cookie_context.create(rbx_asset::cookie::CreateRequest{
|
||||||
|
|||||||
@@ -26,10 +26,13 @@ impl crate::message_handler::MessageHandler{
|
|||||||
).await.map_err(Error::ApiActionMapfixValidate)?;
|
).await.map_err(Error::ApiActionMapfixValidate)?;
|
||||||
},
|
},
|
||||||
Err(e)=>{
|
Err(e)=>{
|
||||||
|
// log error
|
||||||
|
println!("[validate_mapfix] Error: {e}");
|
||||||
|
|
||||||
// update the mapfix model status to accepted
|
// update the mapfix model status to accepted
|
||||||
self.api.action_mapfix_accepted(submissions_api::types::ActionMapfixAcceptedRequest{
|
self.api.action_mapfix_accepted(submissions_api::types::ActionMapfixAcceptedRequest{
|
||||||
MapfixID:mapfix_id,
|
MapfixID:mapfix_id,
|
||||||
StatusMessage:format!("{e}"),
|
ErrorMessage:e.to_string(),
|
||||||
}).await.map_err(Error::ApiActionMapfixValidate)?;
|
}).await.map_err(Error::ApiActionMapfixValidate)?;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,10 +26,13 @@ impl crate::message_handler::MessageHandler{
|
|||||||
).await.map_err(Error::ApiActionSubmissionValidate)?;
|
).await.map_err(Error::ApiActionSubmissionValidate)?;
|
||||||
},
|
},
|
||||||
Err(e)=>{
|
Err(e)=>{
|
||||||
|
// log error
|
||||||
|
println!("[validate_submission] Error: {e}");
|
||||||
|
|
||||||
// update the submission model status to accepted
|
// update the submission model status to accepted
|
||||||
self.api.action_submission_accepted(submissions_api::types::ActionSubmissionAcceptedRequest{
|
self.api.action_submission_accepted(submissions_api::types::ActionSubmissionAcceptedRequest{
|
||||||
SubmissionID:submission_id,
|
SubmissionID:submission_id,
|
||||||
StatusMessage:format!("{e}"),
|
ErrorMessage:e.to_string(),
|
||||||
}).await.map_err(Error::ApiActionSubmissionValidate)?;
|
}).await.map_err(Error::ApiActionSubmissionValidate)?;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use submissions_api::types::ResourceType;
|
use submissions_api::types::ResourceType;
|
||||||
|
|
||||||
use crate::rbx_util::{class_is_a,read_dom,ReadDomError};
|
use crate::download::download_asset_version;
|
||||||
|
use crate::rbx_util::{read_dom,static_ustr,ReadDomError};
|
||||||
use crate::types::ResourceID;
|
use crate::types::ResourceID;
|
||||||
|
|
||||||
const SCRIPT_CONCURRENCY:usize=16;
|
const SCRIPT_CONCURRENCY:usize=16;
|
||||||
@@ -20,7 +21,7 @@ struct NamePolicy{
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn source_has_illegal_keywords(source:&str)->bool{
|
fn source_has_illegal_keywords(source:&str)->bool{
|
||||||
source.find("getfenv").is_some()||source.find("require").is_some()
|
source.contains("getfenv")||source.contains("require")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_source(source:&str)->String{
|
fn hash_source(source:&str)->String{
|
||||||
@@ -36,9 +37,7 @@ pub enum Error{
|
|||||||
ScriptFlaggedIllegalKeyword(String),
|
ScriptFlaggedIllegalKeyword(String),
|
||||||
ScriptBlocked(Option<submissions_api::types::ScriptID>),
|
ScriptBlocked(Option<submissions_api::types::ScriptID>),
|
||||||
ScriptNotYetReviewed(Option<submissions_api::types::ScriptID>),
|
ScriptNotYetReviewed(Option<submissions_api::types::ScriptID>),
|
||||||
ModelLocationDownload(rbx_asset::cloud::GetError),
|
Download(crate::download::Error),
|
||||||
NonFreeModel,
|
|
||||||
ModelFileDownload(rbx_asset::cloud::GetError),
|
|
||||||
ModelFileDecode(ReadDomError),
|
ModelFileDecode(ReadDomError),
|
||||||
ApiGetScriptPolicyFromHash(submissions_api::types::SingleItemError),
|
ApiGetScriptPolicyFromHash(submissions_api::types::SingleItemError),
|
||||||
ApiGetScript(submissions_api::Error),
|
ApiGetScript(submissions_api::Error),
|
||||||
@@ -91,31 +90,26 @@ impl From<crate::nats_types::ValidateSubmissionRequest> for ValidateRequest{
|
|||||||
|
|
||||||
impl crate::message_handler::MessageHandler{
|
impl crate::message_handler::MessageHandler{
|
||||||
pub async fn validate_inner(&self,validate_info:ValidateRequest)->Result<(),Error>{
|
pub async fn validate_inner(&self,validate_info:ValidateRequest)->Result<(),Error>{
|
||||||
// download the location of the map model
|
// download the map model
|
||||||
let location=self.cloud_context.get_asset_version_location(rbx_asset::cloud::GetAssetVersionRequest{
|
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
|
||||||
asset_id:validate_info.ModelID,
|
asset_id:validate_info.ModelID,
|
||||||
version:validate_info.ModelVersion,
|
version:validate_info.ModelVersion,
|
||||||
}).await.map_err(Error::ModelLocationDownload)?;
|
}).await.map_err(Error::Download)?;
|
||||||
|
|
||||||
// if the location does not exist, you are not allowed to donwload it
|
|
||||||
let Some(location)=location.location else{
|
|
||||||
return Err(Error::NonFreeModel);
|
|
||||||
};
|
|
||||||
|
|
||||||
// download the map model
|
|
||||||
let model_data=self.cloud_context.get_asset(&location).await.map_err(Error::ModelFileDownload)?;
|
|
||||||
|
|
||||||
// decode dom (slow!)
|
// decode dom (slow!)
|
||||||
let mut dom=read_dom(std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?;
|
let mut dom=maybe_gzip.read_with(read_dom,read_dom).map_err(Error::ModelFileDecode)?;
|
||||||
|
|
||||||
/* VALIDATE MAP */
|
/* VALIDATE MAP */
|
||||||
|
|
||||||
|
// stupid ustr thing
|
||||||
|
let source_property=static_ustr("Source");
|
||||||
|
|
||||||
// collect unique scripts
|
// collect unique scripts
|
||||||
let script_refs=get_script_refs(&dom);
|
let script_refs=get_script_refs(&dom);
|
||||||
let mut script_map=std::collections::HashMap::<String,NamePolicy>::new();
|
let mut script_map=std::collections::HashMap::<String,NamePolicy>::new();
|
||||||
for &script_ref in &script_refs{
|
for &script_ref in &script_refs{
|
||||||
if let Some(script)=dom.get_by_ref(script_ref){
|
if let Some(script)=dom.get_by_ref(script_ref){
|
||||||
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get("Source"){
|
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get(&source_property){
|
||||||
// check the source for illegal keywords
|
// check the source for illegal keywords
|
||||||
if source_has_illegal_keywords(source){
|
if source_has_illegal_keywords(source){
|
||||||
// immediately abort
|
// immediately abort
|
||||||
@@ -188,7 +182,7 @@ impl crate::message_handler::MessageHandler{
|
|||||||
let mut modified=false;
|
let mut modified=false;
|
||||||
for &script_ref in &script_refs{
|
for &script_ref in &script_refs{
|
||||||
if let Some(script)=dom.get_by_ref_mut(script_ref){
|
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"){
|
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get_mut(&source_property){
|
||||||
match script_map.get(source.as_str()).map(|p|&p.policy){
|
match script_map.get(source.as_str()).map(|p|&p.policy){
|
||||||
Some(Policy::Blocked)=>{
|
Some(Policy::Blocked)=>{
|
||||||
let hash=hash_source(source.as_str());
|
let hash=hash_source(source.as_str());
|
||||||
@@ -286,17 +280,6 @@ impl crate::message_handler::MessageHandler{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recursive_collect_superclass(objects:&mut std::vec::Vec<rbx_dom_weak::types::Ref>,dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,superclass:&str){
|
|
||||||
for &referent in instance.children(){
|
|
||||||
if let Some(c)=dom.get_by_ref(referent){
|
|
||||||
if class_is_a(c.class.as_str(),superclass){
|
|
||||||
objects.push(c.referent());//copy ref
|
|
||||||
}
|
|
||||||
recursive_collect_superclass(objects,dom,c,superclass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_partial_path(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->String{
|
fn get_partial_path(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->String{
|
||||||
let mut names:Vec<_>=core::iter::successors(
|
let mut names:Vec<_>=core::iter::successors(
|
||||||
Some(instance),
|
Some(instance),
|
||||||
@@ -311,7 +294,11 @@ fn get_partial_path(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_script_refs(dom:&rbx_dom_weak::WeakDom)->Vec<rbx_dom_weak::types::Ref>{
|
fn get_script_refs(dom:&rbx_dom_weak::WeakDom)->Vec<rbx_dom_weak::types::Ref>{
|
||||||
let mut scripts=std::vec::Vec::new();
|
let db=rbx_reflection_database::get();
|
||||||
recursive_collect_superclass(&mut scripts,dom,dom.root(),"LuaSourceContainer");
|
let superclass=&db.classes["LuaSourceContainer"];
|
||||||
scripts
|
dom.descendants().filter_map(|inst|{
|
||||||
|
let class=db.classes.get(inst.class.as_str())?;
|
||||||
|
db.has_superclass(class,superclass)
|
||||||
|
.then_some(inst.referent())
|
||||||
|
}).collect()
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
web/public/errors/404.png
Normal file
BIN
web/public/errors/404.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
web/public/errors/500.png
Normal file
BIN
web/public/errors/500.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
@@ -1,6 +1,11 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
import "./styles/header.scss"
|
import "./styles/header.scss"
|
||||||
|
import { UserInfo } from "@/app/ts/User";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
interface HeaderButton {
|
interface HeaderButton {
|
||||||
name: string,
|
name: string,
|
||||||
@@ -15,6 +20,25 @@ function HeaderButton(header: HeaderButton) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
|
const handleLoginClick = () => {
|
||||||
|
window.location.href = "/auth/oauth2/login?redirect=" + window.location.href;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [valid, setValid] = useState<boolean>(false)
|
||||||
|
const [user, setUser] = useState<UserInfo | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getLoginInfo() {
|
||||||
|
const [validateData, userData] = await Promise.all([
|
||||||
|
fetch("/api/session/validate").then(validateResponse => validateResponse.json()),
|
||||||
|
fetch("/api/session/user").then(userResponse => userResponse.json())
|
||||||
|
]);
|
||||||
|
setValid(validateData)
|
||||||
|
setUser(userData)
|
||||||
|
}
|
||||||
|
getLoginInfo()
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="header-bar">
|
<header className="header-bar">
|
||||||
<nav className="left">
|
<nav className="left">
|
||||||
@@ -24,6 +48,16 @@ export default function Header() {
|
|||||||
</nav>
|
</nav>
|
||||||
<nav className="right">
|
<nav className="right">
|
||||||
<HeaderButton name="Submit" href="/submit"/>
|
<HeaderButton name="Submit" href="/submit"/>
|
||||||
|
{valid && user ? (
|
||||||
|
<div className="author">
|
||||||
|
<Link href="/auth">
|
||||||
|
<Image className="avatar" width={28} height={28} priority={true} src={user.AvatarURL} alt={user.Username}/>
|
||||||
|
<button>{user.Username}</button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button onClick={handleLoginClick}>Login</button>
|
||||||
|
)}
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,25 +1,8 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { redirect } from "next/navigation";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
|
|
||||||
import Header from "./header";
|
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}>) {
|
export default function Webpage({children}: Readonly<{children?: React.ReactNode}>) {
|
||||||
useEffect(() => { login_check() }, [])
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<Header/>
|
<Header/>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
54
web/src/app/admin-submit/(styles)/page.scss
Normal file
54
web/src/app/admin-submit/(styles)/page.scss
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
65
web/src/app/admin-submit/_game.tsx
Normal file
65
web/src/app/admin-submit/_game.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
90
web/src/app/admin-submit/page.tsx
Normal file
90
web/src/app/admin-submit/page.tsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { Button, TextField } from "@mui/material"
|
||||||
|
|
||||||
|
import GameSelection from "./_game";
|
||||||
|
import SendIcon from '@mui/icons-material/Send';
|
||||||
|
import Webpage from "@/app/_components/webpage"
|
||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
import "./(styles)/page.scss"
|
||||||
|
|
||||||
|
interface SubmissionPayload {
|
||||||
|
AssetID: number;
|
||||||
|
DisplayName: string;
|
||||||
|
Creator: string;
|
||||||
|
GameID: number;
|
||||||
|
}
|
||||||
|
interface IdResponse {
|
||||||
|
OperationID: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SubmissionInfoPage() {
|
||||||
|
const [game, setGame] = useState(1);
|
||||||
|
|
||||||
|
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const form = event.currentTarget;
|
||||||
|
const formData = new FormData(form);
|
||||||
|
|
||||||
|
const payload: SubmissionPayload = {
|
||||||
|
DisplayName: (formData.get("display-name") as string) ?? "unknown", // TEMPORARY! TODO: Change
|
||||||
|
Creator: (formData.get("creator") as string) ?? "unknown", // TEMPORARY! TODO: Change
|
||||||
|
GameID: game,
|
||||||
|
AssetID: Number((formData.get("asset-id") as string) ?? "-1"),
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(payload)
|
||||||
|
console.log(JSON.stringify(payload))
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Send the POST request
|
||||||
|
const response = await fetch("/api/submissions-admin", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow any HTTP status
|
||||||
|
const id_response:IdResponse = await response.json();
|
||||||
|
|
||||||
|
// navigate to newly created submission
|
||||||
|
window.location.assign(`/operations/${id_response.OperationID}`)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error submitting data:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Webpage>
|
||||||
|
<main>
|
||||||
|
<header>
|
||||||
|
<h1>Submit New Map on Behalf of Another User (Admin)</h1>
|
||||||
|
<span className="spacer form-spacer"></span>
|
||||||
|
</header>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<TextField className="form-field" id="asset-id" name="asset-id" label="Asset ID (required)" variant="outlined"/>
|
||||||
|
<TextField className="form-field" id="display-name" name="display-name" label="Display Name" variant="outlined"/>
|
||||||
|
<TextField className="form-field" id="creator" name="creator" label="Creator" variant="outlined"/>
|
||||||
|
<GameSelection game={game} setGame={setGame} />
|
||||||
|
<span className="spacer form-spacer"></span>
|
||||||
|
<Button type="submit" variant="contained" startIcon={<SendIcon/>} sx={{
|
||||||
|
width: "400px",
|
||||||
|
height: "50px",
|
||||||
|
marginInline: "auto"
|
||||||
|
}}>Create Submission in ChangesRequested Status (Ready to Force-Submit)</Button>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</Webpage>
|
||||||
|
)
|
||||||
|
}
|
||||||
BIN
web/src/app/favicon.ico
Normal file
BIN
web/src/app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
35
web/src/app/lib/errorImageResponse.ts
Normal file
35
web/src/app/lib/errorImageResponse.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
export async function errorImageResponse(
|
||||||
|
statusCode: number = 500,
|
||||||
|
options?: { message?: string }
|
||||||
|
): Promise<NextResponse> {
|
||||||
|
const file = `${statusCode}.png`;
|
||||||
|
const filePath = path.join(process.cwd(), 'public/errors', file);
|
||||||
|
|
||||||
|
const headers: Record<string, string> = {
|
||||||
|
'Content-Type': 'image/png',
|
||||||
|
};
|
||||||
|
if (options?.message) {
|
||||||
|
headers['X-Error-Message'] = encodeURIComponent(options.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const buffer = await fs.readFile(filePath);
|
||||||
|
headers['Content-Length'] = buffer.length.toString();
|
||||||
|
return new NextResponse(buffer, {
|
||||||
|
status: statusCode,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
const fallback = path.join(process.cwd(), 'public/errors', '500.png');
|
||||||
|
const buffer = await fs.readFile(fallback);
|
||||||
|
headers['Content-Length'] = buffer.length.toString();
|
||||||
|
return new NextResponse(buffer, {
|
||||||
|
status: 500,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,9 +12,9 @@ interface CreatorAndReviewStatus {
|
|||||||
asset_id: MapfixInfo["AssetID"],
|
asset_id: MapfixInfo["AssetID"],
|
||||||
creator: MapfixInfo["DisplayName"],
|
creator: MapfixInfo["DisplayName"],
|
||||||
review: MapfixInfo["StatusID"],
|
review: MapfixInfo["StatusID"],
|
||||||
status_message: MapfixInfo["StatusMessage"],
|
|
||||||
submitter: MapfixInfo["Submitter"],
|
submitter: MapfixInfo["Submitter"],
|
||||||
target_asset_id: MapfixInfo["TargetAssetID"],
|
target_asset_id: MapfixInfo["TargetAssetID"],
|
||||||
|
description: MapfixInfo["Description"],
|
||||||
comments: Comment[],
|
comments: Comment[],
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ function LeaveAComment() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Comments(stats: CommentersProps) {
|
export function Comments(stats: CommentersProps) {
|
||||||
return (<>
|
return (<>
|
||||||
<section className="comments">
|
<section className="comments">
|
||||||
{stats.comments_data.comments.length===0
|
{stats.comments_data.comments.length===0
|
||||||
@@ -66,5 +66,6 @@ export default function Comments(stats: CommentersProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
type CreatorAndReviewStatus
|
type CreatorAndReviewStatus,
|
||||||
|
type Comment,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ interface ReviewAction {
|
|||||||
|
|
||||||
const ReviewActions = {
|
const ReviewActions = {
|
||||||
Submit: {name:"Submit",action:"trigger-submit"} as ReviewAction,
|
Submit: {name:"Submit",action:"trigger-submit"} as ReviewAction,
|
||||||
|
BypassSubmit: {name:"Bypass Submit",action:"bypass-submit"} as ReviewAction,
|
||||||
|
ResetSubmitting: {name:"Reset Submitting (fix softlocked status)",action:"reset-submitting"} as ReviewAction,
|
||||||
Revoke: {name:"Revoke",action:"revoke"} as ReviewAction,
|
Revoke: {name:"Revoke",action:"revoke"} as ReviewAction,
|
||||||
Accept: {name:"Accept",action:"trigger-validate"} as ReviewAction,
|
Accept: {name:"Accept",action:"trigger-validate"} as ReviewAction,
|
||||||
Reject: {name:"Reject",action:"reject"} as ReviewAction,
|
Reject: {name:"Reject",action:"reject"} as ReviewAction,
|
||||||
@@ -112,9 +114,16 @@ export default function ReviewButtons(props: ReviewId) {
|
|||||||
if ([MapfixStatus.Submitted, MapfixStatus.ChangesRequested].includes(mapfixStatus!)) {
|
if ([MapfixStatus.Submitted, MapfixStatus.ChangesRequested].includes(mapfixStatus!)) {
|
||||||
visibleButtons.push({ action: ReviewActions.Revoke, color: "info", mapfixId });
|
visibleButtons.push({ action: ReviewActions.Revoke, color: "info", mapfixId });
|
||||||
}
|
}
|
||||||
|
if (mapfixStatus === MapfixStatus.Submitting) {
|
||||||
|
visibleButtons.push({ action: ReviewActions.ResetSubmitting, color: "error", mapfixId });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (roles&RolesConstants.MapfixReview) {
|
if (roles&RolesConstants.MapfixReview) {
|
||||||
|
// you can force submit a map in ChangesRequested status
|
||||||
|
if (!is_submitter && mapfixStatus === MapfixStatus.ChangesRequested) {
|
||||||
|
visibleButtons.push({ action: ReviewActions.BypassSubmit, color: "error", mapfixId });
|
||||||
|
}
|
||||||
// you can't review your own mapfix!
|
// you can't review your own mapfix!
|
||||||
// note that this means there needs to be more than one person with MapfixReview
|
// note that this means there needs to be more than one person with MapfixReview
|
||||||
if (!is_submitter && mapfixStatus === MapfixStatus.Submitted) {
|
if (!is_submitter && mapfixStatus === MapfixStatus.Submitted) {
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import type { CreatorAndReviewStatus } from "./_comments";
|
|||||||
import { MapImage } from "./_mapImage";
|
import { MapImage } from "./_mapImage";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import ReviewButtons from "./_reviewButtons";
|
import ReviewButtons from "./_reviewButtons";
|
||||||
import Comments from "./_comments";
|
import { Comments, Comment } from "./_comments";
|
||||||
|
import { AuditEvent, decodeAuditEvent as auditEventMessage } from "@/app/ts/AuditEvent";
|
||||||
import Webpage from "@/app/_components/webpage";
|
import Webpage from "@/app/_components/webpage";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
@@ -54,7 +55,7 @@ function TitleAndComments(stats: CreatorAndReviewStatus) {
|
|||||||
<p className="submitter">Submitter {stats.submitter}</p>
|
<p className="submitter">Submitter {stats.submitter}</p>
|
||||||
<p className="asset-id">Model Asset ID {stats.asset_id}</p>
|
<p className="asset-id">Model Asset ID {stats.asset_id}</p>
|
||||||
<p className="target-asset-id">Target Asset ID {stats.target_asset_id}</p>
|
<p className="target-asset-id">Target Asset ID {stats.target_asset_id}</p>
|
||||||
<p className="status-message">Validation Error: {stats.status_message}</p>
|
<p className="description">Description: {stats.description}</p>
|
||||||
<span className="spacer"></span>
|
<span className="spacer"></span>
|
||||||
<Comments comments_data={stats}/>
|
<Comments comments_data={stats}/>
|
||||||
</main>
|
</main>
|
||||||
@@ -62,19 +63,42 @@ function TitleAndComments(stats: CreatorAndReviewStatus) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function MapfixInfoPage() {
|
export default function MapfixInfoPage() {
|
||||||
const dynamicId = useParams<{mapfixId: string}>()
|
const { mapfixId } = useParams < { mapfixId: string } >()
|
||||||
|
|
||||||
const [mapfix, setMapfix] = useState<MapfixInfo | null>(null)
|
const [mapfix, setMapfix] = useState<MapfixInfo | null>(null)
|
||||||
|
const [auditEvents, setAuditEvents] = useState<AuditEvent[]>([])
|
||||||
|
|
||||||
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)
|
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() {
|
async function getMapfix() {
|
||||||
const res = await fetch(`/api/mapfixes/${dynamicId.mapfixId}`)
|
const res = await fetch(`/api/mapfixes/${mapfixId}`)
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
setMapfix(await res.json())
|
setMapfix(await res.json())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getMapfix()
|
async function getAuditEvents() {
|
||||||
}, [dynamicId.mapfixId])
|
const res = await fetch(`/api/mapfixes/${mapfixId}/audit-events?Page=1&Limit=100`)
|
||||||
|
if (res.ok) {
|
||||||
|
setAuditEvents(await res.json())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getMapfix()
|
||||||
|
getAuditEvents()
|
||||||
|
}, [mapfixId])
|
||||||
|
|
||||||
|
const comments:Comment[] = auditEvents.map((auditEvent) => {
|
||||||
|
let username = auditEvent.Username;
|
||||||
|
if (auditEvent.User == 9223372036854776000) {
|
||||||
|
username = "[Validator]";
|
||||||
|
}
|
||||||
|
if (username === "" && mapfix && auditEvent.User == mapfix.Submitter) {
|
||||||
|
username = "[Submitter]";
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
date: auditEvent.CreatedAt,
|
||||||
|
name: username,
|
||||||
|
comment: auditEventMessage(auditEvent),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (!mapfix) {
|
if (!mapfix) {
|
||||||
return <Webpage>
|
return <Webpage>
|
||||||
@@ -85,8 +109,17 @@ export default function MapfixInfoPage() {
|
|||||||
<Webpage>
|
<Webpage>
|
||||||
<main className="map-page-main">
|
<main className="map-page-main">
|
||||||
<section className="review-section">
|
<section className="review-section">
|
||||||
<RatingArea mapfixId={dynamicId.mapfixId} mapfixStatus={mapfix.StatusID} mapfixSubmitter={mapfix.Submitter} mapfixAssetId={mapfix.AssetID} mapfixTargetAssetId={mapfix.TargetAssetID} />
|
<RatingArea mapfixId={mapfixId} mapfixStatus={mapfix.StatusID} mapfixSubmitter={mapfix.Submitter} mapfixAssetId={mapfix.AssetID} mapfixTargetAssetId={mapfix.TargetAssetID} />
|
||||||
<TitleAndComments name={mapfix.DisplayName} creator={mapfix.Creator} review={mapfix.StatusID} status_message={mapfix.StatusMessage} asset_id={mapfix.AssetID} submitter={mapfix.Submitter} target_asset_id={mapfix.TargetAssetID} comments={[]}/>
|
<TitleAndComments
|
||||||
|
name={mapfix.DisplayName}
|
||||||
|
creator={mapfix.Creator}
|
||||||
|
review={mapfix.StatusID}
|
||||||
|
asset_id={mapfix.AssetID}
|
||||||
|
submitter={mapfix.Submitter}
|
||||||
|
target_asset_id={mapfix.TargetAssetID}
|
||||||
|
description={mapfix.Description}
|
||||||
|
comments={comments}
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</Webpage>
|
</Webpage>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React, { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { MapfixList } from "../ts/Mapfix";
|
import { MapfixList } from "../ts/Mapfix";
|
||||||
import { MapfixCard } from "../_components/mapCard";
|
import { MapfixCard } from "../_components/mapCard";
|
||||||
import Webpage from "@/app/_components/webpage";
|
import Webpage from "@/app/_components/webpage";
|
||||||
@@ -11,29 +11,10 @@ import "./(styles)/page.scss";
|
|||||||
import { ListSortConstants } from "../ts/Sort";
|
import { ListSortConstants } from "../ts/Sort";
|
||||||
|
|
||||||
export default function MapfixInfoPage() {
|
export default function MapfixInfoPage() {
|
||||||
const [mapfixes, setMapfixes] = useState<MapfixList>({Total:0,Mapfixes:[]})
|
const [mapfixes, setMapfixes] = useState<MapfixList|null>(null)
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const cardsPerPage = 24; // built to fit on a 1920x1080 monitor
|
const cardsPerPage = 24; // built to fit on a 1920x1080 monitor
|
||||||
|
|
||||||
const totalPages = Math.ceil(mapfixes.Total / cardsPerPage);
|
|
||||||
|
|
||||||
const currentCards = mapfixes.Mapfixes.slice(
|
|
||||||
(currentPage - 1) * cardsPerPage,
|
|
||||||
currentPage * cardsPerPage
|
|
||||||
);
|
|
||||||
|
|
||||||
const nextPage = () => {
|
|
||||||
if (currentPage < totalPages) {
|
|
||||||
setCurrentPage(currentPage + 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const prevPage = () => {
|
|
||||||
if (currentPage > 1) {
|
|
||||||
setCurrentPage(currentPage - 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchMapfixes() {
|
async function fetchMapfixes() {
|
||||||
const res = await fetch(`/api/mapfixes?Page=${currentPage}&Limit=${cardsPerPage}&Sort=${ListSortConstants.ListSortDateDescending}`)
|
const res = await fetch(`/api/mapfixes?Page=${currentPage}&Limit=${cardsPerPage}&Sort=${ListSortConstants.ListSortDateDescending}`)
|
||||||
@@ -55,7 +36,26 @@ export default function MapfixInfoPage() {
|
|||||||
</Webpage>
|
</Webpage>
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mapfixes && mapfixes.Total == 0) {
|
const totalPages = Math.ceil(mapfixes.Total / cardsPerPage);
|
||||||
|
|
||||||
|
const currentCards = mapfixes.Mapfixes.slice(
|
||||||
|
(currentPage - 1) * cardsPerPage,
|
||||||
|
currentPage * cardsPerPage
|
||||||
|
);
|
||||||
|
|
||||||
|
const nextPage = () => {
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
setCurrentPage(currentPage + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const prevPage = () => {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
setCurrentPage(currentPage - 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (mapfixes.Total == 0) {
|
||||||
return <Webpage>
|
return <Webpage>
|
||||||
<main>
|
<main>
|
||||||
Mapfixes list is empty.
|
Mapfixes list is empty.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import "./(styles)/page.scss"
|
|||||||
interface MapfixPayload {
|
interface MapfixPayload {
|
||||||
AssetID: number;
|
AssetID: number;
|
||||||
TargetAssetID: number;
|
TargetAssetID: number;
|
||||||
|
Description: string;
|
||||||
}
|
}
|
||||||
interface IdResponse {
|
interface IdResponse {
|
||||||
OperationID: number;
|
OperationID: number;
|
||||||
@@ -26,8 +27,9 @@ export default function MapfixInfoPage() {
|
|||||||
const formData = new FormData(form);
|
const formData = new FormData(form);
|
||||||
|
|
||||||
const payload: MapfixPayload = {
|
const payload: MapfixPayload = {
|
||||||
AssetID: Number((formData.get("asset-id") as string) ?? "0"),
|
AssetID: Number((formData.get("asset-id") as string) ?? "-1"),
|
||||||
TargetAssetID: Number(dynamicId.mapId),
|
TargetAssetID: Number(dynamicId.mapId),
|
||||||
|
Description: (formData.get("description") as string) ?? "unknown", // TEMPORARY! TODO: Change
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(payload)
|
console.log(payload)
|
||||||
@@ -70,12 +72,13 @@ export default function MapfixInfoPage() {
|
|||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
{/* TODO: Add form data for mapfixes, such as changes they did, and any times that need to be deleted & what styles */}
|
{/* TODO: Add form data for mapfixes, such as changes they did, and any times that need to be deleted & what styles */}
|
||||||
<TextField className="form-field" id="asset-id" name="asset-id" label="Asset ID" variant="outlined"/>
|
<TextField className="form-field" id="asset-id" name="asset-id" label="Asset ID" variant="outlined"/>
|
||||||
|
<TextField className="form-field" id="description" name="description" label="Describe the Mapfix" variant="outlined"/>
|
||||||
<span className="spacer form-spacer"></span>
|
<span className="spacer form-spacer"></span>
|
||||||
<Button type="submit" variant="contained" startIcon={<SendIcon/>} sx={{
|
<Button type="submit" variant="contained" startIcon={<SendIcon/>} sx={{
|
||||||
width: "400px",
|
width: "400px",
|
||||||
height: "50px",
|
height: "50px",
|
||||||
marginInline: "auto"
|
marginInline: "auto"
|
||||||
}}>Submit</Button>
|
}}>Create Mapfix</Button>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
</Webpage>
|
</Webpage>
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ interface CreatorAndReviewStatus {
|
|||||||
asset_id: SubmissionInfo["AssetID"],
|
asset_id: SubmissionInfo["AssetID"],
|
||||||
creator: SubmissionInfo["DisplayName"],
|
creator: SubmissionInfo["DisplayName"],
|
||||||
review: SubmissionInfo["StatusID"],
|
review: SubmissionInfo["StatusID"],
|
||||||
status_message: SubmissionInfo["StatusMessage"],
|
|
||||||
submitter: SubmissionInfo["Submitter"],
|
submitter: SubmissionInfo["Submitter"],
|
||||||
uploaded_asset_id: SubmissionInfo["UploadedAssetID"],
|
uploaded_asset_id: SubmissionInfo["UploadedAssetID"],
|
||||||
comments: Comment[],
|
comments: Comment[],
|
||||||
@@ -52,7 +51,7 @@ function LeaveAComment() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Comments(stats: CommentersProps) {
|
export function Comments(stats: CommentersProps) {
|
||||||
return (<>
|
return (<>
|
||||||
<section className="comments">
|
<section className="comments">
|
||||||
{stats.comments_data.comments.length===0
|
{stats.comments_data.comments.length===0
|
||||||
@@ -66,5 +65,6 @@ export default function Comments(stats: CommentersProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
type CreatorAndReviewStatus
|
type CreatorAndReviewStatus,
|
||||||
|
type Comment,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ interface ReviewAction {
|
|||||||
|
|
||||||
const ReviewActions = {
|
const ReviewActions = {
|
||||||
Submit: {name:"Submit",action:"trigger-submit"} as ReviewAction,
|
Submit: {name:"Submit",action:"trigger-submit"} as ReviewAction,
|
||||||
|
AdminSubmit: {name:"Admin Submit",action:"trigger-submit"} as ReviewAction,
|
||||||
|
BypassSubmit: {name:"Bypass Submit",action:"bypass-submit"} as ReviewAction,
|
||||||
|
ResetSubmitting: {name:"Reset Submitting (fix softlocked status)",action:"reset-submitting"} as ReviewAction,
|
||||||
Revoke: {name:"Revoke",action:"revoke"} as ReviewAction,
|
Revoke: {name:"Revoke",action:"revoke"} as ReviewAction,
|
||||||
Accept: {name:"Accept",action:"trigger-validate"} as ReviewAction,
|
Accept: {name:"Accept",action:"trigger-validate"} as ReviewAction,
|
||||||
Reject: {name:"Reject",action:"reject"} as ReviewAction,
|
Reject: {name:"Reject",action:"reject"} as ReviewAction,
|
||||||
@@ -112,9 +115,17 @@ export default function ReviewButtons(props: ReviewId) {
|
|||||||
if ([SubmissionStatus.Submitted, SubmissionStatus.ChangesRequested].includes(submissionStatus!)) {
|
if ([SubmissionStatus.Submitted, SubmissionStatus.ChangesRequested].includes(submissionStatus!)) {
|
||||||
visibleButtons.push({ action: ReviewActions.Revoke, color: "info", submissionId });
|
visibleButtons.push({ action: ReviewActions.Revoke, color: "info", submissionId });
|
||||||
}
|
}
|
||||||
|
if (submissionStatus === SubmissionStatus.Submitting) {
|
||||||
|
visibleButtons.push({ action: ReviewActions.ResetSubmitting, color: "error", submissionId });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (roles&RolesConstants.SubmissionReview) {
|
if (roles&RolesConstants.SubmissionReview) {
|
||||||
|
// you can force submit a map in ChangesRequested status
|
||||||
|
if (!is_submitter && submissionStatus === SubmissionStatus.ChangesRequested) {
|
||||||
|
visibleButtons.push({ action: ReviewActions.AdminSubmit, color: "error", submissionId });
|
||||||
|
visibleButtons.push({ action: ReviewActions.BypassSubmit, color: "error", submissionId });
|
||||||
|
}
|
||||||
// you can't review your own submission!
|
// you can't review your own submission!
|
||||||
// note that this means there needs to be more than one person with SubmissionReview
|
// note that this means there needs to be more than one person with SubmissionReview
|
||||||
if (!is_submitter && submissionStatus === SubmissionStatus.Submitted) {
|
if (!is_submitter && submissionStatus === SubmissionStatus.Submitted) {
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import type { CreatorAndReviewStatus } from "./_comments";
|
|||||||
import { MapImage } from "./_mapImage";
|
import { MapImage } from "./_mapImage";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import ReviewButtons from "./_reviewButtons";
|
import ReviewButtons from "./_reviewButtons";
|
||||||
import Comments from "./_comments";
|
import { Comments, Comment } from "./_comments";
|
||||||
|
import { AuditEvent, decodeAuditEvent as auditEventMessage } from "@/app/ts/AuditEvent";
|
||||||
import Webpage from "@/app/_components/webpage";
|
import Webpage from "@/app/_components/webpage";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
@@ -46,7 +47,6 @@ function TitleAndComments(stats: CreatorAndReviewStatus) {
|
|||||||
<p className="submitter">Submitter {stats.submitter}</p>
|
<p className="submitter">Submitter {stats.submitter}</p>
|
||||||
<p className="asset-id">Model Asset ID {stats.asset_id}</p>
|
<p className="asset-id">Model Asset ID {stats.asset_id}</p>
|
||||||
<p className="uploaded-asset-id">Uploaded Asset ID {stats.uploaded_asset_id}</p>
|
<p className="uploaded-asset-id">Uploaded Asset ID {stats.uploaded_asset_id}</p>
|
||||||
<p className="status-message">Validation Error: {stats.status_message}</p>
|
|
||||||
<span className="spacer"></span>
|
<span className="spacer"></span>
|
||||||
<Comments comments_data={stats}/>
|
<Comments comments_data={stats}/>
|
||||||
</main>
|
</main>
|
||||||
@@ -54,19 +54,42 @@ function TitleAndComments(stats: CreatorAndReviewStatus) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function SubmissionInfoPage() {
|
export default function SubmissionInfoPage() {
|
||||||
const dynamicId = useParams<{submissionId: string}>()
|
const { submissionId } = useParams < { submissionId: string } >()
|
||||||
|
|
||||||
const [submission, setSubmission] = useState<SubmissionInfo | null>(null)
|
const [submission, setSubmission] = useState<SubmissionInfo | null>(null)
|
||||||
|
const [auditEvents, setAuditEvents] = useState<AuditEvent[]>([])
|
||||||
|
|
||||||
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)
|
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 getSubmission() {
|
async function getSubmission() {
|
||||||
const res = await fetch(`/api/submissions/${dynamicId.submissionId}`)
|
const res = await fetch(`/api/submissions/${submissionId}`)
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
setSubmission(await res.json())
|
setSubmission(await res.json())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getSubmission()
|
async function getAuditEvents() {
|
||||||
}, [dynamicId.submissionId])
|
const res = await fetch(`/api/submissions/${submissionId}/audit-events?Page=1&Limit=100`)
|
||||||
|
if (res.ok) {
|
||||||
|
setAuditEvents(await res.json())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getSubmission()
|
||||||
|
getAuditEvents()
|
||||||
|
}, [submissionId])
|
||||||
|
|
||||||
|
const comments:Comment[] = auditEvents.map((auditEvent) => {
|
||||||
|
let username = auditEvent.Username;
|
||||||
|
if (auditEvent.User == 9223372036854776000) {
|
||||||
|
username = "[Validator]";
|
||||||
|
}
|
||||||
|
if (username === "" && submission && auditEvent.User == submission.Submitter) {
|
||||||
|
username = "[Submitter]";
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
date: auditEvent.CreatedAt,
|
||||||
|
name: username,
|
||||||
|
comment: auditEventMessage(auditEvent),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (!submission) {
|
if (!submission) {
|
||||||
return <Webpage>
|
return <Webpage>
|
||||||
@@ -77,8 +100,8 @@ export default function SubmissionInfoPage() {
|
|||||||
<Webpage>
|
<Webpage>
|
||||||
<main className="map-page-main">
|
<main className="map-page-main">
|
||||||
<section className="review-section">
|
<section className="review-section">
|
||||||
<RatingArea assetId={submission.AssetID} submissionId={dynamicId.submissionId} submissionStatus={submission.StatusID} submissionSubmitter={submission.Submitter}/>
|
<RatingArea assetId={submission.AssetID} submissionId={submissionId} submissionStatus={submission.StatusID} submissionSubmitter={submission.Submitter}/>
|
||||||
<TitleAndComments name={submission.DisplayName} creator={submission.Creator} review={submission.StatusID} status_message={submission.StatusMessage} asset_id={submission.AssetID} submitter={submission.Submitter} uploaded_asset_id={submission.UploadedAssetID} comments={[]}/>
|
<TitleAndComments name={submission.DisplayName} creator={submission.Creator} review={submission.StatusID} asset_id={submission.AssetID} submitter={submission.Submitter} uploaded_asset_id={submission.UploadedAssetID} comments={comments}/>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</Webpage>
|
</Webpage>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React, { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { SubmissionList } from "../ts/Submission";
|
import { SubmissionList } from "../ts/Submission";
|
||||||
import { SubmissionCard } from "../_components/mapCard";
|
import { SubmissionCard } from "../_components/mapCard";
|
||||||
import Webpage from "@/app/_components/webpage";
|
import Webpage from "@/app/_components/webpage";
|
||||||
@@ -9,29 +9,10 @@ import "./(styles)/page.scss";
|
|||||||
import { ListSortConstants } from "../ts/Sort";
|
import { ListSortConstants } from "../ts/Sort";
|
||||||
|
|
||||||
export default function SubmissionInfoPage() {
|
export default function SubmissionInfoPage() {
|
||||||
const [submissions, setSubmissions] = useState<SubmissionList>({Total:0,Submissions:[]})
|
const [submissions, setSubmissions] = useState<SubmissionList|null>(null)
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const cardsPerPage = 24; // built to fit on a 1920x1080 monitor
|
const cardsPerPage = 24; // built to fit on a 1920x1080 monitor
|
||||||
|
|
||||||
const totalPages = Math.ceil(submissions.Total / cardsPerPage);
|
|
||||||
|
|
||||||
const currentCards = submissions.Submissions.slice(
|
|
||||||
(currentPage - 1) * cardsPerPage,
|
|
||||||
currentPage * cardsPerPage
|
|
||||||
);
|
|
||||||
|
|
||||||
const nextPage = () => {
|
|
||||||
if (currentPage < totalPages) {
|
|
||||||
setCurrentPage(currentPage + 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const prevPage = () => {
|
|
||||||
if (currentPage > 1) {
|
|
||||||
setCurrentPage(currentPage - 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchSubmissions() {
|
async function fetchSubmissions() {
|
||||||
const res = await fetch(`/api/submissions?Page=${currentPage}&Limit=${cardsPerPage}&Sort=${ListSortConstants.ListSortDateDescending}`)
|
const res = await fetch(`/api/submissions?Page=${currentPage}&Limit=${cardsPerPage}&Sort=${ListSortConstants.ListSortDateDescending}`)
|
||||||
@@ -53,7 +34,26 @@ export default function SubmissionInfoPage() {
|
|||||||
</Webpage>
|
</Webpage>
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submissions && submissions.Total == 0) {
|
const totalPages = Math.ceil(submissions.Total / cardsPerPage);
|
||||||
|
|
||||||
|
const currentCards = submissions.Submissions.slice(
|
||||||
|
(currentPage - 1) * cardsPerPage,
|
||||||
|
currentPage * cardsPerPage
|
||||||
|
);
|
||||||
|
|
||||||
|
const nextPage = () => {
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
setCurrentPage(currentPage + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const prevPage = () => {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
setCurrentPage(currentPage - 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (submissions.Total == 0) {
|
||||||
return <Webpage>
|
return <Webpage>
|
||||||
<main>
|
<main>
|
||||||
Submissions list is empty.
|
Submissions list is empty.
|
||||||
|
|||||||
65
web/src/app/submit/_game.tsx
Normal file
65
web/src/app/submit/_game.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,19 +2,25 @@
|
|||||||
|
|
||||||
import { Button, TextField } from "@mui/material"
|
import { Button, TextField } from "@mui/material"
|
||||||
|
|
||||||
|
import GameSelection from "./_game";
|
||||||
import SendIcon from '@mui/icons-material/Send';
|
import SendIcon from '@mui/icons-material/Send';
|
||||||
import Webpage from "@/app/_components/webpage"
|
import Webpage from "@/app/_components/webpage"
|
||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import "./(styles)/page.scss"
|
import "./(styles)/page.scss"
|
||||||
|
|
||||||
interface SubmissionPayload {
|
interface SubmissionPayload {
|
||||||
AssetID: number;
|
AssetID: number;
|
||||||
|
DisplayName: string;
|
||||||
|
Creator: string;
|
||||||
|
GameID: number;
|
||||||
}
|
}
|
||||||
interface IdResponse {
|
interface IdResponse {
|
||||||
OperationID: number;
|
OperationID: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SubmissionInfoPage() {
|
export default function SubmissionInfoPage() {
|
||||||
|
const [game, setGame] = useState(1);
|
||||||
|
|
||||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -23,7 +29,10 @@ export default function SubmissionInfoPage() {
|
|||||||
const formData = new FormData(form);
|
const formData = new FormData(form);
|
||||||
|
|
||||||
const payload: SubmissionPayload = {
|
const payload: SubmissionPayload = {
|
||||||
AssetID: Number((formData.get("asset-id") as string) ?? "0"),
|
DisplayName: (formData.get("display-name") as string) ?? "unknown", // TEMPORARY! TODO: Change
|
||||||
|
Creator: (formData.get("creator") as string) ?? "unknown", // TEMPORARY! TODO: Change
|
||||||
|
GameID: game,
|
||||||
|
AssetID: Number((formData.get("asset-id") as string) ?? "-1"),
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(payload)
|
console.log(payload)
|
||||||
@@ -64,13 +73,16 @@ export default function SubmissionInfoPage() {
|
|||||||
<span className="spacer form-spacer"></span>
|
<span className="spacer form-spacer"></span>
|
||||||
</header>
|
</header>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<TextField className="form-field" id="asset-id" name="asset-id" label="Asset ID" variant="outlined"/>
|
<TextField className="form-field" id="asset-id" name="asset-id" label="Asset ID (required)" variant="outlined"/>
|
||||||
|
<TextField className="form-field" id="display-name" name="display-name" label="Display Name" variant="outlined"/>
|
||||||
|
<TextField className="form-field" id="creator" name="creator" label="Creator" variant="outlined"/>
|
||||||
|
<GameSelection game={game} setGame={setGame} />
|
||||||
<span className="spacer form-spacer"></span>
|
<span className="spacer form-spacer"></span>
|
||||||
<Button type="submit" variant="contained" startIcon={<SendIcon/>} sx={{
|
<Button type="submit" variant="contained" startIcon={<SendIcon/>} sx={{
|
||||||
width: "400px",
|
width: "400px",
|
||||||
height: "50px",
|
height: "50px",
|
||||||
marginInline: "auto"
|
marginInline: "auto"
|
||||||
}}>Submit</Button>
|
}}>Create Submission</Button>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
</Webpage>
|
</Webpage>
|
||||||
|
|||||||
@@ -1,4 +1,17 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
import { errorImageResponse } from '@/app/lib/errorImageResponse';
|
||||||
|
|
||||||
|
const cache = new Map<number, { buffer: Buffer; expires: number }>();
|
||||||
|
const CACHE_TTL = 15 * 60 * 1000;
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
const now = Date.now();
|
||||||
|
for (const [key, value] of cache.entries()) {
|
||||||
|
if (value.expires <= now) {
|
||||||
|
cache.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 60 * 5 * 1000);
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: NextRequest,
|
request: NextRequest,
|
||||||
@@ -7,10 +20,9 @@ export async function GET(
|
|||||||
const { assetId } = await context.params;
|
const { assetId } = await context.params;
|
||||||
|
|
||||||
if (!assetId) {
|
if (!assetId) {
|
||||||
return NextResponse.json(
|
return errorImageResponse(400, {
|
||||||
{ error: 'Missing asset ID' },
|
message: "Missing asset ID",
|
||||||
{ status: 400 }
|
})
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let finalAssetId = assetId;
|
let finalAssetId = assetId;
|
||||||
@@ -27,43 +39,57 @@ export async function GET(
|
|||||||
}
|
}
|
||||||
} catch { }
|
} catch { }
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
const cached = cache.get(finalAssetId);
|
||||||
|
|
||||||
|
if (cached && cached.expires > now) {
|
||||||
|
return new NextResponse(cached.buffer, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'image/png',
|
||||||
|
'Content-Length': cached.buffer.length.toString(),
|
||||||
|
'Cache-Control': `public, max-age=${CACHE_TTL / 1000}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://thumbnails.roblox.com/v1/assets?format=png&size=512x512&assetIds=${finalAssetId}`
|
`https://thumbnails.roblox.com/v1/assets?format=png&size=512x512&assetIds=${finalAssetId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to fetch thumbnail JSON');
|
throw new Error(`Failed to fetch thumbnail JSON [${response.status}]`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
const imageUrl = data.data[0]?.imageUrl;
|
const imageUrl = data.data[0]?.imageUrl;
|
||||||
if (!imageUrl) {
|
if (!imageUrl) {
|
||||||
return NextResponse.json(
|
return errorImageResponse(404, {
|
||||||
{ error: 'No image URL found in the response' },
|
message: "No image URL found in the response",
|
||||||
{ status: 404 }
|
})
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageResponse = await fetch(imageUrl);
|
const imageResponse = await fetch(imageUrl);
|
||||||
if (!imageResponse.ok) {
|
if (!imageResponse.ok) {
|
||||||
throw new Error('Failed to fetch the image');
|
throw new Error(`Failed to fetch the image [${imageResponse.status}]`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const arrayBuffer = await imageResponse.arrayBuffer();
|
const arrayBuffer = await imageResponse.arrayBuffer();
|
||||||
const buffer = Buffer.from(arrayBuffer);
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
|
|
||||||
|
cache.set(finalAssetId, { buffer, expires: now + CACHE_TTL });
|
||||||
|
|
||||||
return new NextResponse(buffer, {
|
return new NextResponse(buffer, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'image/png',
|
'Content-Type': 'image/png',
|
||||||
'Content-Length': buffer.length.toString(),
|
'Content-Length': buffer.length.toString(),
|
||||||
|
'Cache-Control': `public, max-age=${CACHE_TTL / 1000}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch {
|
} catch (err) {
|
||||||
return NextResponse.json(
|
return errorImageResponse(500, {
|
||||||
{ error: 'Failed to fetch or process thumbnail' },
|
message: `Failed to fetch or process thumbnail: ${err}`,
|
||||||
{ status: 500 }
|
})
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: NextRequest,
|
request: NextRequest,
|
||||||
@@ -6,8 +6,15 @@ export async function GET(
|
|||||||
): Promise<NextResponse> {
|
): Promise<NextResponse> {
|
||||||
// TODO: implement this, we need a cdn for in-game map thumbnails...
|
// TODO: implement this, we need a cdn for in-game map thumbnails...
|
||||||
|
|
||||||
const { mapId } = await context.params;
|
if (!process.env.API_HOST) {
|
||||||
const baseUrl = request.nextUrl.origin; // Gets the current base URL
|
throw new Error('env variable "API_HOST" is not set')
|
||||||
|
}
|
||||||
|
|
||||||
return NextResponse.redirect(`${baseUrl}/thumbnails/asset/${mapId}`);
|
const { mapId } = await context.params
|
||||||
|
|
||||||
|
const apiHost = process.env.API_HOST.replace(/\/api\/?$/, "")
|
||||||
|
const redirectPath = `/thumbnails/asset/${mapId}`
|
||||||
|
const redirectUrl = `${apiHost}${redirectPath}`
|
||||||
|
|
||||||
|
return NextResponse.redirect(redirectUrl)
|
||||||
}
|
}
|
||||||
94
web/src/app/ts/AuditEvent.ts
Normal file
94
web/src/app/ts/AuditEvent.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import { SubmissionStatusToString } from "./Submission";
|
||||||
|
|
||||||
|
// Shared audit event types
|
||||||
|
export const enum AuditEventType {
|
||||||
|
Action = 0,
|
||||||
|
Comment = 1,
|
||||||
|
ChangeModel = 2,
|
||||||
|
ChangeValidatedModel = 3,
|
||||||
|
ChangeDisplayName = 4,
|
||||||
|
ChangeCreator = 5,
|
||||||
|
Error = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discriminated union types for each event
|
||||||
|
export type AuditEventData =
|
||||||
|
| { EventType: AuditEventType.Action; EventData: AuditEventDataAction }
|
||||||
|
| { EventType: AuditEventType.Comment; EventData: AuditEventDataComment }
|
||||||
|
| { EventType: AuditEventType.ChangeModel; EventData: AuditEventDataChangeModel }
|
||||||
|
| { EventType: AuditEventType.ChangeValidatedModel; EventData: AuditEventDataChangeValidatedModel; }
|
||||||
|
| { EventType: AuditEventType.ChangeDisplayName; EventData: AuditEventDataChangeName; }
|
||||||
|
| { EventType: AuditEventType.ChangeCreator; EventData: AuditEventDataChangeName; }
|
||||||
|
| { EventType: AuditEventType.Error; EventData: AuditEventDataError };
|
||||||
|
|
||||||
|
// Concrete data interfaces
|
||||||
|
export interface AuditEventDataAction {
|
||||||
|
target_status: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuditEventDataComment {
|
||||||
|
comment: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuditEventDataChangeModel {
|
||||||
|
old_model_id: number;
|
||||||
|
old_model_version: number;
|
||||||
|
new_model_id: number;
|
||||||
|
new_model_version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuditEventDataChangeValidatedModel {
|
||||||
|
validated_model_id: number;
|
||||||
|
validated_model_version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuditEventDataChangeName {
|
||||||
|
old_name: string;
|
||||||
|
new_name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuditEventDataError {
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full audit event type (mirroring the Go struct)
|
||||||
|
export interface AuditEvent {
|
||||||
|
Id: number;
|
||||||
|
CreatedAt: string; // ISO string, can convert to Date if needed
|
||||||
|
User: number;
|
||||||
|
Username: string;
|
||||||
|
ResourceType: string; // Assuming this is a string enum or similar
|
||||||
|
ResourceId: number;
|
||||||
|
EventType: AuditEventType;
|
||||||
|
EventData: unknown; // You'll decode this into a specific AuditEventData based on `event_type`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: decode function to parse event_data into strongly-typed structure
|
||||||
|
export function decodeAuditEvent(event: AuditEvent): string {
|
||||||
|
switch (event.EventType) {
|
||||||
|
case AuditEventType.Action:{
|
||||||
|
const data = event.EventData as AuditEventDataAction;
|
||||||
|
return `Changed status to ${SubmissionStatusToString(data.target_status)}`;
|
||||||
|
}case AuditEventType.Comment:{
|
||||||
|
const data = event.EventData as AuditEventDataComment;
|
||||||
|
return data.comment;
|
||||||
|
}case AuditEventType.ChangeModel:{
|
||||||
|
const data = event.EventData as AuditEventDataChangeModel;
|
||||||
|
return `Model changed to asset id = ${data.new_model_id}`;
|
||||||
|
}case AuditEventType.ChangeValidatedModel:{
|
||||||
|
const data = event.EventData as AuditEventDataChangeValidatedModel;
|
||||||
|
return `Model validated as asset id = ${data.validated_model_id}`;
|
||||||
|
}case AuditEventType.ChangeDisplayName:{
|
||||||
|
const data = event.EventData as AuditEventDataChangeName;
|
||||||
|
return `DisplayName changed to ${data.new_name}`;
|
||||||
|
}case AuditEventType.ChangeCreator:{
|
||||||
|
const data = event.EventData as AuditEventDataChangeName;
|
||||||
|
return `Creator changed to ${data.new_name}`;
|
||||||
|
}case AuditEventType.Error:{
|
||||||
|
const data = event.EventData as AuditEventDataError;
|
||||||
|
return `Error: ${data.error}`;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown EventType: ${event.EventType}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,8 +25,8 @@ interface MapfixInfo {
|
|||||||
readonly ValidatedAssetVersion: number,
|
readonly ValidatedAssetVersion: number,
|
||||||
readonly Completed: boolean,
|
readonly Completed: boolean,
|
||||||
readonly TargetAssetID: number,
|
readonly TargetAssetID: number,
|
||||||
readonly StatusID: MapfixStatus
|
readonly StatusID: MapfixStatus,
|
||||||
readonly StatusMessage: string,
|
readonly Description: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MapfixList {
|
interface MapfixList {
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ interface SubmissionInfo {
|
|||||||
readonly ValidatedAssetVersion: number,
|
readonly ValidatedAssetVersion: number,
|
||||||
readonly Completed: boolean,
|
readonly Completed: boolean,
|
||||||
readonly UploadedAssetID: number,
|
readonly UploadedAssetID: number,
|
||||||
readonly StatusID: SubmissionStatus
|
readonly StatusID: SubmissionStatus,
|
||||||
readonly StatusMessage: string,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SubmissionList {
|
interface SubmissionList {
|
||||||
|
|||||||
5
web/src/app/ts/User.ts
Normal file
5
web/src/app/ts/User.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export interface UserInfo {
|
||||||
|
readonly UserID: number,
|
||||||
|
readonly Username: string,
|
||||||
|
readonly AvatarURL: string,
|
||||||
|
}
|
||||||
@@ -1,13 +1,33 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server"
|
import { NextRequest, NextResponse } from "next/server"
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
matcher: ["/api/:path*"],
|
matcher: ["/api/:path*", "/auth/:path*"],
|
||||||
}
|
}
|
||||||
|
|
||||||
export function middleware(request: NextRequest) {
|
export function middleware(request: NextRequest) {
|
||||||
if (!process.env.API_HOST) {
|
const { pathname, search } = request.nextUrl
|
||||||
throw new Error("env variable \"API_HOST\" is not set")
|
|
||||||
|
if (pathname.startsWith("/api")) {
|
||||||
|
if (!process.env.API_HOST) {
|
||||||
|
throw new Error('env variable "API_HOST" is not set')
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseUrl = process.env.API_HOST.replace(/\/$/, "");
|
||||||
|
const path = pathname.replace(/^\/api/, "");
|
||||||
|
const apiUrl = new URL(baseUrl + path + search);
|
||||||
|
|
||||||
|
return NextResponse.rewrite(apiUrl, { request });
|
||||||
|
} else if (pathname.startsWith("/auth")) {
|
||||||
|
if (!process.env.AUTH_HOST) {
|
||||||
|
throw new Error('env variable "AUTH_HOST" is not set')
|
||||||
|
}
|
||||||
|
|
||||||
|
const authHost = process.env.AUTH_HOST.replace(/\/$/, "");
|
||||||
|
const path = pathname.replace(/^\/auth/, "");
|
||||||
|
const redirectUrl = new URL(authHost + path + search);
|
||||||
|
|
||||||
|
return NextResponse.redirect(redirectUrl, 302);
|
||||||
}
|
}
|
||||||
const url = new URL(process.env.API_HOST + request.nextUrl.pathname.replace(/^\/api/, '') + request.nextUrl.search)
|
|
||||||
return NextResponse.rewrite(url, { request })
|
return NextResponse.next()
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user