3 Commits

Author SHA1 Message Date
89b3ba44b5 use staging
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-30 05:08:41 -08:00
706f5bad3f run auth with debug
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-28 22:11:16 -08:00
be75903226 Revert "submissions: refactor auth to only make requests when needed"
This reverts commit 8bf2c92df3.
2024-12-28 22:11:16 -08:00
85 changed files with 4640 additions and 7684 deletions

View File

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

3
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

@@ -7,21 +7,21 @@ tags:
- name: Submissions - name: Submissions
description: Submission operations description: Submission operations
paths: paths:
/submissions/{SubmissionID}/validated-model: /submissions/{SubmissionID}/model:
post: post:
summary: Update validated model summary: Update model following role restrictions
operationId: updateSubmissionValidatedModel operationId: updateSubmissionModel
tags: tags:
- Submissions - Submissions
parameters: parameters:
- $ref: '#/components/parameters/SubmissionID' - $ref: '#/components/parameters/SubmissionID'
- name: ValidatedModelID - name: ModelID
in: query in: query
required: true required: true
schema: schema:
type: integer type: integer
format: int64 format: int64
- name: ValidatedModelVersion - name: VersionID
in: query in: query
required: true required: true
schema: schema:
@@ -61,13 +61,6 @@ paths:
- Submissions - Submissions
parameters: parameters:
- $ref: '#/components/parameters/SubmissionID' - $ref: '#/components/parameters/SubmissionID'
- name: StatusMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses: responses:
"204": "204":
description: Successful response description: Successful response
@@ -85,9 +78,8 @@ paths:
- Submissions - Submissions
parameters: parameters:
- $ref: '#/components/parameters/SubmissionID' - $ref: '#/components/parameters/SubmissionID'
- name: UploadedAssetID - name: TargetAssetID
in: query in: query
required: true
schema: schema:
type: integer type: integer
format: int64 format: int64
@@ -100,6 +92,23 @@ paths:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/releaser-released:
post:
summary: (Internal endpoint) Role Releaser changes status from releasing -> released
operationId: actionSubmissionReleased
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/script-policy: /script-policy:
get: get:
summary: Get list of script policies summary: Get list of script policies
@@ -304,8 +313,7 @@ components:
- Name - Name
- Hash - Hash
- Source - Source
- ResourceType - SubmissionID
- ResourceID
type: object type: object
properties: properties:
ID: ID:
@@ -321,18 +329,14 @@ components:
Source: Source:
type: string type: string
maxLength: 1048576 maxLength: 1048576
ResourceType: SubmissionID:
type: integer
format: int32
ResourceID:
type: integer type: integer
format: int64 format: int64
ScriptCreate: ScriptCreate:
required: required:
- Name - Name
- Source - Source
- ResourceType # - SubmissionID
# - ResourceID
type: object type: object
properties: properties:
Name: Name:
@@ -341,10 +345,7 @@ components:
Source: Source:
type: string type: string
maxLength: 1048576 maxLength: 1048576
ResourceType: SubmissionID:
type: integer
format: int32
ResourceID:
type: integer type: integer
format: int64 format: int64
ScriptPolicy: ScriptPolicy:

View File

@@ -6,8 +6,6 @@ info:
servers: servers:
- url: https://submissions.strafes.net/v1 - url: https://submissions.strafes.net/v1
tags: tags:
- name: Session
description: Session operations
- name: Submissions - name: Submissions
description: Submission operations description: Submission operations
- name: Scripts - name: Scripts
@@ -17,70 +15,12 @@ tags:
security: security:
- cookieAuth: [] - cookieAuth: []
paths: paths:
/session/user:
get:
summary: Get information about the currently logged in user
operationId: sessionUser
tags:
- Session
responses:
"200":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/User"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/session/roles:
get:
summary: Get list of roles for the current session
operationId: sessionRoles
tags:
- Session
responses:
"200":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/Roles"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/session/validate:
get:
summary: Ask if the current session is valid
operationId: sessionValidate
tags:
- Session
responses:
"200":
description: Successful response
content:
application/json:
schema:
type: boolean
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions: /submissions:
get: get:
summary: Get list of submissions summary: Get list of submissions
operationId: listSubmissions operationId: listSubmissions
tags: tags:
- Submissions - Submissions
security: []
parameters: parameters:
- $ref: "#/components/parameters/Page" - $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/Limit"
@@ -99,11 +39,6 @@ paths:
schema: schema:
type: integer type: integer
format: int32 format: int32
- name: Sort
in: query
schema:
type: integer
format: int32
responses: responses:
"200": "200":
description: Successful response description: Successful response
@@ -149,7 +84,6 @@ paths:
operationId: getSubmission operationId: getSubmission
tags: tags:
- Submissions - Submissions
security: []
parameters: parameters:
- $ref: '#/components/parameters/SubmissionID' - $ref: '#/components/parameters/SubmissionID'
responses: responses:
@@ -196,7 +130,7 @@ paths:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/completed: /submissions/{SubmissionID}/completed:
post: post:
summary: Called by maptest when a player completes the map summary: Retrieve map with ID
operationId: setSubmissionCompleted operationId: setSubmissionCompleted
tags: tags:
- Submissions - Submissions
@@ -247,7 +181,7 @@ paths:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/trigger-validate: /submissions/{SubmissionID}/status/trigger-validate:
post: post:
summary: Role Reviewer triggers validation and changes status from Submitted -> Validating summary: Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating
operationId: actionSubmissionTriggerValidate operationId: actionSubmissionTriggerValidate
tags: tags:
- Submissions - Submissions
@@ -262,40 +196,6 @@ paths:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/retry-validate:
post:
summary: Role Reviewer re-runs validation and changes status from Accepted -> Validating
operationId: actionSubmissionRetryValidate
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/reset-validating:
post:
summary: Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted
operationId: actionSubmissionAccepted
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/reject: /submissions/{SubmissionID}/status/reject:
post: post:
summary: Role Reviewer changes status from Submitted -> Rejected summary: Role Reviewer changes status from Submitted -> Rejected
@@ -347,55 +247,12 @@ paths:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/reset-uploading:
post:
summary: Role Admin manually resets uploading softlock and changes status from Uploading -> Validated
operationId: actionSubmissionValidated
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/release-submissions:
post:
summary: Release a set of uploaded maps
operationId: releaseSubmissions
tags:
- Submissions
requestBody:
required: true
content:
application/json:
schema:
type: array
minItems: 1
maxItems: 255
items:
$ref: '#/components/schemas/ReleaseInfo'
responses:
"201":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/script-policy: /script-policy:
get: get:
summary: Get list of script policies summary: Get list of script policies
operationId: listScriptPolicy operationId: listScriptPolicy
tags: tags:
- ScriptPolicy - ScriptPolicy
security: []
parameters: parameters:
- $ref: "#/components/parameters/Page" - $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/Limit"
@@ -460,7 +317,6 @@ paths:
operationId: getScriptPolicy operationId: getScriptPolicy
tags: tags:
- ScriptPolicy - ScriptPolicy
security: []
parameters: parameters:
- $ref: '#/components/parameters/ScriptPolicyID' - $ref: '#/components/parameters/ScriptPolicyID'
responses: responses:
@@ -520,7 +376,6 @@ paths:
operationId: listScripts operationId: listScripts
tags: tags:
- Script - Script
security: []
parameters: parameters:
- $ref: "#/components/parameters/Page" - $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/Limit"
@@ -590,7 +445,6 @@ paths:
operationId: getScript operationId: getScript
tags: tags:
- Scripts - Scripts
security: []
parameters: parameters:
- $ref: '#/components/parameters/ScriptID' - $ref: '#/components/parameters/ScriptID'
responses: responses:
@@ -701,30 +555,6 @@ components:
ID: ID:
type: integer type: integer
format: int64 format: int64
Roles:
required:
- Roles
type: object
properties:
Roles:
type: integer
format: int32
User:
required:
- UserID
- Username
- AvatarURL
type: object
properties:
UserID:
type: integer
format: int64
Username:
type: string
maxLength: 128
AvatarURL:
type: string
maxLength: 256
Submission: Submission:
required: required:
- ID - ID
@@ -737,9 +567,9 @@ components:
- AssetID - AssetID
- AssetVersion - AssetVersion
- Completed - Completed
# - UploadedAssetID - SubmissionType
# - TargetAssetID
- StatusID - StatusID
- StatusMessage
type: object type: object
properties: properties:
ID: ID:
@@ -771,15 +601,15 @@ components:
format: int64 format: int64
Completed: Completed:
type: boolean type: boolean
UploadedAssetID: SubmissionType:
type: integer
format: int32
TargetAssetID:
type: integer type: integer
format: int64 format: int64
StatusID: StatusID:
type: integer type: integer
format: int32 format: int32
StatusMessage:
type: string
maxLength: 256
SubmissionCreate: SubmissionCreate:
required: required:
- DisplayName - DisplayName
@@ -787,6 +617,7 @@ components:
- GameID - GameID
- AssetID - AssetID
- AssetVersion - AssetVersion
# - TargetAssetID
type: object type: object
properties: properties:
DisplayName: DisplayName:
@@ -804,26 +635,16 @@ components:
AssetVersion: AssetVersion:
type: integer type: integer
format: int64 format: int64
ReleaseInfo: TargetAssetID:
required:
- SubmissionID
- Date
type: object
properties:
SubmissionID:
type: integer type: integer
format: int64 format: int64
Date:
type: string
format: date-time
Script: Script:
required: required:
- ID - ID
- Name - Name
- Hash - Hash
- Source - Source
- ResourceType - SubmissionID
- ResourceID
type: object type: object
properties: properties:
ID: ID:
@@ -839,18 +660,14 @@ components:
Source: Source:
type: string type: string
maxLength: 1048576 maxLength: 1048576
ResourceType: SubmissionID:
type: integer
format: int32
ResourceID:
type: integer type: integer
format: int64 format: int64
ScriptCreate: ScriptCreate:
required: required:
- Name - Name
- Source - Source
- ResourceType # - SubmissionID
# - ResourceID
type: object type: object
properties: properties:
Name: Name:
@@ -859,10 +676,7 @@ components:
Source: Source:
type: string type: string
maxLength: 1048576 maxLength: 1048576
ResourceType: SubmissionID:
type: integer
format: int32
ResourceID:
type: integer type: integer
format: int64 format: int64
ScriptUpdate: ScriptUpdate:
@@ -879,10 +693,7 @@ components:
Source: Source:
type: string type: string
maxLength: 1048576 maxLength: 1048576
ResourceType: SubmissionID:
type: integer
format: int32
ResourceID:
type: integer type: integer
format: int64 format: int64
ScriptPolicy: ScriptPolicy:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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"
"github.com/ogen-go/ogen/json"
"github.com/ogen-go/ogen/validate" "github.com/ogen-go/ogen/validate"
) )
@@ -327,215 +326,6 @@ func (s *OptString) UnmarshalJSON(data []byte) error {
return s.Decode(d) return s.Decode(d)
} }
// Encode implements json.Marshaler.
func (s *ReleaseInfo) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *ReleaseInfo) encodeFields(e *jx.Encoder) {
{
e.FieldStart("SubmissionID")
e.Int64(s.SubmissionID)
}
{
e.FieldStart("Date")
json.EncodeDateTime(e, s.Date)
}
}
var jsonFieldsNameOfReleaseInfo = [2]string{
0: "SubmissionID",
1: "Date",
}
// Decode decodes ReleaseInfo from json.
func (s *ReleaseInfo) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode ReleaseInfo to nil")
}
var requiredBitSet [1]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "SubmissionID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.SubmissionID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"SubmissionID\"")
}
case "Date":
requiredBitSet[0] |= 1 << 1
if err := func() error {
v, err := json.DecodeDateTime(d)
s.Date = v
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Date\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode ReleaseInfo")
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00000011,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
//
// If XOR result is not zero, result is not equal to expected, so some fields are missed.
// Bits of fields which would be set are actually bits of missed fields.
missed := bits.OnesCount8(result)
for bitN := 0; bitN < missed; bitN++ {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfReleaseInfo) {
name = jsonFieldsNameOfReleaseInfo[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
failures = append(failures, validate.FieldError{
Name: name,
Error: validate.ErrFieldRequired,
})
// Reset bit.
result &^= 1 << bitIdx
}
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *ReleaseInfo) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *ReleaseInfo) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *Roles) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *Roles) encodeFields(e *jx.Encoder) {
{
e.FieldStart("Roles")
e.Int32(s.Roles)
}
}
var jsonFieldsNameOfRoles = [1]string{
0: "Roles",
}
// Decode decodes Roles from json.
func (s *Roles) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode Roles to nil")
}
var requiredBitSet [1]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "Roles":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int32()
s.Roles = int32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Roles\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode Roles")
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00000001,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
//
// If XOR result is not zero, result is not equal to expected, so some fields are missed.
// Bits of fields which would be set are actually bits of missed fields.
missed := bits.OnesCount8(result)
for bitN := 0; bitN < missed; bitN++ {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfRoles) {
name = jsonFieldsNameOfRoles[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
failures = append(failures, validate.FieldError{
Name: name,
Error: validate.ErrFieldRequired,
})
// Reset bit.
result &^= 1 << bitIdx
}
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *Roles) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *Roles) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode implements json.Marshaler. // Encode implements json.Marshaler.
func (s *Script) Encode(e *jx.Encoder) { func (s *Script) Encode(e *jx.Encoder) {
e.ObjStart() e.ObjStart()
@@ -562,22 +352,17 @@ func (s *Script) encodeFields(e *jx.Encoder) {
e.Str(s.Source) e.Str(s.Source)
} }
{ {
e.FieldStart("ResourceType") e.FieldStart("SubmissionID")
e.Int32(s.ResourceType) e.Int64(s.SubmissionID)
}
{
e.FieldStart("ResourceID")
e.Int64(s.ResourceID)
} }
} }
var jsonFieldsNameOfScript = [6]string{ var jsonFieldsNameOfScript = [5]string{
0: "ID", 0: "ID",
1: "Name", 1: "Name",
2: "Hash", 2: "Hash",
3: "Source", 3: "Source",
4: "ResourceType", 4: "SubmissionID",
5: "ResourceID",
} }
// Decode decodes Script from json. // Decode decodes Script from json.
@@ -637,29 +422,17 @@ func (s *Script) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"Source\"") return errors.Wrap(err, "decode field \"Source\"")
} }
case "ResourceType": case "SubmissionID":
requiredBitSet[0] |= 1 << 4 requiredBitSet[0] |= 1 << 4
if err := func() error {
v, err := d.Int32()
s.ResourceType = int32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceType\"")
}
case "ResourceID":
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.SubmissionID = int64(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 \"ResourceID\"") return errors.Wrap(err, "decode field \"SubmissionID\"")
} }
default: default:
return d.Skip() return d.Skip()
@@ -671,7 +444,7 @@ func (s *Script) 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{
0b00111111, 0b00011111,
} { } {
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.
@@ -735,22 +508,17 @@ func (s *ScriptCreate) encodeFields(e *jx.Encoder) {
e.Str(s.Source) e.Str(s.Source)
} }
{ {
e.FieldStart("ResourceType") if s.SubmissionID.Set {
e.Int32(s.ResourceType) e.FieldStart("SubmissionID")
} s.SubmissionID.Encode(e)
{
if s.ResourceID.Set {
e.FieldStart("ResourceID")
s.ResourceID.Encode(e)
} }
} }
} }
var jsonFieldsNameOfScriptCreate = [4]string{ var jsonFieldsNameOfScriptCreate = [3]string{
0: "Name", 0: "Name",
1: "Source", 1: "Source",
2: "ResourceType", 2: "SubmissionID",
3: "ResourceID",
} }
// Decode decodes ScriptCreate from json. // Decode decodes ScriptCreate from json.
@@ -786,27 +554,15 @@ func (s *ScriptCreate) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"Source\"") return errors.Wrap(err, "decode field \"Source\"")
} }
case "ResourceType": case "SubmissionID":
requiredBitSet[0] |= 1 << 2
if err := func() error { if err := func() error {
v, err := d.Int32() s.SubmissionID.Reset()
s.ResourceType = int32(v) if err := s.SubmissionID.Decode(d); err != nil {
if err != nil {
return err return err
} }
return nil return nil
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"ResourceType\"") return errors.Wrap(err, "decode field \"SubmissionID\"")
}
case "ResourceID":
if err := func() error {
s.ResourceID.Reset()
if err := s.ResourceID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
} }
default: default:
return d.Skip() return d.Skip()
@@ -818,7 +574,7 @@ func (s *ScriptCreate) 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{
0b00000111, 0b00000011,
} { } {
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.
@@ -1314,25 +1070,18 @@ func (s *ScriptUpdate) encodeFields(e *jx.Encoder) {
} }
} }
{ {
if s.ResourceType.Set { if s.SubmissionID.Set {
e.FieldStart("ResourceType") e.FieldStart("SubmissionID")
s.ResourceType.Encode(e) s.SubmissionID.Encode(e)
}
}
{
if s.ResourceID.Set {
e.FieldStart("ResourceID")
s.ResourceID.Encode(e)
} }
} }
} }
var jsonFieldsNameOfScriptUpdate = [5]string{ var jsonFieldsNameOfScriptUpdate = [4]string{
0: "ID", 0: "ID",
1: "Name", 1: "Name",
2: "Source", 2: "Source",
3: "ResourceType", 3: "SubmissionID",
4: "ResourceID",
} }
// Decode decodes ScriptUpdate from json. // Decode decodes ScriptUpdate from json.
@@ -1376,25 +1125,15 @@ func (s *ScriptUpdate) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"Source\"") return errors.Wrap(err, "decode field \"Source\"")
} }
case "ResourceType": case "SubmissionID":
if err := func() error { if err := func() error {
s.ResourceType.Reset() s.SubmissionID.Reset()
if err := s.ResourceType.Decode(d); err != nil { if err := s.SubmissionID.Decode(d); err != nil {
return err return err
} }
return nil return nil
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"ResourceType\"") return errors.Wrap(err, "decode field \"SubmissionID\"")
}
case "ResourceID":
if err := func() error {
s.ResourceID.Reset()
if err := s.ResourceID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
} }
default: default:
return d.Skip() return d.Skip()
@@ -1502,19 +1241,19 @@ func (s *Submission) encodeFields(e *jx.Encoder) {
e.Bool(s.Completed) e.Bool(s.Completed)
} }
{ {
if s.UploadedAssetID.Set { e.FieldStart("SubmissionType")
e.FieldStart("UploadedAssetID") e.Int32(s.SubmissionType)
s.UploadedAssetID.Encode(e) }
{
if s.TargetAssetID.Set {
e.FieldStart("TargetAssetID")
s.TargetAssetID.Encode(e)
} }
} }
{ {
e.FieldStart("StatusID") e.FieldStart("StatusID")
e.Int32(s.StatusID) e.Int32(s.StatusID)
} }
{
e.FieldStart("StatusMessage")
e.Str(s.StatusMessage)
}
} }
var jsonFieldsNameOfSubmission = [13]string{ var jsonFieldsNameOfSubmission = [13]string{
@@ -1528,9 +1267,9 @@ var jsonFieldsNameOfSubmission = [13]string{
7: "AssetID", 7: "AssetID",
8: "AssetVersion", 8: "AssetVersion",
9: "Completed", 9: "Completed",
10: "UploadedAssetID", 10: "SubmissionType",
11: "StatusID", 11: "TargetAssetID",
12: "StatusMessage", 12: "StatusID",
} }
// Decode decodes Submission from json. // Decode decodes Submission from json.
@@ -1662,18 +1401,30 @@ func (s *Submission) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"Completed\"") return errors.Wrap(err, "decode field \"Completed\"")
} }
case "UploadedAssetID": case "SubmissionType":
requiredBitSet[1] |= 1 << 2
if err := func() error { if err := func() error {
s.UploadedAssetID.Reset() v, err := d.Int32()
if err := s.UploadedAssetID.Decode(d); err != nil { s.SubmissionType = int32(v)
if err != nil {
return err return err
} }
return nil return nil
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"UploadedAssetID\"") return errors.Wrap(err, "decode field \"SubmissionType\"")
}
case "TargetAssetID":
if err := func() error {
s.TargetAssetID.Reset()
if err := s.TargetAssetID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"TargetAssetID\"")
} }
case "StatusID": case "StatusID":
requiredBitSet[1] |= 1 << 3 requiredBitSet[1] |= 1 << 4
if err := func() error { if err := func() error {
v, err := d.Int32() v, err := d.Int32()
s.StatusID = int32(v) s.StatusID = int32(v)
@@ -1684,18 +1435,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 << 4
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()
} }
@@ -1707,7 +1446,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,
0b00011011, 0b00010111,
} { } {
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.
@@ -1782,14 +1521,21 @@ func (s *SubmissionCreate) encodeFields(e *jx.Encoder) {
e.FieldStart("AssetVersion") e.FieldStart("AssetVersion")
e.Int64(s.AssetVersion) e.Int64(s.AssetVersion)
} }
{
if s.TargetAssetID.Set {
e.FieldStart("TargetAssetID")
s.TargetAssetID.Encode(e)
}
}
} }
var jsonFieldsNameOfSubmissionCreate = [5]string{ var jsonFieldsNameOfSubmissionCreate = [6]string{
0: "DisplayName", 0: "DisplayName",
1: "Creator", 1: "Creator",
2: "GameID", 2: "GameID",
3: "AssetID", 3: "AssetID",
4: "AssetVersion", 4: "AssetVersion",
5: "TargetAssetID",
} }
// Decode decodes SubmissionCreate from json. // Decode decodes SubmissionCreate from json.
@@ -1861,6 +1607,16 @@ 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 "TargetAssetID":
if err := func() error {
s.TargetAssetID.Reset()
if err := s.TargetAssetID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"TargetAssetID\"")
}
default: default:
return d.Skip() return d.Skip()
} }
@@ -1916,133 +1672,3 @@ func (s *SubmissionCreate) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data) d := jx.DecodeBytes(data)
return s.Decode(d) return s.Decode(d)
} }
// Encode implements json.Marshaler.
func (s *User) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *User) encodeFields(e *jx.Encoder) {
{
e.FieldStart("UserID")
e.Int64(s.UserID)
}
{
e.FieldStart("Username")
e.Str(s.Username)
}
{
e.FieldStart("AvatarURL")
e.Str(s.AvatarURL)
}
}
var jsonFieldsNameOfUser = [3]string{
0: "UserID",
1: "Username",
2: "AvatarURL",
}
// Decode decodes User from json.
func (s *User) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode User to nil")
}
var requiredBitSet [1]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "UserID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.UserID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"UserID\"")
}
case "Username":
requiredBitSet[0] |= 1 << 1
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 "AvatarURL":
requiredBitSet[0] |= 1 << 2
if err := func() error {
v, err := d.Str()
s.AvatarURL = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"AvatarURL\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode User")
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00000111,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
//
// If XOR result is not zero, result is not equal to expected, so some fields are missed.
// Bits of fields which would be set are actually bits of missed fields.
missed := bits.OnesCount8(result)
for bitN := 0; bitN < missed; bitN++ {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfUser) {
name = jsonFieldsNameOfUser[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
failures = append(failures, validate.FieldError{
Name: name,
Error: validate.ErrFieldRequired,
})
// Reset bit.
result &^= 1 << bitIdx
}
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *User) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *User) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}

View File

@@ -6,15 +6,12 @@ package api
type OperationName = string type OperationName = string
const ( const (
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject" ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject"
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges" ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
ActionSubmissionRetryValidateOperation OperationName = "ActionSubmissionRetryValidate"
ActionSubmissionRevokeOperation OperationName = "ActionSubmissionRevoke" ActionSubmissionRevokeOperation OperationName = "ActionSubmissionRevoke"
ActionSubmissionSubmitOperation OperationName = "ActionSubmissionSubmit" ActionSubmissionSubmitOperation OperationName = "ActionSubmissionSubmit"
ActionSubmissionTriggerUploadOperation OperationName = "ActionSubmissionTriggerUpload" ActionSubmissionTriggerUploadOperation OperationName = "ActionSubmissionTriggerUpload"
ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate" ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateScriptOperation OperationName = "CreateScript" CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy" CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission" CreateSubmissionOperation OperationName = "CreateSubmission"
@@ -26,10 +23,6 @@ const (
ListScriptPolicyOperation OperationName = "ListScriptPolicy" ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts" ListScriptsOperation OperationName = "ListScripts"
ListSubmissionsOperation OperationName = "ListSubmissions" ListSubmissionsOperation OperationName = "ListSubmissions"
ReleaseSubmissionsOperation OperationName = "ReleaseSubmissions"
SessionRolesOperation OperationName = "SessionRoles"
SessionUserOperation OperationName = "SessionUser"
SessionValidateOperation OperationName = "SessionValidate"
SetSubmissionCompletedOperation OperationName = "SetSubmissionCompleted" SetSubmissionCompletedOperation OperationName = "SetSubmissionCompleted"
UpdateScriptOperation OperationName = "UpdateScript" UpdateScriptOperation OperationName = "UpdateScript"
UpdateScriptPolicyOperation OperationName = "UpdateScriptPolicy" UpdateScriptPolicyOperation OperationName = "UpdateScriptPolicy"

View File

@@ -15,72 +15,6 @@ import (
"github.com/ogen-go/ogen/validate" "github.com/ogen-go/ogen/validate"
) )
// ActionSubmissionAcceptedParams is parameters of actionSubmissionAccepted operation.
type ActionSubmissionAcceptedParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
func unpackActionSubmissionAcceptedParams(packed middleware.Parameters) (params ActionSubmissionAcceptedParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
In: "path",
}
params.SubmissionID = packed[key].(int64)
}
return params
}
func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionAcceptedParams, _ 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
}
} 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.
@@ -213,72 +147,6 @@ func decodeActionSubmissionRequestChangesParams(args [1]string, argsEscaped bool
return params, nil return params, nil
} }
// ActionSubmissionRetryValidateParams is parameters of actionSubmissionRetryValidate operation.
type ActionSubmissionRetryValidateParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
func unpackActionSubmissionRetryValidateParams(packed middleware.Parameters) (params ActionSubmissionRetryValidateParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
In: "path",
}
params.SubmissionID = packed[key].(int64)
}
return params
}
func decodeActionSubmissionRetryValidateParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionRetryValidateParams, _ 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
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "SubmissionID",
In: "path",
Err: err,
}
}
return params, nil
}
// ActionSubmissionRevokeParams is parameters of actionSubmissionRevoke operation. // ActionSubmissionRevokeParams is parameters of actionSubmissionRevoke operation.
type ActionSubmissionRevokeParams struct { type ActionSubmissionRevokeParams struct {
// The unique identifier for a submission. // The unique identifier for a submission.
@@ -543,72 +411,6 @@ func decodeActionSubmissionTriggerValidateParams(args [1]string, argsEscaped boo
return params, nil return params, nil
} }
// ActionSubmissionValidatedParams is parameters of actionSubmissionValidated operation.
type ActionSubmissionValidatedParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
func unpackActionSubmissionValidatedParams(packed middleware.Parameters) (params ActionSubmissionValidatedParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
In: "path",
}
params.SubmissionID = packed[key].(int64)
}
return params
}
func decodeActionSubmissionValidatedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionValidatedParams, _ 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
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "SubmissionID",
In: "path",
Err: err,
}
}
return params, nil
}
// DeleteScriptParams is parameters of deleteScript operation. // DeleteScriptParams is parameters of deleteScript operation.
type DeleteScriptParams struct { type DeleteScriptParams struct {
// The unique identifier for a script. // The unique identifier for a script.
@@ -1038,7 +840,7 @@ func decodeListScriptPolicyParams(args [0]string, argsEscaped bool, r *http.Requ
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
@@ -1091,7 +893,7 @@ func decodeListScriptPolicyParams(args [0]string, argsEscaped bool, r *http.Requ
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
@@ -1359,7 +1161,7 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
@@ -1412,7 +1214,7 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
@@ -1665,7 +1467,6 @@ type ListSubmissionsParams struct {
DisplayName OptString DisplayName OptString
Creator OptString Creator OptString
GameID OptInt32 GameID OptInt32
Sort OptInt32
} }
func unpackListSubmissionsParams(packed middleware.Parameters) (params ListSubmissionsParams) { func unpackListSubmissionsParams(packed middleware.Parameters) (params ListSubmissionsParams) {
@@ -1710,15 +1511,6 @@ func unpackListSubmissionsParams(packed middleware.Parameters) (params ListSubmi
params.GameID = v.(OptInt32) params.GameID = v.(OptInt32)
} }
} }
{
key := middleware.ParameterKey{
Name: "Sort",
In: "query",
}
if v, ok := packed[key]; ok {
params.Sort = v.(OptInt32)
}
}
return params return params
} }
@@ -1767,7 +1559,7 @@ func decodeListSubmissionsParams(args [0]string, argsEscaped bool, r *http.Reque
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
@@ -1820,7 +1612,7 @@ func decodeListSubmissionsParams(args [0]string, argsEscaped bool, r *http.Reque
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
@@ -1999,47 +1791,6 @@ func decodeListSubmissionsParams(args [0]string, argsEscaped bool, r *http.Reque
Err: err, Err: err,
} }
} }
// Decode query: Sort.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "Sort",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
var paramsDotSortVal int32
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt32(val)
if err != nil {
return err
}
paramsDotSortVal = c
return nil
}(); err != nil {
return err
}
params.Sort.SetTo(paramsDotSortVal)
return nil
}); err != nil {
return err
}
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "Sort",
In: "query",
Err: err,
}
}
return params, nil return params, nil
} }
@@ -2347,7 +2098,7 @@ func decodeUpdateSubmissionModelParams(args [1]string, argsEscaped bool, r *http
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
@@ -2383,7 +2134,7 @@ func decodeUpdateSubmissionModelParams(args [1]string, argsEscaped bool, r *http
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {

View File

@@ -220,93 +220,6 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
} }
} }
func (s *Server) decodeReleaseSubmissionsRequest(r *http.Request) (
req []ReleaseInfo,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = multierr.Append(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
if r.ContentLength == 0 {
return req, close, validate.ErrBodyRequired
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return req, close, err
}
if len(buf) == 0 {
return req, close, validate.ErrBodyRequired
}
d := jx.DecodeBytes(buf)
var request []ReleaseInfo
if err := func() error {
request = make([]ReleaseInfo, 0)
if err := d.Arr(func(d *jx.Decoder) error {
var elem ReleaseInfo
if err := elem.Decode(d); err != nil {
return err
}
request = append(request, elem)
return nil
}); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
if err := func() error {
if request == nil {
return errors.New("nil is invalid value")
}
if err := (validate.Array{
MinLength: 1,
MinLengthSet: true,
MaxLength: 255,
MaxLengthSet: true,
}).ValidateLength(len(request)); err != nil {
return errors.Wrap(err, "array")
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeUpdateScriptRequest(r *http.Request) ( func (s *Server) decodeUpdateScriptRequest(r *http.Request) (
req *ScriptUpdate, req *ScriptUpdate,
close func() error, close func() error,

View File

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

View File

@@ -15,57 +15,6 @@ import (
"github.com/ogen-go/ogen/validate" "github.com/ogen-go/ogen/validate"
) )
func decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSubmissionAcceptedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionAcceptedNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
}
return res, errors.Wrap(defRes, "error")
}
func 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:
@@ -168,57 +117,6 @@ func decodeActionSubmissionRequestChangesResponse(resp *http.Response) (res *Act
return res, errors.Wrap(defRes, "error") return res, errors.Wrap(defRes, "error")
} }
func decodeActionSubmissionRetryValidateResponse(resp *http.Response) (res *ActionSubmissionRetryValidateNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionRetryValidateNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
}
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionRevokeResponse(resp *http.Response) (res *ActionSubmissionRevokeNoContent, _ error) { func decodeActionSubmissionRevokeResponse(resp *http.Response) (res *ActionSubmissionRevokeNoContent, _ error) {
switch resp.StatusCode { switch resp.StatusCode {
case 204: case 204:
@@ -423,57 +321,6 @@ func decodeActionSubmissionTriggerValidateResponse(resp *http.Response) (res *Ac
return res, errors.Wrap(defRes, "error") return res, errors.Wrap(defRes, "error")
} }
func decodeActionSubmissionValidatedResponse(resp *http.Response) (res *ActionSubmissionValidatedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionValidatedNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
}
return res, errors.Wrap(defRes, "error")
}
func decodeCreateScriptResponse(resp *http.Response) (res *ID, _ error) { func decodeCreateScriptResponse(resp *http.Response) (res *ID, _ error) {
switch resp.StatusCode { switch resp.StatusCode {
case 201: case 201:
@@ -1452,317 +1299,6 @@ func decodeListSubmissionsResponse(resp *http.Response) (res []Submission, _ err
return res, errors.Wrap(defRes, "error") return res, errors.Wrap(defRes, "error")
} }
func decodeReleaseSubmissionsResponse(resp *http.Response) (res *ReleaseSubmissionsCreated, _ error) {
switch resp.StatusCode {
case 201:
// Code 201.
return &ReleaseSubmissionsCreated{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
}
return res, errors.Wrap(defRes, "error")
}
func decodeSessionRolesResponse(resp *http.Response) (res *Roles, _ error) {
switch resp.StatusCode {
case 200:
// Code 200.
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Roles
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return &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
}
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 decodeSessionUserResponse(resp *http.Response) (res *User, _ error) {
switch resp.StatusCode {
case 200:
// Code 200.
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response User
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
}
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 decodeSessionValidateResponse(resp *http.Response) (res bool, _ error) {
switch resp.StatusCode {
case 200:
// Code 200.
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response bool
if err := func() error {
v, err := d.Bool()
response = bool(v)
if err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return 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
}
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 decodeSetSubmissionCompletedResponse(resp *http.Response) (res *SetSubmissionCompletedNoContent, _ error) { func decodeSetSubmissionCompletedResponse(resp *http.Response) (res *SetSubmissionCompletedNoContent, _ error) {
switch resp.StatusCode { switch resp.StatusCode {
case 204: case 204:

View File

@@ -13,13 +13,6 @@ import (
ht "github.com/ogen-go/ogen/http" ht "github.com/ogen-go/ogen/http"
) )
func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionRejectResponse(response *ActionSubmissionRejectNoContent, w http.ResponseWriter, span trace.Span) error { 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))
@@ -34,13 +27,6 @@ func encodeActionSubmissionRequestChangesResponse(response *ActionSubmissionRequ
return nil return nil
} }
func encodeActionSubmissionRetryValidateResponse(response *ActionSubmissionRetryValidateNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionRevokeResponse(response *ActionSubmissionRevokeNoContent, w http.ResponseWriter, span trace.Span) error { func encodeActionSubmissionRevokeResponse(response *ActionSubmissionRevokeNoContent, 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))
@@ -69,13 +55,6 @@ func encodeActionSubmissionTriggerValidateResponse(response *ActionSubmissionTri
return nil return nil
} }
func encodeActionSubmissionValidatedResponse(response *ActionSubmissionValidatedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeCreateScriptResponse(response *ID, w http.ResponseWriter, span trace.Span) error { func encodeCreateScriptResponse(response *ID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201) w.WriteHeader(201)
@@ -228,55 +207,6 @@ func encodeListSubmissionsResponse(response []Submission, w http.ResponseWriter,
return nil return nil
} }
func encodeReleaseSubmissionsResponse(response *ReleaseSubmissionsCreated, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
return nil
}
func encodeSessionRolesResponse(response *Roles, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeSessionUserResponse(response *User, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeSessionValidateResponse(response bool, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := new(jx.Encoder)
e.Bool(response)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeSetSubmissionCompletedResponse(response *SetSubmissionCompletedNoContent, w http.ResponseWriter, span trace.Span) error { func encodeSetSubmissionCompletedResponse(response *SetSubmissionCompletedNoContent, 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))

File diff suppressed because it is too large Load Diff

View File

@@ -4,25 +4,18 @@ package api
import ( import (
"fmt" "fmt"
"time"
) )
func (s *ErrorStatusCode) Error() string { func (s *ErrorStatusCode) Error() string {
return fmt.Sprintf("code %d: %+v", s.StatusCode, s.Response) return fmt.Sprintf("code %d: %+v", s.StatusCode, s.Response)
} }
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
type ActionSubmissionAcceptedNoContent struct{}
// ActionSubmissionRejectNoContent is response for ActionSubmissionReject operation. // ActionSubmissionRejectNoContent is response for ActionSubmissionReject operation.
type ActionSubmissionRejectNoContent struct{} type ActionSubmissionRejectNoContent struct{}
// ActionSubmissionRequestChangesNoContent is response for ActionSubmissionRequestChanges operation. // ActionSubmissionRequestChangesNoContent is response for ActionSubmissionRequestChanges operation.
type ActionSubmissionRequestChangesNoContent struct{} type ActionSubmissionRequestChangesNoContent struct{}
// ActionSubmissionRetryValidateNoContent is response for ActionSubmissionRetryValidate operation.
type ActionSubmissionRetryValidateNoContent struct{}
// ActionSubmissionRevokeNoContent is response for ActionSubmissionRevoke operation. // ActionSubmissionRevokeNoContent is response for ActionSubmissionRevoke operation.
type ActionSubmissionRevokeNoContent struct{} type ActionSubmissionRevokeNoContent struct{}
@@ -35,9 +28,6 @@ type ActionSubmissionTriggerUploadNoContent struct{}
// ActionSubmissionTriggerValidateNoContent is response for ActionSubmissionTriggerValidate operation. // ActionSubmissionTriggerValidateNoContent is response for ActionSubmissionTriggerValidate operation.
type ActionSubmissionTriggerValidateNoContent struct{} type ActionSubmissionTriggerValidateNoContent struct{}
// ActionSubmissionValidatedNoContent is response for ActionSubmissionValidated operation.
type ActionSubmissionValidatedNoContent struct{}
type CookieAuth struct { type CookieAuth struct {
APIKey string APIKey string
} }
@@ -264,58 +254,13 @@ func (o OptString) Or(d string) string {
return d return d
} }
// Ref: #/components/schemas/ReleaseInfo
type ReleaseInfo struct {
SubmissionID int64 `json:"SubmissionID"`
Date time.Time `json:"Date"`
}
// GetSubmissionID returns the value of SubmissionID.
func (s *ReleaseInfo) GetSubmissionID() int64 {
return s.SubmissionID
}
// GetDate returns the value of Date.
func (s *ReleaseInfo) GetDate() time.Time {
return s.Date
}
// SetSubmissionID sets the value of SubmissionID.
func (s *ReleaseInfo) SetSubmissionID(val int64) {
s.SubmissionID = val
}
// SetDate sets the value of Date.
func (s *ReleaseInfo) SetDate(val time.Time) {
s.Date = val
}
// ReleaseSubmissionsCreated is response for ReleaseSubmissions operation.
type ReleaseSubmissionsCreated struct{}
// Ref: #/components/schemas/Roles
type Roles struct {
Roles int32 `json:"Roles"`
}
// GetRoles returns the value of Roles.
func (s *Roles) GetRoles() int32 {
return s.Roles
}
// SetRoles sets the value of Roles.
func (s *Roles) SetRoles(val int32) {
s.Roles = val
}
// Ref: #/components/schemas/Script // Ref: #/components/schemas/Script
type Script struct { type Script struct {
ID int64 `json:"ID"` ID int64 `json:"ID"`
Name string `json:"Name"` Name string `json:"Name"`
Hash string `json:"Hash"` Hash string `json:"Hash"`
Source string `json:"Source"` Source string `json:"Source"`
ResourceType int32 `json:"ResourceType"` SubmissionID int64 `json:"SubmissionID"`
ResourceID int64 `json:"ResourceID"`
} }
// GetID returns the value of ID. // GetID returns the value of ID.
@@ -338,14 +283,9 @@ func (s *Script) GetSource() string {
return s.Source return s.Source
} }
// GetResourceType returns the value of ResourceType. // GetSubmissionID returns the value of SubmissionID.
func (s *Script) GetResourceType() int32 { func (s *Script) GetSubmissionID() int64 {
return s.ResourceType return s.SubmissionID
}
// GetResourceID returns the value of ResourceID.
func (s *Script) GetResourceID() int64 {
return s.ResourceID
} }
// SetID sets the value of ID. // SetID sets the value of ID.
@@ -368,22 +308,16 @@ func (s *Script) SetSource(val string) {
s.Source = val s.Source = val
} }
// SetResourceType sets the value of ResourceType. // SetSubmissionID sets the value of SubmissionID.
func (s *Script) SetResourceType(val int32) { func (s *Script) SetSubmissionID(val int64) {
s.ResourceType = val s.SubmissionID = val
}
// SetResourceID sets the value of ResourceID.
func (s *Script) SetResourceID(val int64) {
s.ResourceID = val
} }
// Ref: #/components/schemas/ScriptCreate // Ref: #/components/schemas/ScriptCreate
type ScriptCreate struct { type ScriptCreate struct {
Name string `json:"Name"` Name string `json:"Name"`
Source string `json:"Source"` Source string `json:"Source"`
ResourceType int32 `json:"ResourceType"` SubmissionID OptInt64 `json:"SubmissionID"`
ResourceID OptInt64 `json:"ResourceID"`
} }
// GetName returns the value of Name. // GetName returns the value of Name.
@@ -396,14 +330,9 @@ func (s *ScriptCreate) GetSource() string {
return s.Source return s.Source
} }
// GetResourceType returns the value of ResourceType. // GetSubmissionID returns the value of SubmissionID.
func (s *ScriptCreate) GetResourceType() int32 { func (s *ScriptCreate) GetSubmissionID() OptInt64 {
return s.ResourceType return s.SubmissionID
}
// GetResourceID returns the value of ResourceID.
func (s *ScriptCreate) GetResourceID() OptInt64 {
return s.ResourceID
} }
// SetName sets the value of Name. // SetName sets the value of Name.
@@ -416,14 +345,9 @@ func (s *ScriptCreate) SetSource(val string) {
s.Source = val s.Source = val
} }
// SetResourceType sets the value of ResourceType. // SetSubmissionID sets the value of SubmissionID.
func (s *ScriptCreate) SetResourceType(val int32) { func (s *ScriptCreate) SetSubmissionID(val OptInt64) {
s.ResourceType = val s.SubmissionID = val
}
// SetResourceID sets the value of ResourceID.
func (s *ScriptCreate) SetResourceID(val OptInt64) {
s.ResourceID = val
} }
// Ref: #/components/schemas/ScriptPolicy // Ref: #/components/schemas/ScriptPolicy
@@ -564,8 +488,7 @@ type ScriptUpdate struct {
ID int64 `json:"ID"` ID int64 `json:"ID"`
Name OptString `json:"Name"` Name OptString `json:"Name"`
Source OptString `json:"Source"` Source OptString `json:"Source"`
ResourceType OptInt32 `json:"ResourceType"` SubmissionID OptInt64 `json:"SubmissionID"`
ResourceID OptInt64 `json:"ResourceID"`
} }
// GetID returns the value of ID. // GetID returns the value of ID.
@@ -583,14 +506,9 @@ func (s *ScriptUpdate) GetSource() OptString {
return s.Source return s.Source
} }
// GetResourceType returns the value of ResourceType. // GetSubmissionID returns the value of SubmissionID.
func (s *ScriptUpdate) GetResourceType() OptInt32 { func (s *ScriptUpdate) GetSubmissionID() OptInt64 {
return s.ResourceType return s.SubmissionID
}
// GetResourceID returns the value of ResourceID.
func (s *ScriptUpdate) GetResourceID() OptInt64 {
return s.ResourceID
} }
// SetID sets the value of ID. // SetID sets the value of ID.
@@ -608,14 +526,9 @@ func (s *ScriptUpdate) SetSource(val OptString) {
s.Source = val s.Source = val
} }
// SetResourceType sets the value of ResourceType. // SetSubmissionID sets the value of SubmissionID.
func (s *ScriptUpdate) SetResourceType(val OptInt32) { func (s *ScriptUpdate) SetSubmissionID(val OptInt64) {
s.ResourceType = val s.SubmissionID = val
}
// SetResourceID sets the value of ResourceID.
func (s *ScriptUpdate) SetResourceID(val OptInt64) {
s.ResourceID = val
} }
// SetSubmissionCompletedNoContent is response for SetSubmissionCompleted operation. // SetSubmissionCompletedNoContent is response for SetSubmissionCompleted operation.
@@ -623,19 +536,19 @@ type SetSubmissionCompletedNoContent struct{}
// Ref: #/components/schemas/Submission // Ref: #/components/schemas/Submission
type Submission struct { type Submission struct {
ID int64 `json:"ID"` ID int64 `json:"ID"`
DisplayName string `json:"DisplayName"` DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"` Creator string `json:"Creator"`
GameID int32 `json:"GameID"` GameID int32 `json:"GameID"`
CreatedAt int64 `json:"CreatedAt"` CreatedAt int64 `json:"CreatedAt"`
UpdatedAt int64 `json:"UpdatedAt"` UpdatedAt int64 `json:"UpdatedAt"`
Submitter int64 `json:"Submitter"` Submitter int64 `json:"Submitter"`
AssetID int64 `json:"AssetID"` AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"` AssetVersion int64 `json:"AssetVersion"`
Completed bool `json:"Completed"` Completed bool `json:"Completed"`
UploadedAssetID OptInt64 `json:"UploadedAssetID"` SubmissionType int32 `json:"SubmissionType"`
StatusID int32 `json:"StatusID"` TargetAssetID OptInt64 `json:"TargetAssetID"`
StatusMessage string `json:"StatusMessage"` StatusID int32 `json:"StatusID"`
} }
// GetID returns the value of ID. // GetID returns the value of ID.
@@ -688,9 +601,14 @@ func (s *Submission) GetCompleted() bool {
return s.Completed return s.Completed
} }
// GetUploadedAssetID returns the value of UploadedAssetID. // GetSubmissionType returns the value of SubmissionType.
func (s *Submission) GetUploadedAssetID() OptInt64 { func (s *Submission) GetSubmissionType() int32 {
return s.UploadedAssetID return s.SubmissionType
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *Submission) GetTargetAssetID() OptInt64 {
return s.TargetAssetID
} }
// GetStatusID returns the value of StatusID. // GetStatusID returns the value of StatusID.
@@ -698,11 +616,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
@@ -753,9 +666,14 @@ func (s *Submission) SetCompleted(val bool) {
s.Completed = val s.Completed = val
} }
// SetUploadedAssetID sets the value of UploadedAssetID. // SetSubmissionType sets the value of SubmissionType.
func (s *Submission) SetUploadedAssetID(val OptInt64) { func (s *Submission) SetSubmissionType(val int32) {
s.UploadedAssetID = val s.SubmissionType = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *Submission) SetTargetAssetID(val OptInt64) {
s.TargetAssetID = val
} }
// SetStatusID sets the value of StatusID. // SetStatusID sets the value of StatusID.
@@ -763,18 +681,14 @@ 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/SubmissionCreate // Ref: #/components/schemas/SubmissionCreate
type SubmissionCreate struct { type SubmissionCreate struct {
DisplayName string `json:"DisplayName"` DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"` Creator string `json:"Creator"`
GameID int32 `json:"GameID"` GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"` AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"` AssetVersion int64 `json:"AssetVersion"`
TargetAssetID OptInt64 `json:"TargetAssetID"`
} }
// GetDisplayName returns the value of DisplayName. // GetDisplayName returns the value of DisplayName.
@@ -802,6 +716,11 @@ func (s *SubmissionCreate) GetAssetVersion() int64 {
return s.AssetVersion return s.AssetVersion
} }
// GetTargetAssetID returns the value of TargetAssetID.
func (s *SubmissionCreate) GetTargetAssetID() OptInt64 {
return s.TargetAssetID
}
// SetDisplayName sets the value of DisplayName. // SetDisplayName sets the value of DisplayName.
func (s *SubmissionCreate) SetDisplayName(val string) { func (s *SubmissionCreate) SetDisplayName(val string) {
s.DisplayName = val s.DisplayName = val
@@ -827,6 +746,11 @@ func (s *SubmissionCreate) SetAssetVersion(val int64) {
s.AssetVersion = val s.AssetVersion = val
} }
// SetTargetAssetID sets the value of TargetAssetID.
func (s *SubmissionCreate) SetTargetAssetID(val OptInt64) {
s.TargetAssetID = val
}
// UpdateScriptNoContent is response for UpdateScript operation. // UpdateScriptNoContent is response for UpdateScript operation.
type UpdateScriptNoContent struct{} type UpdateScriptNoContent struct{}
@@ -835,40 +759,3 @@ type UpdateScriptPolicyNoContent struct{}
// UpdateSubmissionModelNoContent is response for UpdateSubmissionModel operation. // UpdateSubmissionModelNoContent is response for UpdateSubmissionModel operation.
type UpdateSubmissionModelNoContent struct{} type UpdateSubmissionModelNoContent struct{}
// Ref: #/components/schemas/User
type User struct {
UserID int64 `json:"UserID"`
Username string `json:"Username"`
AvatarURL string `json:"AvatarURL"`
}
// GetUserID returns the value of UserID.
func (s *User) GetUserID() int64 {
return s.UserID
}
// GetUsername returns the value of Username.
func (s *User) GetUsername() string {
return s.Username
}
// GetAvatarURL returns the value of AvatarURL.
func (s *User) GetAvatarURL() string {
return s.AvatarURL
}
// SetUserID sets the value of UserID.
func (s *User) SetUserID(val int64) {
s.UserID = val
}
// SetUsername sets the value of Username.
func (s *User) SetUsername(val string) {
s.Username = val
}
// SetAvatarURL sets the value of AvatarURL.
func (s *User) SetAvatarURL(val string) {
s.AvatarURL = val
}

View File

@@ -8,12 +8,6 @@ import (
// Handler handles operations described by OpenAPI v3 specification. // Handler handles operations described by OpenAPI v3 specification.
type Handler interface { type Handler interface {
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/reset-validating
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionReject implements actionSubmissionReject operation. // ActionSubmissionReject implements actionSubmissionReject operation.
// //
// Role Reviewer changes status from Submitted -> Rejected. // Role Reviewer changes status from Submitted -> Rejected.
@@ -26,12 +20,6 @@ type Handler interface {
// //
// POST /submissions/{SubmissionID}/status/request-changes // POST /submissions/{SubmissionID}/status/request-changes
ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error
// ActionSubmissionRetryValidate implements actionSubmissionRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
//
// POST /submissions/{SubmissionID}/status/retry-validate
ActionSubmissionRetryValidate(ctx context.Context, params ActionSubmissionRetryValidateParams) error
// ActionSubmissionRevoke implements actionSubmissionRevoke operation. // ActionSubmissionRevoke implements actionSubmissionRevoke operation.
// //
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction. // Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
@@ -52,16 +40,10 @@ type Handler interface {
ActionSubmissionTriggerUpload(ctx context.Context, params ActionSubmissionTriggerUploadParams) error ActionSubmissionTriggerUpload(ctx context.Context, params ActionSubmissionTriggerUploadParams) error
// ActionSubmissionTriggerValidate implements actionSubmissionTriggerValidate operation. // ActionSubmissionTriggerValidate implements actionSubmissionTriggerValidate operation.
// //
// Role Reviewer triggers validation and changes status from Submitted -> Validating. // Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
// //
// POST /submissions/{SubmissionID}/status/trigger-validate // POST /submissions/{SubmissionID}/status/trigger-validate
ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error
// ActionSubmissionValidated implements actionSubmissionValidated operation.
//
// Role Admin manually resets uploading softlock and changes status from Uploading -> Validated.
//
// POST /submissions/{SubmissionID}/status/reset-uploading
ActionSubmissionValidated(ctx context.Context, params ActionSubmissionValidatedParams) error
// CreateScript implements createScript operation. // CreateScript implements createScript operation.
// //
// Create a new script. // Create a new script.
@@ -128,33 +110,9 @@ type Handler interface {
// //
// GET /submissions // GET /submissions
ListSubmissions(ctx context.Context, params ListSubmissionsParams) ([]Submission, error) ListSubmissions(ctx context.Context, params ListSubmissionsParams) ([]Submission, error)
// ReleaseSubmissions implements releaseSubmissions operation.
//
// Release a set of uploaded maps.
//
// POST /release-submissions
ReleaseSubmissions(ctx context.Context, req []ReleaseInfo) error
// SessionRoles implements sessionRoles operation.
//
// Get list of roles for the current session.
//
// GET /session/roles
SessionRoles(ctx context.Context) (*Roles, error)
// SessionUser implements sessionUser operation.
//
// Get information about the currently logged in user.
//
// GET /session/user
SessionUser(ctx context.Context) (*User, error)
// SessionValidate implements sessionValidate operation.
//
// Ask if the current session is valid.
//
// GET /session/validate
SessionValidate(ctx context.Context) (bool, error)
// SetSubmissionCompleted implements setSubmissionCompleted operation. // SetSubmissionCompleted implements setSubmissionCompleted operation.
// //
// Called by maptest when a player completes the map. // Retrieve map with ID.
// //
// POST /submissions/{SubmissionID}/completed // POST /submissions/{SubmissionID}/completed
SetSubmissionCompleted(ctx context.Context, params SetSubmissionCompletedParams) error SetSubmissionCompleted(ctx context.Context, params SetSubmissionCompletedParams) error

View File

@@ -13,15 +13,6 @@ type UnimplementedHandler struct{}
var _ Handler = UnimplementedHandler{} var _ Handler = UnimplementedHandler{}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/reset-validating
func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionReject implements actionSubmissionReject operation. // ActionSubmissionReject implements actionSubmissionReject operation.
// //
// Role Reviewer changes status from Submitted -> Rejected. // Role Reviewer changes status from Submitted -> Rejected.
@@ -40,15 +31,6 @@ func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context,
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }
// ActionSubmissionRetryValidate implements actionSubmissionRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
//
// POST /submissions/{SubmissionID}/status/retry-validate
func (UnimplementedHandler) ActionSubmissionRetryValidate(ctx context.Context, params ActionSubmissionRetryValidateParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionRevoke implements actionSubmissionRevoke operation. // ActionSubmissionRevoke implements actionSubmissionRevoke operation.
// //
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction. // Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
@@ -78,22 +60,13 @@ func (UnimplementedHandler) ActionSubmissionTriggerUpload(ctx context.Context, p
// ActionSubmissionTriggerValidate implements actionSubmissionTriggerValidate operation. // ActionSubmissionTriggerValidate implements actionSubmissionTriggerValidate operation.
// //
// Role Reviewer triggers validation and changes status from Submitted -> Validating. // Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
// //
// POST /submissions/{SubmissionID}/status/trigger-validate // POST /submissions/{SubmissionID}/status/trigger-validate
func (UnimplementedHandler) ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error { func (UnimplementedHandler) ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error {
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }
// ActionSubmissionValidated implements actionSubmissionValidated operation.
//
// Role Admin manually resets uploading softlock and changes status from Uploading -> Validated.
//
// POST /submissions/{SubmissionID}/status/reset-uploading
func (UnimplementedHandler) ActionSubmissionValidated(ctx context.Context, params ActionSubmissionValidatedParams) error {
return ht.ErrNotImplemented
}
// CreateScript implements createScript operation. // CreateScript implements createScript operation.
// //
// Create a new script. // Create a new script.
@@ -193,45 +166,9 @@ func (UnimplementedHandler) ListSubmissions(ctx context.Context, params ListSubm
return r, ht.ErrNotImplemented return r, ht.ErrNotImplemented
} }
// ReleaseSubmissions implements releaseSubmissions operation.
//
// Release a set of uploaded maps.
//
// POST /release-submissions
func (UnimplementedHandler) ReleaseSubmissions(ctx context.Context, req []ReleaseInfo) error {
return ht.ErrNotImplemented
}
// SessionRoles implements sessionRoles operation.
//
// Get list of roles for the current session.
//
// GET /session/roles
func (UnimplementedHandler) SessionRoles(ctx context.Context) (r *Roles, _ error) {
return r, ht.ErrNotImplemented
}
// SessionUser implements sessionUser operation.
//
// Get information about the currently logged in user.
//
// GET /session/user
func (UnimplementedHandler) SessionUser(ctx context.Context) (r *User, _ error) {
return r, ht.ErrNotImplemented
}
// SessionValidate implements sessionValidate operation.
//
// Ask if the current session is valid.
//
// GET /session/validate
func (UnimplementedHandler) SessionValidate(ctx context.Context) (r bool, _ error) {
return r, ht.ErrNotImplemented
}
// SetSubmissionCompleted implements setSubmissionCompleted operation. // SetSubmissionCompleted implements setSubmissionCompleted operation.
// //
// Called by maptest when a player completes the map. // Retrieve map with ID.
// //
// POST /submissions/{SubmissionID}/completed // POST /submissions/{SubmissionID}/completed
func (UnimplementedHandler) SetSubmissionCompleted(ctx context.Context, params SetSubmissionCompletedParams) error { func (UnimplementedHandler) SetSubmissionCompleted(ctx context.Context, params SetSubmissionCompletedParams) error {

View File

@@ -266,25 +266,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}
} }
@@ -340,53 +321,3 @@ func (s *SubmissionCreate) Validate() error {
} }
return nil return nil
} }
func (s *User) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Username)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Username",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 256,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.AvatarURL)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AvatarURL",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}

View File

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

View File

@@ -9,16 +9,6 @@ import (
var ( var (
ErrNotExist = errors.New("resource does not exist") ErrNotExist = errors.New("resource does not exist")
ErroNoRowsAffected = errors.New("query did not affect any rows") ErroNoRowsAffected = errors.New("query did not affect any rows")
ErrInvalidListSort = errors.New("invalid list sort parameter [1,2,3,4]")
)
type ListSort uint32
const (
ListSortDisabled ListSort = 0
ListSortDisplayNameAscending ListSort = 1
ListSortDisplayNameDescending ListSort = 2
ListSortDateAscending ListSort = 3
ListSortDateDescending ListSort = 4
) )
type Datastore interface { type Datastore interface {
@@ -32,10 +22,10 @@ type Submissions interface {
GetList(ctx context.Context, id []int64) ([]model.Submission, error) GetList(ctx context.Context, id []int64) ([]model.Submission, error)
Create(ctx context.Context, smap model.Submission) (model.Submission, error) Create(ctx context.Context, smap model.Submission) (model.Submission, error)
Update(ctx context.Context, id int64, values OptionalMap) error Update(ctx context.Context, id int64, values OptionalMap) error
IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.SubmissionStatus, values OptionalMap) error IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values OptionalMap) error
IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.SubmissionStatus, values OptionalMap) (model.Submission, error) IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.Status, values OptionalMap) (model.Submission, error)
Delete(ctx context.Context, id int64) error Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) ([]model.Submission, error) List(ctx context.Context, filters OptionalMap, page model.Page) ([]model.Submission, error)
} }
type Scripts interface { type Scripts interface {

View File

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

View File

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

View File

@@ -21,11 +21,6 @@ import (
"github.com/ogen-go/ogen/uri" "github.com/ogen-go/ogen/uri"
) )
func trimTrailingSlashes(u *url.URL) {
u.Path = strings.TrimRight(u.Path, "/")
u.RawPath = strings.TrimRight(u.RawPath, "/")
}
// Invoker invokes operations described by OpenAPI v3 specification. // Invoker invokes operations described by OpenAPI v3 specification.
type Invoker interface { type Invoker interface {
// ActionSubmissionAccepted invokes actionSubmissionAccepted operation. // ActionSubmissionAccepted invokes actionSubmissionAccepted operation.
@@ -34,6 +29,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
// ActionSubmissionReleased invokes actionSubmissionReleased operation.
//
// (Internal endpoint) Role Releaser changes status from releasing -> released.
//
// POST /submissions/{SubmissionID}/status/releaser-released
ActionSubmissionReleased(ctx context.Context, params ActionSubmissionReleasedParams) error
// ActionSubmissionUploaded invokes actionSubmissionUploaded operation. // ActionSubmissionUploaded invokes actionSubmissionUploaded operation.
// //
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@@ -76,12 +77,12 @@ type Invoker interface {
// //
// GET /scripts // GET /scripts
ListScripts(ctx context.Context, params ListScriptsParams) ([]Script, error) ListScripts(ctx context.Context, params ListScriptsParams) ([]Script, error)
// UpdateSubmissionValidatedModel invokes updateSubmissionValidatedModel operation. // UpdateSubmissionModel invokes updateSubmissionModel operation.
// //
// Update validated model. // Update model following role restrictions.
// //
// POST /submissions/{SubmissionID}/validated-model // POST /submissions/{SubmissionID}/model
UpdateSubmissionValidatedModel(ctx context.Context, params UpdateSubmissionValidatedModelParams) error UpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) error
} }
// Client implements OAS client. // Client implements OAS client.
@@ -98,6 +99,11 @@ var _ Handler = struct {
*Client *Client
}{} }{}
func trimTrailingSlashes(u *url.URL) {
u.Path = strings.TrimRight(u.Path, "/")
u.RawPath = strings.TrimRight(u.RawPath, "/")
}
// NewClient initializes new Client defined by OAS. // NewClient initializes new Client defined by OAS.
func NewClient(serverURL string, opts ...ClientOption) (*Client, error) { func NewClient(serverURL string, opts ...ClientOption) (*Client, error) {
u, err := url.Parse(serverURL) u, err := url.Parse(serverURL)
@@ -200,24 +206,6 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
pathParts[2] = "/status/validator-failed" pathParts[2] = "/status/validator-failed"
uri.AddPathParts(u, pathParts[:]...) uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "StatusMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "StatusMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.StatusMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
stage = "EncodeRequest" stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u) r, err := ht.NewRequest(ctx, "POST", u)
if err != nil { if err != nil {
@@ -240,6 +228,97 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
return result, nil return result, nil
} }
// ActionSubmissionReleased invokes actionSubmissionReleased operation.
//
// (Internal endpoint) Role Releaser changes status from releasing -> released.
//
// POST /submissions/{SubmissionID}/status/releaser-released
func (c *Client) ActionSubmissionReleased(ctx context.Context, params ActionSubmissionReleasedParams) error {
_, err := c.sendActionSubmissionReleased(ctx, params)
return err
}
func (c *Client) sendActionSubmissionReleased(ctx context.Context, params ActionSubmissionReleasedParams) (res *ActionSubmissionReleasedNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionSubmissionReleased"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/releaser-released"),
}
// Run stopwatch.
startTime := time.Now()
defer func() {
// Use floating point division here for higher precision (instead of Millisecond method).
elapsedDuration := time.Since(startTime)
c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...))
}()
// Increment request counter.
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, ActionSubmissionReleasedOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/submissions/"
{
// Encode "SubmissionID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "SubmissionID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/status/releaser-released"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeActionSubmissionReleasedResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionSubmissionUploaded invokes actionSubmissionUploaded operation. // ActionSubmissionUploaded invokes actionSubmissionUploaded operation.
// //
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@@ -312,15 +391,18 @@ func (c *Client) sendActionSubmissionUploaded(ctx context.Context, params Action
stage = "EncodeQueryParams" stage = "EncodeQueryParams"
q := uri.NewQueryEncoder() q := uri.NewQueryEncoder()
{ {
// Encode "UploadedAssetID" parameter. // Encode "TargetAssetID" parameter.
cfg := uri.QueryParameterEncodingConfig{ cfg := uri.QueryParameterEncodingConfig{
Name: "UploadedAssetID", Name: "TargetAssetID",
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.Int64ToString(params.UploadedAssetID)) if val, ok := params.TargetAssetID.Get(); ok {
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil { }); err != nil {
return res, errors.Wrap(err, "encode query") return res, errors.Wrap(err, "encode query")
} }
@@ -1007,21 +1089,21 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
return result, nil return result, nil
} }
// UpdateSubmissionValidatedModel invokes updateSubmissionValidatedModel operation. // UpdateSubmissionModel invokes updateSubmissionModel operation.
// //
// Update validated model. // Update model following role restrictions.
// //
// POST /submissions/{SubmissionID}/validated-model // POST /submissions/{SubmissionID}/model
func (c *Client) UpdateSubmissionValidatedModel(ctx context.Context, params UpdateSubmissionValidatedModelParams) error { func (c *Client) UpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) error {
_, err := c.sendUpdateSubmissionValidatedModel(ctx, params) _, err := c.sendUpdateSubmissionModel(ctx, params)
return err return err
} }
func (c *Client) sendUpdateSubmissionValidatedModel(ctx context.Context, params UpdateSubmissionValidatedModelParams) (res *UpdateSubmissionValidatedModelNoContent, err error) { func (c *Client) sendUpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) (res *UpdateSubmissionModelNoContent, err error) {
otelAttrs := []attribute.KeyValue{ otelAttrs := []attribute.KeyValue{
otelogen.OperationID("updateSubmissionValidatedModel"), otelogen.OperationID("updateSubmissionModel"),
semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/validated-model"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/model"),
} }
// Run stopwatch. // Run stopwatch.
@@ -1036,7 +1118,7 @@ func (c *Client) sendUpdateSubmissionValidatedModel(ctx context.Context, params
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request. // Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, UpdateSubmissionValidatedModelOperation, ctx, span := c.cfg.Tracer.Start(ctx, UpdateSubmissionModelOperation,
trace.WithAttributes(otelAttrs...), trace.WithAttributes(otelAttrs...),
clientSpanKind, clientSpanKind,
) )
@@ -1073,35 +1155,35 @@ func (c *Client) sendUpdateSubmissionValidatedModel(ctx context.Context, params
} }
pathParts[1] = encoded pathParts[1] = encoded
} }
pathParts[2] = "/validated-model" pathParts[2] = "/model"
uri.AddPathParts(u, pathParts[:]...) uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams" stage = "EncodeQueryParams"
q := uri.NewQueryEncoder() q := uri.NewQueryEncoder()
{ {
// Encode "ValidatedModelID" parameter. // Encode "ModelID" parameter.
cfg := uri.QueryParameterEncodingConfig{ cfg := uri.QueryParameterEncodingConfig{
Name: "ValidatedModelID", Name: "ModelID",
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.Int64ToString(params.ValidatedModelID)) return e.EncodeValue(conv.Int64ToString(params.ModelID))
}); err != nil { }); err != nil {
return res, errors.Wrap(err, "encode query") return res, errors.Wrap(err, "encode query")
} }
} }
{ {
// Encode "ValidatedModelVersion" parameter. // Encode "VersionID" parameter.
cfg := uri.QueryParameterEncodingConfig{ cfg := uri.QueryParameterEncodingConfig{
Name: "ValidatedModelVersion", Name: "VersionID",
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.Int64ToString(params.ValidatedModelVersion)) return e.EncodeValue(conv.Int64ToString(params.VersionID))
}); err != nil { }); err != nil {
return res, errors.Wrap(err, "encode query") return res, errors.Wrap(err, "encode query")
} }
@@ -1122,7 +1204,7 @@ func (c *Client) sendUpdateSubmissionValidatedModel(ctx context.Context, params
defer resp.Body.Close() defer resp.Body.Close()
stage = "DecodeResponse" stage = "DecodeResponse"
result, err := decodeUpdateSubmissionValidatedModelResponse(resp) result, err := decodeUpdateSubmissionModelResponse(resp)
if err != nil { if err != nil {
return res, errors.Wrap(err, "decode response") return res, errors.Wrap(err, "decode response")
} }

View File

@@ -128,10 +128,6 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
Name: "SubmissionID", Name: "SubmissionID",
In: "path", In: "path",
}: params.SubmissionID, }: params.SubmissionID,
{
Name: "StatusMessage",
In: "query",
}: params.StatusMessage,
}, },
Raw: r, Raw: r,
} }
@@ -183,6 +179,155 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
} }
} }
// handleActionSubmissionReleasedRequest handles actionSubmissionReleased operation.
//
// (Internal endpoint) Role Releaser changes status from releasing -> released.
//
// POST /submissions/{SubmissionID}/status/releaser-released
func (s *Server) handleActionSubmissionReleasedRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionSubmissionReleased"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/releaser-released"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionReleasedOperation,
trace.WithAttributes(otelAttrs...),
serverSpanKind,
)
defer span.End()
// Add Labeler to context.
labeler := &Labeler{attrs: otelAttrs}
ctx = contextWithLabeler(ctx, labeler)
// Run stopwatch.
startTime := time.Now()
defer func() {
elapsedDuration := time.Since(startTime)
attrSet := labeler.AttributeSet()
attrs := attrSet.ToSlice()
code := statusWriter.status
if code != 0 {
codeAttr := semconv.HTTPResponseStatusCode(code)
attrs = append(attrs, codeAttr)
span.SetAttributes(codeAttr)
}
attrOpt := metric.WithAttributes(attrs...)
// Increment request counter.
s.requests.Add(ctx, 1, attrOpt)
// Use floating point division here for higher precision (instead of Millisecond method).
s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt)
}()
var (
recordError = func(stage string, err error) {
span.RecordError(err)
// https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status
// Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges,
// unless there was another error (e.g., network error receiving the response body; or 3xx codes with
// max redirects exceeded), in which case status MUST be set to Error.
code := statusWriter.status
if code >= 100 && code < 500 {
span.SetStatus(codes.Error, stage)
}
attrSet := labeler.AttributeSet()
attrs := attrSet.ToSlice()
if code != 0 {
attrs = append(attrs, semconv.HTTPResponseStatusCode(code))
}
s.errors.Add(ctx, 1, metric.WithAttributes(attrs...))
}
err error
opErrContext = ogenerrors.OperationContext{
Name: ActionSubmissionReleasedOperation,
ID: "actionSubmissionReleased",
}
)
params, err := decodeActionSubmissionReleasedParams(args, argsEscaped, r)
if err != nil {
err = &ogenerrors.DecodeParamsError{
OperationContext: opErrContext,
Err: err,
}
defer recordError("DecodeParams", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
var response *ActionSubmissionReleasedNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: ActionSubmissionReleasedOperation,
OperationSummary: "(Internal endpoint) Role Releaser changes status from releasing -> released",
OperationID: "actionSubmissionReleased",
Body: nil,
Params: middleware.Parameters{
{
Name: "SubmissionID",
In: "path",
}: params.SubmissionID,
},
Raw: r,
}
type (
Request = struct{}
Params = ActionSubmissionReleasedParams
Response = *ActionSubmissionReleasedNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackActionSubmissionReleasedParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.ActionSubmissionReleased(ctx, params)
return response, err
},
)
} else {
err = s.h.ActionSubmissionReleased(ctx, params)
}
if err != nil {
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {
if err := encodeErrorResponse(errRes, w, span); err != nil {
defer recordError("Internal", err)
}
return
}
if errors.Is(err, ht.ErrNotImplemented) {
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil {
defer recordError("Internal", err)
}
return
}
if err := encodeActionSubmissionReleasedResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleActionSubmissionUploadedRequest handles actionSubmissionUploaded operation. // handleActionSubmissionUploadedRequest handles actionSubmissionUploaded operation.
// //
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@@ -282,9 +427,9 @@ func (s *Server) handleActionSubmissionUploadedRequest(args [1]string, argsEscap
In: "path", In: "path",
}: params.SubmissionID, }: params.SubmissionID,
{ {
Name: "UploadedAssetID", Name: "TargetAssetID",
In: "query", In: "query",
}: params.UploadedAssetID, }: params.TargetAssetID,
}, },
Raw: r, Raw: r,
} }
@@ -1266,22 +1411,22 @@ func (s *Server) handleListScriptsRequest(args [0]string, argsEscaped bool, w ht
} }
} }
// handleUpdateSubmissionValidatedModelRequest handles updateSubmissionValidatedModel operation. // handleUpdateSubmissionModelRequest handles updateSubmissionModel operation.
// //
// Update validated model. // Update model following role restrictions.
// //
// POST /submissions/{SubmissionID}/validated-model // POST /submissions/{SubmissionID}/model
func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { func (s *Server) handleUpdateSubmissionModelRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w} statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter w = statusWriter
otelAttrs := []attribute.KeyValue{ otelAttrs := []attribute.KeyValue{
otelogen.OperationID("updateSubmissionValidatedModel"), otelogen.OperationID("updateSubmissionModel"),
semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/validated-model"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/model"),
} }
// Start a span for this request. // Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), UpdateSubmissionValidatedModelOperation, ctx, span := s.cfg.Tracer.Start(r.Context(), UpdateSubmissionModelOperation,
trace.WithAttributes(otelAttrs...), trace.WithAttributes(otelAttrs...),
serverSpanKind, serverSpanKind,
) )
@@ -1336,11 +1481,11 @@ func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, arg
} }
err error err error
opErrContext = ogenerrors.OperationContext{ opErrContext = ogenerrors.OperationContext{
Name: UpdateSubmissionValidatedModelOperation, Name: UpdateSubmissionModelOperation,
ID: "updateSubmissionValidatedModel", ID: "updateSubmissionModel",
} }
) )
params, err := decodeUpdateSubmissionValidatedModelParams(args, argsEscaped, r) params, err := decodeUpdateSubmissionModelParams(args, argsEscaped, r)
if err != nil { if err != nil {
err = &ogenerrors.DecodeParamsError{ err = &ogenerrors.DecodeParamsError{
OperationContext: opErrContext, OperationContext: opErrContext,
@@ -1351,13 +1496,13 @@ func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, arg
return return
} }
var response *UpdateSubmissionValidatedModelNoContent var response *UpdateSubmissionModelNoContent
if m := s.cfg.Middleware; m != nil { if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{ mreq := middleware.Request{
Context: ctx, Context: ctx,
OperationName: UpdateSubmissionValidatedModelOperation, OperationName: UpdateSubmissionModelOperation,
OperationSummary: "Update validated model", OperationSummary: "Update model following role restrictions",
OperationID: "updateSubmissionValidatedModel", OperationID: "updateSubmissionModel",
Body: nil, Body: nil,
Params: middleware.Parameters{ Params: middleware.Parameters{
{ {
@@ -1365,21 +1510,21 @@ func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, arg
In: "path", In: "path",
}: params.SubmissionID, }: params.SubmissionID,
{ {
Name: "ValidatedModelID", Name: "ModelID",
In: "query", In: "query",
}: params.ValidatedModelID, }: params.ModelID,
{ {
Name: "ValidatedModelVersion", Name: "VersionID",
In: "query", In: "query",
}: params.ValidatedModelVersion, }: params.VersionID,
}, },
Raw: r, Raw: r,
} }
type ( type (
Request = struct{} Request = struct{}
Params = UpdateSubmissionValidatedModelParams Params = UpdateSubmissionModelParams
Response = *UpdateSubmissionValidatedModelNoContent Response = *UpdateSubmissionModelNoContent
) )
response, err = middleware.HookMiddleware[ response, err = middleware.HookMiddleware[
Request, Request,
@@ -1388,14 +1533,14 @@ func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, arg
]( ](
m, m,
mreq, mreq,
unpackUpdateSubmissionValidatedModelParams, unpackUpdateSubmissionModelParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) { func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.UpdateSubmissionValidatedModel(ctx, params) err = s.h.UpdateSubmissionModel(ctx, params)
return response, err return response, err
}, },
) )
} else { } else {
err = s.h.UpdateSubmissionValidatedModel(ctx, params) err = s.h.UpdateSubmissionModel(ctx, params)
} }
if err != nil { if err != nil {
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok { if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {
@@ -1414,7 +1559,7 @@ func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, arg
return return
} }
if err := encodeUpdateSubmissionValidatedModelResponse(response, w, span); err != nil { if err := encodeUpdateSubmissionModelResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err) defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) { if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err) s.cfg.ErrorHandler(ctx, w, r, err)

View File

@@ -282,22 +282,17 @@ func (s *Script) encodeFields(e *jx.Encoder) {
e.Str(s.Source) e.Str(s.Source)
} }
{ {
e.FieldStart("ResourceType") e.FieldStart("SubmissionID")
e.Int32(s.ResourceType) e.Int64(s.SubmissionID)
}
{
e.FieldStart("ResourceID")
e.Int64(s.ResourceID)
} }
} }
var jsonFieldsNameOfScript = [6]string{ var jsonFieldsNameOfScript = [5]string{
0: "ID", 0: "ID",
1: "Name", 1: "Name",
2: "Hash", 2: "Hash",
3: "Source", 3: "Source",
4: "ResourceType", 4: "SubmissionID",
5: "ResourceID",
} }
// Decode decodes Script from json. // Decode decodes Script from json.
@@ -357,29 +352,17 @@ func (s *Script) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"Source\"") return errors.Wrap(err, "decode field \"Source\"")
} }
case "ResourceType": case "SubmissionID":
requiredBitSet[0] |= 1 << 4 requiredBitSet[0] |= 1 << 4
if err := func() error {
v, err := d.Int32()
s.ResourceType = int32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceType\"")
}
case "ResourceID":
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.SubmissionID = int64(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 \"ResourceID\"") return errors.Wrap(err, "decode field \"SubmissionID\"")
} }
default: default:
return d.Skip() return d.Skip()
@@ -391,7 +374,7 @@ func (s *Script) 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{
0b00111111, 0b00011111,
} { } {
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.
@@ -455,22 +438,17 @@ func (s *ScriptCreate) encodeFields(e *jx.Encoder) {
e.Str(s.Source) e.Str(s.Source)
} }
{ {
e.FieldStart("ResourceType") if s.SubmissionID.Set {
e.Int32(s.ResourceType) e.FieldStart("SubmissionID")
} s.SubmissionID.Encode(e)
{
if s.ResourceID.Set {
e.FieldStart("ResourceID")
s.ResourceID.Encode(e)
} }
} }
} }
var jsonFieldsNameOfScriptCreate = [4]string{ var jsonFieldsNameOfScriptCreate = [3]string{
0: "Name", 0: "Name",
1: "Source", 1: "Source",
2: "ResourceType", 2: "SubmissionID",
3: "ResourceID",
} }
// Decode decodes ScriptCreate from json. // Decode decodes ScriptCreate from json.
@@ -506,27 +484,15 @@ func (s *ScriptCreate) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"Source\"") return errors.Wrap(err, "decode field \"Source\"")
} }
case "ResourceType": case "SubmissionID":
requiredBitSet[0] |= 1 << 2
if err := func() error { if err := func() error {
v, err := d.Int32() s.SubmissionID.Reset()
s.ResourceType = int32(v) if err := s.SubmissionID.Decode(d); err != nil {
if err != nil {
return err return err
} }
return nil return nil
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"ResourceType\"") return errors.Wrap(err, "decode field \"SubmissionID\"")
}
case "ResourceID":
if err := func() error {
s.ResourceID.Reset()
if err := s.ResourceID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
} }
default: default:
return d.Skip() return d.Skip()
@@ -538,7 +504,7 @@ func (s *ScriptCreate) 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{
0b00000111, 0b00000011,
} { } {
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.

View File

@@ -6,13 +6,14 @@ package api
type OperationName = string type OperationName = string
const ( const (
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted" ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionUploadedOperation OperationName = "ActionSubmissionUploaded" ActionSubmissionReleasedOperation OperationName = "ActionSubmissionReleased"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated" ActionSubmissionUploadedOperation OperationName = "ActionSubmissionUploaded"
CreateScriptOperation OperationName = "CreateScript" ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy" CreateScriptOperation OperationName = "CreateScript"
GetScriptOperation OperationName = "GetScript" CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
ListScriptPolicyOperation OperationName = "ListScriptPolicy" GetScriptOperation OperationName = "GetScript"
ListScriptsOperation OperationName = "ListScripts" ListScriptPolicyOperation OperationName = "ListScriptPolicy"
UpdateSubmissionValidatedModelOperation OperationName = "UpdateSubmissionValidatedModel" ListScriptsOperation OperationName = "ListScripts"
UpdateSubmissionModelOperation OperationName = "UpdateSubmissionModel"
) )

View File

@@ -18,8 +18,7 @@ import (
// 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
} }
func unpackActionSubmissionAcceptedParams(packed middleware.Parameters) (params ActionSubmissionAcceptedParams) { func unpackActionSubmissionAcceptedParams(packed middleware.Parameters) (params ActionSubmissionAcceptedParams) {
@@ -30,18 +29,10 @@ func unpackActionSubmissionAcceptedParams(packed middleware.Parameters) (params
} }
params.SubmissionID = packed[key].(int64) params.SubmissionID = packed[key].(int64)
} }
{
key := middleware.ParameterKey{
Name: "StatusMessage",
In: "query",
}
params.StatusMessage = packed[key].(string)
}
return params return params
} }
func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionAcceptedParams, _ error) { func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionAcceptedParams, _ 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]
@@ -87,55 +78,69 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
Err: err, Err: err,
} }
} }
// Decode query: StatusMessage. return params, nil
if err := func() error { }
cfg := uri.QueryParameterDecodingConfig{
Name: "StatusMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil { // ActionSubmissionReleasedParams is parameters of actionSubmissionReleased operation.
if err := q.DecodeParam(cfg, func(d uri.Decoder) error { type ActionSubmissionReleasedParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
func unpackActionSubmissionReleasedParams(packed middleware.Parameters) (params ActionSubmissionReleasedParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
In: "path",
}
params.SubmissionID = packed[key].(int64)
}
return params
}
func decodeActionSubmissionReleasedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionReleasedParams, _ error) {
// Decode path: SubmissionID.
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() val, err := d.DecodeValue()
if err != nil { if err != nil {
return err return err
} }
c, err := conv.ToString(val) c, err := conv.ToInt64(val)
if err != nil { if err != nil {
return err return err
} }
params.StatusMessage = c params.SubmissionID = c
return nil
}); err != nil {
return err
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: true,
MaxLength: 4096,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(params.StatusMessage)); err != nil {
return errors.Wrap(err, "string")
}
return nil return nil
}(); err != nil { }(); err != nil {
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
return params, &ogenerrors.DecodeParamError{ return params, &ogenerrors.DecodeParamError{
Name: "StatusMessage", Name: "SubmissionID",
In: "query", In: "path",
Err: err, Err: err,
} }
} }
@@ -145,8 +150,8 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
// ActionSubmissionUploadedParams is parameters of actionSubmissionUploaded operation. // ActionSubmissionUploadedParams is parameters of actionSubmissionUploaded operation.
type ActionSubmissionUploadedParams struct { type ActionSubmissionUploadedParams struct {
// The unique identifier for a submission. // The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
UploadedAssetID int64 TargetAssetID OptInt64
} }
func unpackActionSubmissionUploadedParams(packed middleware.Parameters) (params ActionSubmissionUploadedParams) { func unpackActionSubmissionUploadedParams(packed middleware.Parameters) (params ActionSubmissionUploadedParams) {
@@ -159,10 +164,12 @@ func unpackActionSubmissionUploadedParams(packed middleware.Parameters) (params
} }
{ {
key := middleware.ParameterKey{ key := middleware.ParameterKey{
Name: "UploadedAssetID", Name: "TargetAssetID",
In: "query", In: "query",
} }
params.UploadedAssetID = packed[key].(int64) if v, ok := packed[key]; ok {
params.TargetAssetID = v.(OptInt64)
}
} }
return params return params
} }
@@ -214,38 +221,43 @@ func decodeActionSubmissionUploadedParams(args [1]string, argsEscaped bool, r *h
Err: err, Err: err,
} }
} }
// Decode query: UploadedAssetID. // Decode query: TargetAssetID.
if err := func() error { if err := func() error {
cfg := uri.QueryParameterDecodingConfig{ cfg := uri.QueryParameterDecodingConfig{
Name: "UploadedAssetID", Name: "TargetAssetID",
Style: uri.QueryStyleForm, Style: uri.QueryStyleForm,
Explode: true, Explode: true,
} }
if err := q.HasParam(cfg); err == nil { if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error { if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
val, err := d.DecodeValue() var paramsDotTargetAssetIDVal int64
if err != nil { if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
paramsDotTargetAssetIDVal = c
return nil
}(); err != nil {
return err return err
} }
params.TargetAssetID.SetTo(paramsDotTargetAssetIDVal)
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.UploadedAssetID = c
return nil return nil
}); err != nil { }); err != nil {
return err return err
} }
} else {
return err
} }
return nil return nil
}(); err != nil { }(); err != nil {
return params, &ogenerrors.DecodeParamError{ return params, &ogenerrors.DecodeParamError{
Name: "UploadedAssetID", Name: "TargetAssetID",
In: "query", In: "query",
Err: err, Err: err,
} }
@@ -484,7 +496,7 @@ func decodeListScriptPolicyParams(args [0]string, argsEscaped bool, r *http.Requ
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
@@ -537,7 +549,7 @@ func decodeListScriptPolicyParams(args [0]string, argsEscaped bool, r *http.Requ
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
@@ -805,7 +817,7 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
@@ -858,7 +870,7 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
@@ -1104,15 +1116,15 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
return params, nil return params, nil
} }
// UpdateSubmissionValidatedModelParams is parameters of updateSubmissionValidatedModel operation. // UpdateSubmissionModelParams is parameters of updateSubmissionModel operation.
type UpdateSubmissionValidatedModelParams struct { type UpdateSubmissionModelParams struct {
// The unique identifier for a submission. // The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
ValidatedModelID int64 ModelID int64
ValidatedModelVersion int64 VersionID int64
} }
func unpackUpdateSubmissionValidatedModelParams(packed middleware.Parameters) (params UpdateSubmissionValidatedModelParams) { func unpackUpdateSubmissionModelParams(packed middleware.Parameters) (params UpdateSubmissionModelParams) {
{ {
key := middleware.ParameterKey{ key := middleware.ParameterKey{
Name: "SubmissionID", Name: "SubmissionID",
@@ -1122,22 +1134,22 @@ func unpackUpdateSubmissionValidatedModelParams(packed middleware.Parameters) (p
} }
{ {
key := middleware.ParameterKey{ key := middleware.ParameterKey{
Name: "ValidatedModelID", Name: "ModelID",
In: "query", In: "query",
} }
params.ValidatedModelID = packed[key].(int64) params.ModelID = packed[key].(int64)
} }
{ {
key := middleware.ParameterKey{ key := middleware.ParameterKey{
Name: "ValidatedModelVersion", Name: "VersionID",
In: "query", In: "query",
} }
params.ValidatedModelVersion = packed[key].(int64) params.VersionID = packed[key].(int64)
} }
return params return params
} }
func decodeUpdateSubmissionValidatedModelParams(args [1]string, argsEscaped bool, r *http.Request) (params UpdateSubmissionValidatedModelParams, _ error) { func decodeUpdateSubmissionModelParams(args [1]string, argsEscaped bool, r *http.Request) (params UpdateSubmissionModelParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query()) q := uri.NewQueryDecoder(r.URL.Query())
// Decode path: SubmissionID. // Decode path: SubmissionID.
if err := func() error { if err := func() error {
@@ -1184,10 +1196,10 @@ func decodeUpdateSubmissionValidatedModelParams(args [1]string, argsEscaped bool
Err: err, Err: err,
} }
} }
// Decode query: ValidatedModelID. // Decode query: ModelID.
if err := func() error { if err := func() error {
cfg := uri.QueryParameterDecodingConfig{ cfg := uri.QueryParameterDecodingConfig{
Name: "ValidatedModelID", Name: "ModelID",
Style: uri.QueryStyleForm, Style: uri.QueryStyleForm,
Explode: true, Explode: true,
} }
@@ -1204,26 +1216,26 @@ func decodeUpdateSubmissionValidatedModelParams(args [1]string, argsEscaped bool
return err return err
} }
params.ValidatedModelID = c params.ModelID = c
return nil return nil
}); err != nil { }); err != nil {
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
return params, &ogenerrors.DecodeParamError{ return params, &ogenerrors.DecodeParamError{
Name: "ValidatedModelID", Name: "ModelID",
In: "query", In: "query",
Err: err, Err: err,
} }
} }
// Decode query: ValidatedModelVersion. // Decode query: VersionID.
if err := func() error { if err := func() error {
cfg := uri.QueryParameterDecodingConfig{ cfg := uri.QueryParameterDecodingConfig{
Name: "ValidatedModelVersion", Name: "VersionID",
Style: uri.QueryStyleForm, Style: uri.QueryStyleForm,
Explode: true, Explode: true,
} }
@@ -1240,18 +1252,18 @@ func decodeUpdateSubmissionValidatedModelParams(args [1]string, argsEscaped bool
return err return err
} }
params.ValidatedModelVersion = c params.VersionID = c
return nil return nil
}); err != nil { }); err != nil {
return err return err
} }
} else { } else {
return err return validate.ErrFieldRequired
} }
return nil return nil
}(); err != nil { }(); err != nil {
return params, &ogenerrors.DecodeParamError{ return params, &ogenerrors.DecodeParamError{
Name: "ValidatedModelVersion", Name: "VersionID",
In: "query", In: "query",
Err: err, Err: err,
} }

View File

@@ -66,6 +66,57 @@ func decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSub
return res, errors.Wrap(defRes, "error") return res, errors.Wrap(defRes, "error")
} }
func decodeActionSubmissionReleasedResponse(resp *http.Response) (res *ActionSubmissionReleasedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionReleasedNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
}
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionUploadedResponse(resp *http.Response) (res *ActionSubmissionUploadedNoContent, _ error) { func decodeActionSubmissionUploadedResponse(resp *http.Response) (res *ActionSubmissionUploadedNoContent, _ error) {
switch resp.StatusCode { switch resp.StatusCode {
case 204: case 204:
@@ -660,11 +711,11 @@ func decodeListScriptsResponse(resp *http.Response) (res []Script, _ error) {
return res, errors.Wrap(defRes, "error") return res, errors.Wrap(defRes, "error")
} }
func decodeUpdateSubmissionValidatedModelResponse(resp *http.Response) (res *UpdateSubmissionValidatedModelNoContent, _ error) { func decodeUpdateSubmissionModelResponse(resp *http.Response) (res *UpdateSubmissionModelNoContent, _ error) {
switch resp.StatusCode { switch resp.StatusCode {
case 204: case 204:
// Code 204. // Code 204.
return &UpdateSubmissionValidatedModelNoContent{}, nil return &UpdateSubmissionModelNoContent{}, nil
} }
// Convenient error response. // Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) { defRes, err := func() (res *ErrorStatusCode, err error) {

View File

@@ -20,6 +20,13 @@ func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNo
return nil return nil
} }
func encodeActionSubmissionReleasedResponse(response *ActionSubmissionReleasedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionUploadedResponse(response *ActionSubmissionUploadedNoContent, w http.ResponseWriter, span trace.Span) error { func encodeActionSubmissionUploadedResponse(response *ActionSubmissionUploadedNoContent, 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))
@@ -112,7 +119,7 @@ func encodeListScriptsResponse(response []Script, w http.ResponseWriter, span tr
return nil return nil
} }
func encodeUpdateSubmissionValidatedModelResponse(response *UpdateSubmissionValidatedModelNoContent, w http.ResponseWriter, span trace.Span) error { func encodeUpdateSubmissionModelResponse(response *UpdateSubmissionModelNoContent, 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))

View File

@@ -50,7 +50,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
switch elem[0] { switch elem[0] {
case '/': // Prefix: "/s" case '/': // Prefix: "/s"
origElem := elem
if l := len("/s"); len(elem) >= l && elem[0:l] == "/s" { if l := len("/s"); len(elem) >= l && elem[0:l] == "/s" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -62,7 +62,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
switch elem[0] { switch elem[0] {
case 'c': // Prefix: "cript" case 'c': // Prefix: "cript"
origElem := elem
if l := len("cript"); len(elem) >= l && elem[0:l] == "cript" { if l := len("cript"); len(elem) >= l && elem[0:l] == "cript" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -74,7 +74,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
switch elem[0] { switch elem[0] {
case '-': // Prefix: "-policy" case '-': // Prefix: "-policy"
origElem := elem
if l := len("-policy"); len(elem) >= l && elem[0:l] == "-policy" { if l := len("-policy"); len(elem) >= l && elem[0:l] == "-policy" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -95,8 +95,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
elem = origElem
case 's': // Prefix: "s" case 's': // Prefix: "s"
origElem := elem
if l := len("s"); len(elem) >= l && elem[0:l] == "s" { if l := len("s"); len(elem) >= l && elem[0:l] == "s" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -117,7 +118,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
switch elem[0] { switch elem[0] {
case '/': // Prefix: "/" case '/': // Prefix: "/"
origElem := elem
if l := len("/"); len(elem) >= l && elem[0:l] == "/" { if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -125,11 +126,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
// Param: "ScriptID" // Param: "ScriptID"
// Leaf parameter, slashes are prohibited // Leaf parameter
idx := strings.IndexByte(elem, '/')
if idx >= 0 {
break
}
args[0] = elem args[0] = elem
elem = "" elem = ""
@@ -147,12 +144,15 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
elem = origElem
} }
elem = origElem
} }
elem = origElem
case 'u': // Prefix: "ubmissions/" case 'u': // Prefix: "ubmissions/"
origElem := elem
if l := len("ubmissions/"); len(elem) >= l && elem[0:l] == "ubmissions/" { if l := len("ubmissions/"); len(elem) >= l && elem[0:l] == "ubmissions/" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -173,7 +173,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
switch elem[0] { switch elem[0] {
case '/': // Prefix: "/" case '/': // Prefix: "/"
origElem := elem
if l := len("/"); len(elem) >= l && elem[0:l] == "/" { if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -184,89 +184,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break break
} }
switch elem[0] { switch elem[0] {
case 's': // Prefix: "status/validator-" case 'm': // Prefix: "model"
origElem := elem
if l := len("status/validator-"); len(elem) >= l && elem[0:l] == "status/validator-" { if l := len("model"); len(elem) >= l && elem[0:l] == "model" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'f': // Prefix: "failed"
if l := len("failed"); len(elem) >= l && elem[0:l] == "failed" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionAcceptedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'u': // Prefix: "uploaded"
if l := len("uploaded"); len(elem) >= l && elem[0:l] == "uploaded" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionUploadedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 'v': // Prefix: "validated"
if l := len("validated"); len(elem) >= l && elem[0:l] == "validated" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionValidatedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
}
case 'v': // Prefix: "validated-model"
if l := len("validated-model"); len(elem) >= l && elem[0:l] == "validated-model" {
elem = elem[l:] elem = elem[l:]
} else { } else {
break break
@@ -276,7 +196,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Leaf node. // Leaf node.
switch r.Method { switch r.Method {
case "POST": case "POST":
s.handleUpdateSubmissionValidatedModelRequest([1]string{ s.handleUpdateSubmissionModelRequest([1]string{
args[0], args[0],
}, elemIsEscaped, w, r) }, elemIsEscaped, w, r)
default: default:
@@ -286,12 +206,138 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
elem = origElem
case 's': // Prefix: "status/"
origElem := elem
if l := len("status/"); len(elem) >= l && elem[0:l] == "status/" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'r': // Prefix: "releaser-released"
origElem := elem
if l := len("releaser-released"); len(elem) >= l && elem[0:l] == "releaser-released" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionReleasedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
elem = origElem
case 'v': // Prefix: "validator-"
origElem := elem
if l := len("validator-"); len(elem) >= l && elem[0:l] == "validator-" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'f': // Prefix: "failed"
origElem := elem
if l := len("failed"); len(elem) >= l && elem[0:l] == "failed" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionAcceptedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
elem = origElem
case 'u': // Prefix: "uploaded"
origElem := elem
if l := len("uploaded"); len(elem) >= l && elem[0:l] == "uploaded" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionUploadedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
elem = origElem
case 'v': // Prefix: "validated"
origElem := elem
if l := len("validated"); len(elem) >= l && elem[0:l] == "validated" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionValidatedRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
elem = origElem
}
elem = origElem
}
elem = origElem
} }
elem = origElem
} }
elem = origElem
} }
elem = origElem
} }
} }
s.notFound(w, r) s.notFound(w, r)
@@ -373,7 +419,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
} }
switch elem[0] { switch elem[0] {
case '/': // Prefix: "/s" case '/': // Prefix: "/s"
origElem := elem
if l := len("/s"); len(elem) >= l && elem[0:l] == "/s" { if l := len("/s"); len(elem) >= l && elem[0:l] == "/s" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -385,7 +431,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
} }
switch elem[0] { switch elem[0] {
case 'c': // Prefix: "cript" case 'c': // Prefix: "cript"
origElem := elem
if l := len("cript"); len(elem) >= l && elem[0:l] == "cript" { if l := len("cript"); len(elem) >= l && elem[0:l] == "cript" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -397,7 +443,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
} }
switch elem[0] { switch elem[0] {
case '-': // Prefix: "-policy" case '-': // Prefix: "-policy"
origElem := elem
if l := len("-policy"); len(elem) >= l && elem[0:l] == "-policy" { if l := len("-policy"); len(elem) >= l && elem[0:l] == "-policy" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -428,8 +474,9 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
} }
} }
elem = origElem
case 's': // Prefix: "s" case 's': // Prefix: "s"
origElem := elem
if l := len("s"); len(elem) >= l && elem[0:l] == "s" { if l := len("s"); len(elem) >= l && elem[0:l] == "s" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -460,7 +507,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
} }
switch elem[0] { switch elem[0] {
case '/': // Prefix: "/" case '/': // Prefix: "/"
origElem := elem
if l := len("/"); len(elem) >= l && elem[0:l] == "/" { if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -468,11 +515,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
} }
// Param: "ScriptID" // Param: "ScriptID"
// Leaf parameter, slashes are prohibited // Leaf parameter
idx := strings.IndexByte(elem, '/')
if idx >= 0 {
break
}
args[0] = elem args[0] = elem
elem = "" elem = ""
@@ -492,12 +535,15 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
} }
} }
elem = origElem
} }
elem = origElem
} }
elem = origElem
case 'u': // Prefix: "ubmissions/" case 'u': // Prefix: "ubmissions/"
origElem := elem
if l := len("ubmissions/"); len(elem) >= l && elem[0:l] == "ubmissions/" { if l := len("ubmissions/"); len(elem) >= l && elem[0:l] == "ubmissions/" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -518,7 +564,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
} }
switch elem[0] { switch elem[0] {
case '/': // Prefix: "/" case '/': // Prefix: "/"
origElem := elem
if l := len("/"); len(elem) >= l && elem[0:l] == "/" { if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:] elem = elem[l:]
} else { } else {
@@ -529,95 +575,9 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break break
} }
switch elem[0] { switch elem[0] {
case 's': // Prefix: "status/validator-" case 'm': // Prefix: "model"
origElem := elem
if l := len("status/validator-"); len(elem) >= l && elem[0:l] == "status/validator-" { if l := len("model"); len(elem) >= l && elem[0:l] == "model" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'f': // Prefix: "failed"
if l := len("failed"); len(elem) >= l && elem[0:l] == "failed" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionAcceptedOperation
r.summary = "(Internal endpoint) Role Validator changes status from Validating -> Accepted"
r.operationID = "actionSubmissionAccepted"
r.pathPattern = "/submissions/{SubmissionID}/status/validator-failed"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'u': // Prefix: "uploaded"
if l := len("uploaded"); len(elem) >= l && elem[0:l] == "uploaded" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionUploadedOperation
r.summary = "(Internal endpoint) Role Validator changes status from Uploading -> Uploaded"
r.operationID = "actionSubmissionUploaded"
r.pathPattern = "/submissions/{SubmissionID}/status/validator-uploaded"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 'v': // Prefix: "validated"
if l := len("validated"); len(elem) >= l && elem[0:l] == "validated" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionValidatedOperation
r.summary = "(Internal endpoint) Role Validator changes status from Validating -> Validated"
r.operationID = "actionSubmissionValidated"
r.pathPattern = "/submissions/{SubmissionID}/status/validator-validated"
r.args = args
r.count = 1
return r, true
default:
return
}
}
}
case 'v': // Prefix: "validated-model"
if l := len("validated-model"); len(elem) >= l && elem[0:l] == "validated-model" {
elem = elem[l:] elem = elem[l:]
} else { } else {
break break
@@ -627,10 +587,10 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
// Leaf node. // Leaf node.
switch method { switch method {
case "POST": case "POST":
r.name = UpdateSubmissionValidatedModelOperation r.name = UpdateSubmissionModelOperation
r.summary = "Update validated model" r.summary = "Update model following role restrictions"
r.operationID = "updateSubmissionValidatedModel" r.operationID = "updateSubmissionModel"
r.pathPattern = "/submissions/{SubmissionID}/validated-model" r.pathPattern = "/submissions/{SubmissionID}/model"
r.args = args r.args = args
r.count = 1 r.count = 1
return r, true return r, true
@@ -639,12 +599,146 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
} }
} }
elem = origElem
case 's': // Prefix: "status/"
origElem := elem
if l := len("status/"); len(elem) >= l && elem[0:l] == "status/" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'r': // Prefix: "releaser-released"
origElem := elem
if l := len("releaser-released"); len(elem) >= l && elem[0:l] == "releaser-released" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionReleasedOperation
r.summary = "(Internal endpoint) Role Releaser changes status from releasing -> released"
r.operationID = "actionSubmissionReleased"
r.pathPattern = "/submissions/{SubmissionID}/status/releaser-released"
r.args = args
r.count = 1
return r, true
default:
return
}
}
elem = origElem
case 'v': // Prefix: "validator-"
origElem := elem
if l := len("validator-"); len(elem) >= l && elem[0:l] == "validator-" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'f': // Prefix: "failed"
origElem := elem
if l := len("failed"); len(elem) >= l && elem[0:l] == "failed" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionAcceptedOperation
r.summary = "(Internal endpoint) Role Validator changes status from Validating -> Accepted"
r.operationID = "actionSubmissionAccepted"
r.pathPattern = "/submissions/{SubmissionID}/status/validator-failed"
r.args = args
r.count = 1
return r, true
default:
return
}
}
elem = origElem
case 'u': // Prefix: "uploaded"
origElem := elem
if l := len("uploaded"); len(elem) >= l && elem[0:l] == "uploaded" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionUploadedOperation
r.summary = "(Internal endpoint) Role Validator changes status from Uploading -> Uploaded"
r.operationID = "actionSubmissionUploaded"
r.pathPattern = "/submissions/{SubmissionID}/status/validator-uploaded"
r.args = args
r.count = 1
return r, true
default:
return
}
}
elem = origElem
case 'v': // Prefix: "validated"
origElem := elem
if l := len("validated"); len(elem) >= l && elem[0:l] == "validated" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionValidatedOperation
r.summary = "(Internal endpoint) Role Validator changes status from Validating -> Validated"
r.operationID = "actionSubmissionValidated"
r.pathPattern = "/submissions/{SubmissionID}/status/validator-validated"
r.args = args
r.count = 1
return r, true
default:
return
}
}
elem = origElem
}
elem = origElem
}
elem = origElem
} }
elem = origElem
} }
elem = origElem
} }
elem = origElem
} }
} }
return r, false return r, false

View File

@@ -13,6 +13,9 @@ func (s *ErrorStatusCode) Error() string {
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation. // ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
type ActionSubmissionAcceptedNoContent struct{} type ActionSubmissionAcceptedNoContent struct{}
// ActionSubmissionReleasedNoContent is response for ActionSubmissionReleased operation.
type ActionSubmissionReleasedNoContent struct{}
// ActionSubmissionUploadedNoContent is response for ActionSubmissionUploaded operation. // ActionSubmissionUploadedNoContent is response for ActionSubmissionUploaded operation.
type ActionSubmissionUploadedNoContent struct{} type ActionSubmissionUploadedNoContent struct{}
@@ -231,8 +234,7 @@ type Script struct {
Name string `json:"Name"` Name string `json:"Name"`
Hash string `json:"Hash"` Hash string `json:"Hash"`
Source string `json:"Source"` Source string `json:"Source"`
ResourceType int32 `json:"ResourceType"` SubmissionID int64 `json:"SubmissionID"`
ResourceID int64 `json:"ResourceID"`
} }
// GetID returns the value of ID. // GetID returns the value of ID.
@@ -255,14 +257,9 @@ func (s *Script) GetSource() string {
return s.Source return s.Source
} }
// GetResourceType returns the value of ResourceType. // GetSubmissionID returns the value of SubmissionID.
func (s *Script) GetResourceType() int32 { func (s *Script) GetSubmissionID() int64 {
return s.ResourceType return s.SubmissionID
}
// GetResourceID returns the value of ResourceID.
func (s *Script) GetResourceID() int64 {
return s.ResourceID
} }
// SetID sets the value of ID. // SetID sets the value of ID.
@@ -285,22 +282,16 @@ func (s *Script) SetSource(val string) {
s.Source = val s.Source = val
} }
// SetResourceType sets the value of ResourceType. // SetSubmissionID sets the value of SubmissionID.
func (s *Script) SetResourceType(val int32) { func (s *Script) SetSubmissionID(val int64) {
s.ResourceType = val s.SubmissionID = val
}
// SetResourceID sets the value of ResourceID.
func (s *Script) SetResourceID(val int64) {
s.ResourceID = val
} }
// Ref: #/components/schemas/ScriptCreate // Ref: #/components/schemas/ScriptCreate
type ScriptCreate struct { type ScriptCreate struct {
Name string `json:"Name"` Name string `json:"Name"`
Source string `json:"Source"` Source string `json:"Source"`
ResourceType int32 `json:"ResourceType"` SubmissionID OptInt64 `json:"SubmissionID"`
ResourceID OptInt64 `json:"ResourceID"`
} }
// GetName returns the value of Name. // GetName returns the value of Name.
@@ -313,14 +304,9 @@ func (s *ScriptCreate) GetSource() string {
return s.Source return s.Source
} }
// GetResourceType returns the value of ResourceType. // GetSubmissionID returns the value of SubmissionID.
func (s *ScriptCreate) GetResourceType() int32 { func (s *ScriptCreate) GetSubmissionID() OptInt64 {
return s.ResourceType return s.SubmissionID
}
// GetResourceID returns the value of ResourceID.
func (s *ScriptCreate) GetResourceID() OptInt64 {
return s.ResourceID
} }
// SetName sets the value of Name. // SetName sets the value of Name.
@@ -333,14 +319,9 @@ func (s *ScriptCreate) SetSource(val string) {
s.Source = val s.Source = val
} }
// SetResourceType sets the value of ResourceType. // SetSubmissionID sets the value of SubmissionID.
func (s *ScriptCreate) SetResourceType(val int32) { func (s *ScriptCreate) SetSubmissionID(val OptInt64) {
s.ResourceType = val s.SubmissionID = val
}
// SetResourceID sets the value of ResourceID.
func (s *ScriptCreate) SetResourceID(val OptInt64) {
s.ResourceID = val
} }
// Ref: #/components/schemas/ScriptPolicy // Ref: #/components/schemas/ScriptPolicy
@@ -428,5 +409,5 @@ func (s *ScriptPolicyCreate) SetPolicy(val int32) {
s.Policy = val s.Policy = val
} }
// UpdateSubmissionValidatedModelNoContent is response for UpdateSubmissionValidatedModel operation. // UpdateSubmissionModelNoContent is response for UpdateSubmissionModel operation.
type UpdateSubmissionValidatedModelNoContent struct{} type UpdateSubmissionModelNoContent struct{}

View File

@@ -14,6 +14,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
// ActionSubmissionReleased implements actionSubmissionReleased operation.
//
// (Internal endpoint) Role Releaser changes status from releasing -> released.
//
// POST /submissions/{SubmissionID}/status/releaser-released
ActionSubmissionReleased(ctx context.Context, params ActionSubmissionReleasedParams) error
// ActionSubmissionUploaded implements actionSubmissionUploaded operation. // ActionSubmissionUploaded implements actionSubmissionUploaded operation.
// //
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@@ -56,12 +62,12 @@ type Handler interface {
// //
// GET /scripts // GET /scripts
ListScripts(ctx context.Context, params ListScriptsParams) ([]Script, error) ListScripts(ctx context.Context, params ListScriptsParams) ([]Script, error)
// UpdateSubmissionValidatedModel implements updateSubmissionValidatedModel operation. // UpdateSubmissionModel implements updateSubmissionModel operation.
// //
// Update validated model. // Update model following role restrictions.
// //
// POST /submissions/{SubmissionID}/validated-model // POST /submissions/{SubmissionID}/model
UpdateSubmissionValidatedModel(ctx context.Context, params UpdateSubmissionValidatedModelParams) error UpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) error
// NewError creates *ErrorStatusCode from error returned by handler. // NewError creates *ErrorStatusCode from error returned by handler.
// //
// Used for common default response. // Used for common default response.

View File

@@ -22,6 +22,15 @@ func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }
// ActionSubmissionReleased implements actionSubmissionReleased operation.
//
// (Internal endpoint) Role Releaser changes status from releasing -> released.
//
// POST /submissions/{SubmissionID}/status/releaser-released
func (UnimplementedHandler) ActionSubmissionReleased(ctx context.Context, params ActionSubmissionReleasedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionUploaded implements actionSubmissionUploaded operation. // ActionSubmissionUploaded implements actionSubmissionUploaded operation.
// //
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@@ -85,12 +94,12 @@ func (UnimplementedHandler) ListScripts(ctx context.Context, params ListScriptsP
return r, ht.ErrNotImplemented return r, ht.ErrNotImplemented
} }
// UpdateSubmissionValidatedModel implements updateSubmissionValidatedModel operation. // UpdateSubmissionModel implements updateSubmissionModel operation.
// //
// Update validated model. // Update model following role restrictions.
// //
// POST /submissions/{SubmissionID}/validated-model // POST /submissions/{SubmissionID}/model
func (UnimplementedHandler) UpdateSubmissionValidatedModel(ctx context.Context, params UpdateSubmissionValidatedModelParams) error { func (UnimplementedHandler) UpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) error {
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }

View File

@@ -10,18 +10,18 @@ type ValidateRequest struct {
SubmissionID int64 SubmissionID int64
ModelID int64 ModelID int64
ModelVersion int64 ModelVersion int64
ValidatedModelID *int64 // optional value ValidatedModelID int64 // optional value
} }
// Create a new map // Create a new map
type UploadNewRequest struct { type PublishNewRequest struct {
SubmissionID int64 SubmissionID int64
ModelID int64 ModelID int64
ModelVersion int64 ModelVersion int64
ModelName string ModelName string
} }
type UploadFixRequest struct { type PublishFixRequest struct {
SubmissionID int64 SubmissionID int64
ModelID int64 ModelID int64
ModelVersion int64 ModelVersion int64

View File

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

View File

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

View File

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

View File

@@ -14,16 +14,12 @@ import (
// //
// POST /scripts // POST /scripts
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ID, error) { func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return nil, ErrUserInfo return nil, ErrUserInfo
} }
has_role, err := userInfo.HasRoleScriptWrite() if !userInfo.Roles.ScriptWrite {
if err != nil {
return nil, err
}
if !has_role {
return nil, ErrPermissionDenied return nil, ErrPermissionDenied
} }
@@ -32,8 +28,7 @@ func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*a
Name: req.Name, Name: req.Name,
Hash: int64(model.HashSource(req.Source)), Hash: int64(model.HashSource(req.Source)),
Source: req.Source, Source: req.Source,
ResourceType: model.ResourceType(req.ResourceType), SubmissionID: req.SubmissionID.Or(0),
ResourceID: req.ResourceID.Or(0),
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@@ -78,13 +73,12 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
} }
var resp []api.Script var resp []api.Script
for _, item := range items { for i := 0; i < len(items); i++ {
resp = append(resp, api.Script{ resp = append(resp, api.Script{
ID: item.ID, ID: items[i].ID,
Hash: model.HashFormat(uint64(item.Hash)), Hash: model.HashFormat(uint64(items[i].Hash)),
Source: item.Source, Source: items[i].Source,
ResourceType: int32(item.ResourceType), SubmissionID: items[i].SubmissionID,
ResourceID: item.ResourceID,
}) })
} }
@@ -97,16 +91,12 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
// //
// DELETE /scripts/{ScriptID} // DELETE /scripts/{ScriptID}
func (svc *Service) DeleteScript(ctx context.Context, params api.DeleteScriptParams) error { func (svc *Service) DeleteScript(ctx context.Context, params api.DeleteScriptParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return ErrUserInfo return ErrUserInfo
} }
has_role, err := userInfo.HasRoleScriptWrite() if !userInfo.Roles.ScriptWrite {
if err != nil {
return err
}
if !has_role {
return ErrPermissionDenied return ErrPermissionDenied
} }
@@ -119,7 +109,7 @@ func (svc *Service) DeleteScript(ctx context.Context, params api.DeleteScriptPar
// //
// GET /scripts/{ScriptID} // GET /scripts/{ScriptID}
func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (*api.Script, error) { func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (*api.Script, error) {
_, ok := ctx.Value("UserInfo").(UserInfoHandle) _, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return nil, ErrUserInfo return nil, ErrUserInfo
} }
@@ -136,8 +126,7 @@ func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (
Name: script.Name, Name: script.Name,
Hash: model.HashFormat(uint64(script.Hash)), Hash: model.HashFormat(uint64(script.Hash)),
Source: script.Source, Source: script.Source,
ResourceType: int32(script.ResourceType), SubmissionID: script.SubmissionID,
ResourceID: script.ResourceID,
}, nil }, nil
} }
@@ -147,16 +136,12 @@ func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (
// //
// PATCH /scripts/{ScriptID} // PATCH /scripts/{ScriptID}
func (svc *Service) UpdateScript(ctx context.Context, req *api.ScriptUpdate, params api.UpdateScriptParams) error { func (svc *Service) UpdateScript(ctx context.Context, req *api.ScriptUpdate, params api.UpdateScriptParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return ErrUserInfo return ErrUserInfo
} }
has_role, err := userInfo.HasRoleScriptWrite() if !userInfo.Roles.ScriptWrite {
if err != nil {
return err
}
if !has_role {
return ErrPermissionDenied return ErrPermissionDenied
} }
@@ -168,11 +153,8 @@ func (svc *Service) UpdateScript(ctx context.Context, req *api.ScriptUpdate, par
pmap.Add("source", source) pmap.Add("source", source)
pmap.Add("hash", int64(model.HashSource(source))) // No type safety! pmap.Add("hash", int64(model.HashSource(source))) // No type safety!
} }
if ResourceType, ok := req.ResourceType.Get(); ok { if SubmissionID, ok := req.SubmissionID.Get(); ok {
pmap.Add("resource_type", ResourceType) pmap.Add("submission_id", SubmissionID)
}
if ResourceID, ok := req.ResourceID.Get(); ok {
pmap.Add("resource_id", ResourceID)
} }
return svc.DB.Scripts().Update(ctx, req.ID, pmap) return svc.DB.Scripts().Update(ctx, req.ID, pmap)
} }

View File

@@ -14,145 +14,34 @@ var (
ErrInvalidSession = errors.New("Session invalid") ErrInvalidSession = errors.New("Session invalid")
) )
// Submissions roles bitflag
type Roles int32
var (
RolesSubmissionRelease Roles = 1<<4
RolesScriptWrite Roles = 1<<3
RolesMapUpload Roles = 1<<2
RolesMapReview Roles = 1<<1
RolesMapDownload Roles = 1<<0
RolesEmpty Roles = 0
)
// StrafesNET group roles
type GroupRole int32
var ( var (
// has ScriptWrite // has ScriptWrite
RoleQuat GroupRole = 255 RoleQuat int32 = 255
RoleItzaname GroupRole = 254 // has SubmissionPublish
RoleStagingDeveloper GroupRole = 240 RoleMapAdmin int32 = 128
RolesAll Roles = RolesScriptWrite|RolesSubmissionRelease|RolesMapUpload|RolesMapReview|RolesMapDownload
// has SubmissionUpload
RoleMapAdmin GroupRole = 128
RolesMapAdmin Roles = RolesSubmissionRelease|RolesMapUpload|RolesMapReview|RolesMapDownload
// has SubmissionReview // has SubmissionReview
RoleMapCouncil GroupRole = 64 RoleMapCouncil int32 = 64
RolesMapCouncil Roles = RolesMapReview|RolesMapUpload|RolesMapDownload
// access to downloading maps
RoleMapAccess GroupRole = 32
RolesMapAccess Roles = RolesMapDownload
) )
type UserInfoHandle struct { type Roles struct {
// Would love to know a better way to do this // human roles
svc *SecurityHandler SubmissionRelease bool
ctx *context.Context SubmissionReview bool
sessionId string ScriptWrite bool
// Thumbnail bool
// MapDownload
// automated roles
Maptest bool
} }
type UserInfo struct { type UserInfo struct {
UserID uint64 Roles Roles
Username string UserID uint64
AvatarURL string
} }
func (usr UserInfoHandle) GetUserInfo() (userInfo UserInfo, err error) { func (usr UserInfo) IsSubmitter(submitter uint64) bool {
session, err := usr.svc.Client.GetSessionUser(*usr.ctx, &auth.IdMessage{ return usr.UserID == submitter
SessionID: usr.sessionId,
})
if err != nil {
return userInfo, err
}
userInfo.UserID = session.UserID
userInfo.Username = session.Username
userInfo.AvatarURL = session.AvatarURL
return userInfo, nil
}
func (usr UserInfoHandle) GetUserID() (uint64, error) {
session, err := usr.svc.Client.GetSessionUser(*usr.ctx, &auth.IdMessage{
SessionID: usr.sessionId,
})
if err != nil {
return 0, err
}
return session.UserID, nil
}
func (usr UserInfoHandle) Validate() (bool, error) {
validate, err := usr.svc.Client.ValidateSession(*usr.ctx, &auth.IdMessage{
SessionID: usr.sessionId,
})
if err != nil {
return false, err
}
return validate.Valid, nil
}
func (usr UserInfoHandle) IsSubmitter(submitter uint64) (bool, error) {
userId, err := usr.GetUserID()
if err != nil {
return false, err
}
return userId == submitter, nil
}
func (usr UserInfoHandle) hasRoles(wantRoles Roles) (bool, error) {
haveroles, err := usr.GetRoles()
if err != nil {
return false, err
}
return haveroles & wantRoles == wantRoles, nil
}
func (usr UserInfoHandle) GetRoles() (Roles, error) {
roles, err := usr.svc.Client.GetGroupRole(*usr.ctx, &auth.IdMessage{
SessionID: usr.sessionId,
})
if err != nil {
return RolesEmpty, err
}
// map roles into bitflag
rolesBitflag := RolesEmpty;
for _, r := range roles.Roles {
switch GroupRole(r.Rank){
case RoleQuat, RoleItzaname, RoleStagingDeveloper:
rolesBitflag|=RolesAll
case RoleMapAdmin:
rolesBitflag|=RolesMapAdmin
case RoleMapCouncil:
rolesBitflag|=RolesMapCouncil
case RoleMapAccess:
rolesBitflag|=RolesMapAccess
}
}
return rolesBitflag, nil
}
// RoleThumbnail
func (usr UserInfoHandle) HasRoleMapfixUpload() (bool, error) {
return usr.hasRoles(RolesMapUpload)
}
func (usr UserInfoHandle) HasRoleMapfixReview() (bool, error) {
return usr.hasRoles(RolesMapReview)
}
func (usr UserInfoHandle) HasRoleMapDownload() (bool, error) {
return usr.hasRoles(RolesMapDownload)
}
func (usr UserInfoHandle) HasRoleSubmissionRelease() (bool, error) {
return usr.hasRoles(RolesSubmissionRelease)
}
func (usr UserInfoHandle) HasRoleSubmissionUpload() (bool, error) {
return usr.hasRoles(RolesMapUpload)
}
func (usr UserInfoHandle) HasRoleSubmissionReview() (bool, error) {
return usr.hasRoles(RolesMapReview)
}
func (usr UserInfoHandle) HasRoleScriptWrite() (bool, error) {
return usr.hasRoles(RolesScriptWrite)
}
/// Not implemented
func (usr UserInfoHandle) HasRoleMaptest() (bool, error) {
println("HasRoleMaptest is not implemented!")
return false, nil
} }
type SecurityHandler struct { type SecurityHandler struct {
@@ -165,10 +54,48 @@ func (svc SecurityHandler) HandleCookieAuth(ctx context.Context, operationName a
return nil, ErrMissingSessionID return nil, ErrMissingSessionID
} }
newCtx := context.WithValue(ctx, "UserInfo", UserInfoHandle{ session, err := svc.Client.GetSessionUser(ctx, &auth.IdMessage{
svc: &svc, SessionID: sessionId,
ctx: &ctx, })
sessionId: sessionId, if err != nil {
return nil, err
}
role, err := svc.Client.GetGroupRole(ctx, &auth.IdMessage{
SessionID: sessionId,
})
if err != nil {
return nil, err
}
validate, err := svc.Client.ValidateSession(ctx, &auth.IdMessage{
SessionID: sessionId,
})
if err != nil {
return nil, err
}
if !validate.Valid {
return nil, ErrInvalidSession
}
roles := Roles{}
// fix this when roblox udpates group roles
for _, r := range role.Roles {
if RoleQuat <= r.Rank {
roles.ScriptWrite = true
}
if RoleMapAdmin <= r.Rank {
roles.SubmissionRelease = true
}
if RoleMapCouncil <= r.Rank {
roles.SubmissionReview = true
}
}
newCtx := context.WithValue(ctx, "UserInfo", UserInfo{
Roles: roles,
UserID: session.UserID,
}) })
return newCtx, nil return newCtx, nil

View File

@@ -3,9 +3,6 @@ package service
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"git.itzana.me/strafesnet/go-grpc/maps"
"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"
@@ -16,20 +13,11 @@ var (
ErrPermissionDenied = errors.New("Permission denied") ErrPermissionDenied = errors.New("Permission denied")
// ErrUserInfo user info is missing for some reason // ErrUserInfo user info is missing for some reason
ErrUserInfo = errors.New("Missing user info") ErrUserInfo = errors.New("Missing user info")
ErrDelayReset = errors.New("Please give the validator at least 10 seconds to operate before attempting to reset the status")
ErrPermissionDeniedNotSubmitter = fmt.Errorf("%w: You must be the submitter to perform this action", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionRelease = fmt.Errorf("%w: Need Role SubmissionRelease", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapUpload = fmt.Errorf("%w: Need Role MapUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapReview = fmt.Errorf("%w: Need Role MapReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapDownload = fmt.Errorf("%w: Need Role MapDownload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleScriptWrite = fmt.Errorf("%w: Need Role ScriptWrite", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMaptest = fmt.Errorf("%w: Need Role Maptest", ErrPermissionDenied)
) )
type Service struct { type Service struct {
DB datastore.Datastore DB datastore.Datastore
Nats nats.JetStreamContext Nats nats.JetStreamContext
Client maps.MapsServiceClient
} }
// NewError creates *ErrorStatusCode from error returned by handler. // NewError creates *ErrorStatusCode from error returned by handler.

View File

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

View File

@@ -4,10 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"time"
"git.itzana.me/strafesnet/go-grpc/maps"
"git.itzana.me/strafesnet/maps-service/pkg/api" "git.itzana.me/strafesnet/maps-service/pkg/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"
@@ -15,50 +12,37 @@ import (
var( var(
CreationPhaseSubmissionsLimit = 20 CreationPhaseSubmissionsLimit = 20
CreationPhaseSubmissionStatuses = []model.SubmissionStatus{ CreationPhaseSubmissionStatuses = []model.Status{
model.SubmissionStatusChangesRequested, model.StatusChangesRequested,
model.SubmissionStatusSubmitted, model.StatusSubmitted,
model.SubmissionStatusUnderConstruction, model.StatusUnderConstruction,
} }
// prevent two mapfixes with same asset id ActiveSubmissionStatuses = []model.Status{
ActiveSubmissionStatuses = []model.SubmissionStatus{ model.StatusUploaded,
model.SubmissionStatusUploading, model.StatusUploading,
model.SubmissionStatusValidated, model.StatusValidated,
model.SubmissionStatusValidating, model.StatusValidating,
model.SubmissionStatusAccepted, model.StatusAccepted,
model.SubmissionStatusChangesRequested, model.StatusChangesRequested,
model.SubmissionStatusSubmitted, model.StatusSubmitted,
model.SubmissionStatusUnderConstruction, model.StatusUnderConstruction,
}
// limit mapfixes in the pipeline to one per target map
ActiveAcceptedSubmissionStatuses = []model.SubmissionStatus{
model.SubmissionStatusUploading,
model.SubmissionStatusValidated,
model.SubmissionStatusValidating,
model.SubmissionStatusAccepted,
} }
) )
var ( var (
ErrCreationPhaseSubmissionsLimit = errors.New("Active submissions limited to 20") ErrCreationPhaseSubmissionsLimit = errors.New("Active submissions limited to 20")
ErrActiveSubmissionSameAssetID = errors.New("There is an active submission with the same AssetID") ErrActiveSubmissionSameAssetID = errors.New("There is an active submission with the same AssetID")
ErrUploadedAssetIDAlreadyExists = errors.New("The submission UploadedAssetID is already set") ErrActiveSubmissionSameTargetAssetID = errors.New("There is an active submission with the same TargetAssetID")
ErrReleaseInvalidStatus = errors.New("Only submissions with Uploaded status can be released")
ErrReleaseNoUploadedAssetID = errors.New("Only submissions with a UploadedAssetID can be released")
ErrAcceptOwnSubmission = fmt.Errorf("%w: You cannot accept your own submission as the submitter", ErrPermissionDenied)
) )
// POST /submissions // POST /submissions
func (svc *Service) CreateSubmission(ctx context.Context, request *api.SubmissionCreate) (*api.ID, error) { func (svc *Service) CreateSubmission(ctx context.Context, request *api.SubmissionCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return nil, ErrUserInfo return nil, ErrUserInfo
} }
userId, err := userInfo.GetUserID() userId := userInfo.UserID
if err != nil {
return nil, err
}
// Check if user's submissions in the creation phase exceeds the limit // Check if user's submissions in the creation phase exceeds the limit
{ {
@@ -68,7 +52,7 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
creation_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{ creation_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: 1, Number: 1,
Size: int32(CreationPhaseSubmissionsLimit), Size: int32(CreationPhaseSubmissionsLimit),
},datastore.ListSortDisabled) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -87,7 +71,7 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
active_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{ active_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: 1, Number: 1,
Size: 1, Size: 1,
},datastore.ListSortDisabled) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -96,6 +80,23 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
} }
} }
// Check if an active submission with the same target asset id exists
if request.TargetAssetID.IsSet() && request.TargetAssetID.Value != 0{
filter := datastore.Optional()
filter.Add("target_asset_id", request.TargetAssetID.Value)
filter.Add("status_id", ActiveSubmissionStatuses)
active_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: 1,
Size: 1,
})
if err != nil {
return nil, err
}
if len(active_submissions) != 0{
return nil, ErrActiveSubmissionSameTargetAssetID
}
}
submission, err := svc.DB.Submissions().Create(ctx, model.Submission{ submission, err := svc.DB.Submissions().Create(ctx, model.Submission{
ID: 0, ID: 0,
DisplayName: request.DisplayName, DisplayName: request.DisplayName,
@@ -105,7 +106,8 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
AssetID: request.AssetID, AssetID: request.AssetID,
AssetVersion: request.AssetVersion, AssetVersion: request.AssetVersion,
Completed: false, Completed: false,
StatusID: model.SubmissionStatusUnderConstruction, TargetAssetID: request.TargetAssetID.Value,
StatusID: model.StatusUnderConstruction,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@@ -136,9 +138,8 @@ func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionP
AssetID: int64(submission.AssetID), AssetID: int64(submission.AssetID),
AssetVersion: int64(submission.AssetVersion), AssetVersion: int64(submission.AssetVersion),
Completed: submission.Completed, Completed: submission.Completed,
UploadedAssetID: api.NewOptInt64(int64(submission.UploadedAssetID)), TargetAssetID: api.NewOptInt64(int64(submission.TargetAssetID)),
StatusID: int32(submission.StatusID), StatusID: int32(submission.StatusID),
StatusMessage: submission.StatusMessage,
}, nil }, nil
} }
@@ -160,31 +161,29 @@ func (svc *Service) ListSubmissions(ctx context.Context, params api.ListSubmissi
filter.Add("game_id", params.GameID.Value) filter.Add("game_id", params.GameID.Value)
} }
sort := datastore.ListSort(params.Sort.Or(int32(datastore.ListSortDisabled)))
items, err := svc.DB.Submissions().List(ctx, filter, model.Page{ items, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: params.Page, Number: params.Page,
Size: params.Limit, Size: params.Limit,
},sort) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var resp []api.Submission var resp []api.Submission
for _, item := range items { for i := 0; i < len(items); i++ {
resp = append(resp, api.Submission{ resp = append(resp, api.Submission{
ID: item.ID, ID: items[i].ID,
DisplayName: item.DisplayName, DisplayName: items[i].DisplayName,
Creator: item.Creator, Creator: items[i].Creator,
GameID: item.GameID, GameID: items[i].GameID,
CreatedAt: item.CreatedAt.Unix(), CreatedAt: items[i].CreatedAt.Unix(),
UpdatedAt: item.UpdatedAt.Unix(), UpdatedAt: items[i].UpdatedAt.Unix(),
Submitter: int64(item.Submitter), Submitter: int64(items[i].Submitter),
AssetID: int64(item.AssetID), AssetID: int64(items[i].AssetID),
AssetVersion: int64(item.AssetVersion), AssetVersion: int64(items[i].AssetVersion),
Completed: item.Completed, Completed: items[i].Completed,
UploadedAssetID: api.NewOptInt64(int64(item.UploadedAssetID)), TargetAssetID: api.NewOptInt64(int64(items[i].TargetAssetID)),
StatusID: int32(item.StatusID), StatusID: int32(items[i].StatusID),
}) })
} }
@@ -197,23 +196,20 @@ func (svc *Service) ListSubmissions(ctx context.Context, params api.ListSubmissi
// //
// POST /submissions/{SubmissionID}/completed // POST /submissions/{SubmissionID}/completed
func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSubmissionCompletedParams) error { func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSubmissionCompletedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return ErrUserInfo return ErrUserInfo
} }
has_role, err := userInfo.HasRoleMaptest()
if err != nil {
return err
}
// check if caller has MaptestGame role (request must originate from a maptest roblox game) // check if caller has MaptestGame role (request must originate from a maptest roblox game)
if !has_role { if !userInfo.Roles.Maptest {
return ErrPermissionDeniedNeedRoleMaptest return ErrPermissionDenied
} }
pmap := datastore.Optional() pmap := datastore.Optional()
pmap.Add("completed", true) pmap.Add("completed", true)
return svc.DB.Submissions().Update(ctx, params.SubmissionID, pmap) err := svc.DB.Submissions().Update(ctx, params.SubmissionID, pmap)
return err
} }
// UpdateSubmissionModel implements patchSubmissionModel operation. // UpdateSubmissionModel implements patchSubmissionModel operation.
@@ -222,7 +218,7 @@ func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSu
// //
// POST /submissions/{SubmissionID}/model // POST /submissions/{SubmissionID}/model
func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.UpdateSubmissionModelParams) error { func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.UpdateSubmissionModelParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return ErrUserInfo return ErrUserInfo
} }
@@ -233,13 +229,9 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
return err return err
} }
has_role, err := userInfo.IsSubmitter(uint64(submission.Submitter))
if err != nil {
return err
}
// check if caller is the submitter // check if caller is the submitter
if !has_role { if !userInfo.IsSubmitter(uint64(submission.Submitter)) {
return ErrPermissionDeniedNotSubmitter return ErrPermissionDenied
} }
// check if Status is ChangesRequested|Submitted|UnderConstruction // check if Status is ChangesRequested|Submitted|UnderConstruction
@@ -248,7 +240,7 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
pmap.AddNotNil("asset_version", params.VersionID) pmap.AddNotNil("asset_version", params.VersionID)
//always reset completed when model changes //always reset completed when model changes
pmap.Add("completed", false) pmap.Add("completed", false)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusChangesRequested, model.SubmissionStatusSubmitted, model.SubmissionStatusUnderConstruction}, pmap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusChangesRequested, model.StatusSubmitted, model.StatusUnderConstruction}, pmap)
} }
// ActionSubmissionReject invokes actionSubmissionReject operation. // ActionSubmissionReject invokes actionSubmissionReject operation.
@@ -257,24 +249,20 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
// //
// POST /submissions/{SubmissionID}/status/reject // POST /submissions/{SubmissionID}/status/reject
func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.ActionSubmissionRejectParams) error { func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.ActionSubmissionRejectParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return ErrUserInfo return ErrUserInfo
} }
has_role, err := userInfo.HasRoleSubmissionReview()
if err != nil {
return err
}
// check if caller has required role // check if caller has required role
if !has_role { if !userInfo.Roles.SubmissionReview {
return ErrPermissionDeniedNeedRoleMapReview return ErrPermissionDenied
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusRejected) smap.Add("status_id", model.StatusRejected)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted}, smap)
} }
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation. // ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
@@ -283,24 +271,20 @@ func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.Actio
// //
// POST /submissions/{SubmissionID}/status/request-changes // POST /submissions/{SubmissionID}/status/request-changes
func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params api.ActionSubmissionRequestChangesParams) error { func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params api.ActionSubmissionRequestChangesParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return ErrUserInfo return ErrUserInfo
} }
has_role, err := userInfo.HasRoleSubmissionReview()
if err != nil {
return err
}
// check if caller has required role // check if caller has required role
if !has_role { if !userInfo.Roles.SubmissionReview {
return ErrPermissionDeniedNeedRoleMapReview return ErrPermissionDenied
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusChangesRequested) smap.Add("status_id", model.StatusChangesRequested)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidated, model.SubmissionStatusAccepted, model.SubmissionStatusSubmitted}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidated, model.StatusAccepted, model.StatusSubmitted}, smap)
} }
// ActionSubmissionRevoke invokes actionSubmissionRevoke operation. // ActionSubmissionRevoke invokes actionSubmissionRevoke operation.
@@ -309,7 +293,7 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params a
// //
// POST /submissions/{SubmissionID}/status/revoke // POST /submissions/{SubmissionID}/status/revoke
func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.ActionSubmissionRevokeParams) error { func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.ActionSubmissionRevokeParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return ErrUserInfo return ErrUserInfo
} }
@@ -320,19 +304,15 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio
return err return err
} }
has_role, err := userInfo.IsSubmitter(uint64(submission.Submitter))
if err != nil {
return err
}
// check if caller is the submitter // check if caller is the submitter
if !has_role { if !userInfo.IsSubmitter(uint64(submission.Submitter)) {
return ErrPermissionDeniedNotSubmitter return ErrPermissionDenied
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusUnderConstruction) smap.Add("status_id", model.StatusUnderConstruction)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted, model.SubmissionStatusChangesRequested}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted, model.StatusChangesRequested}, smap)
} }
// ActionSubmissionSubmit invokes actionSubmissionSubmit operation. // ActionSubmissionSubmit invokes actionSubmissionSubmit operation.
@@ -341,7 +321,7 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio
// //
// POST /submissions/{SubmissionID}/status/submit // POST /submissions/{SubmissionID}/status/submit
func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.ActionSubmissionSubmitParams) error { func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.ActionSubmissionSubmitParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return ErrUserInfo return ErrUserInfo
} }
@@ -352,19 +332,15 @@ func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.Actio
return err return err
} }
has_role, err := userInfo.IsSubmitter(uint64(submission.Submitter))
if err != nil {
return err
}
// check if caller is the submitter // check if caller is the submitter
if !has_role { if !userInfo.IsSubmitter(uint64(submission.Submitter)) {
return ErrPermissionDeniedNotSubmitter return ErrPermissionDenied
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusSubmitted) smap.Add("status_id", model.StatusSubmitted)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUnderConstruction, model.SubmissionStatusChangesRequested}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUnderConstruction, model.StatusChangesRequested}, smap)
} }
// ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation. // ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation.
@@ -373,128 +349,81 @@ func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.Actio
// //
// POST /submissions/{SubmissionID}/status/trigger-upload // POST /submissions/{SubmissionID}/status/trigger-upload
func (svc *Service) ActionSubmissionTriggerUpload(ctx context.Context, params api.ActionSubmissionTriggerUploadParams) error { func (svc *Service) ActionSubmissionTriggerUpload(ctx context.Context, params api.ActionSubmissionTriggerUploadParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return ErrUserInfo return ErrUserInfo
} }
has_role, err := userInfo.HasRoleSubmissionUpload()
if err != nil {
return err
}
// check if caller has required role // check if caller has required role
if !has_role { if !userInfo.Roles.SubmissionRelease {
return ErrPermissionDeniedNeedRoleMapUpload return ErrPermissionDenied
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusUploading) smap.Add("status_id", model.StatusUploading)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidated}, smap) submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusValidated}, smap)
if err != nil { if err != nil {
return err return err
} }
// sentinel value because we are not using rust // sentinel value because we are not using rust
if submission.UploadedAssetID == 0 { if submission.TargetAssetID == 0 {
// this is a new map // this is a new map
upload_new_request := model.UploadNewRequest{ publish_new_request := model.PublishNewRequest{
SubmissionID: submission.ID, SubmissionID: submission.ID,
ModelID: submission.ValidatedAssetID, ModelID: submission.AssetID,
ModelVersion: submission.ValidatedAssetVersion, ModelVersion: submission.AssetVersion,
// upload as displayname, whatever // publish as displayname, whatever
ModelName: submission.DisplayName, ModelName: submission.DisplayName,
} }
j, err := json.Marshal(upload_new_request) j, err := json.Marshal(publish_new_request)
if err != nil { if err != nil {
return err return err
} }
svc.Nats.Publish("maptest.submissions.upload", []byte(j)) svc.Nats.Publish("maptest.submissions.publishnew", []byte(j))
} else { } else {
// refuse to operate // this is a map fix
return ErrUploadedAssetIDAlreadyExists publish_fix_request := model.PublishFixRequest{
SubmissionID: submission.ID,
ModelID: submission.AssetID,
ModelVersion: submission.AssetVersion,
TargetAssetID: submission.TargetAssetID,
}
j, err := json.Marshal(publish_fix_request)
if err != nil {
return err
}
svc.Nats.Publish("maptest.submissions.publishfix", []byte(j))
} }
return nil return nil
} }
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
//
// Role SubmissionRelease changes status from Uploading -> Validated.
//
// POST /submissions/{SubmissionID}/status/reset-uploading
func (svc *Service) ActionSubmissionValidated(ctx context.Context, params api.ActionSubmissionValidatedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionUpload()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapUpload
}
// check when submission was updated
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil {
return err
}
if time.Now().Before(submission.UpdatedAt.Add(time.Second*10)) {
// the last time the submission was updated must be longer than 10 seconds ago
return ErrDelayReset
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusValidated)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
}
// ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation. // ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation.
// //
// Role Reviewer triggers validation and changes status from Submitted -> Validating. // Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
// //
// POST /submissions/{SubmissionID}/status/trigger-validate // POST /submissions/{SubmissionID}/status/trigger-validate
func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params api.ActionSubmissionTriggerValidateParams) error { func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params api.ActionSubmissionTriggerValidateParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok { if !ok {
return ErrUserInfo return ErrUserInfo
} }
has_role, err := userInfo.HasRoleSubmissionReview()
if err != nil {
return err
}
// check if caller has required role // check if caller has required role
if !has_role { if !userInfo.Roles.SubmissionReview {
return ErrPermissionDeniedNeedRoleMapReview return ErrPermissionDenied
}
// 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
}
has_role, err = userInfo.IsSubmitter(uint64(submission.Submitter))
if err != nil {
return err
}
// check if caller is NOT the submitter
if has_role {
return ErrAcceptOwnSubmission
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusValidating) smap.Add("status_id", model.StatusValidating)
submission, err = svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted}, smap) submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted, model.StatusAccepted}, smap)
if err != nil { if err != nil {
return err return err
} }
@@ -503,12 +432,7 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
SubmissionID: submission.ID, SubmissionID: submission.ID,
ModelID: submission.AssetID, ModelID: submission.AssetID,
ModelVersion: submission.AssetVersion, ModelVersion: submission.AssetVersion,
ValidatedModelID: nil, ValidatedModelID: 0, //TODO: reuse velidation models
}
// sentinel values because we're not using rust
if submission.ValidatedAssetID != 0 {
validate_request.ValidatedModelID = &submission.ValidatedAssetID
} }
j, err := json.Marshal(validate_request) j, err := json.Marshal(validate_request)
@@ -520,157 +444,3 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
return nil return nil
} }
// ActionSubmissionRetryValidate invokes actionSubmissionRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
//
// POST /submissions/{SubmissionID}/status/retry-validate
func (svc *Service) ActionSubmissionRetryValidate(ctx context.Context, params api.ActionSubmissionRetryValidateParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionReview()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusValidating)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusAccepted}, smap)
if err != nil {
return err
}
validate_request := model.ValidateRequest{
SubmissionID: submission.ID,
ModelID: submission.AssetID,
ModelVersion: submission.AssetVersion,
ValidatedModelID: nil,
}
// sentinel values because we're not using rust
if submission.ValidatedAssetID != 0 {
validate_request.ValidatedModelID = &submission.ValidatedAssetID
}
j, err := json.Marshal(validate_request)
if err != nil {
return err
}
svc.Nats.Publish("maptest.submissions.validate", []byte(j))
return nil
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// Role SubmissionReview changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/reset-validating
func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params api.ActionSubmissionAcceptedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionReview()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleMapReview
}
// check when submission was updated
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil {
return err
}
if time.Now().Before(submission.UpdatedAt.Add(time.Second*10)) {
// the last time the submission was updated must be longer than 10 seconds ago
return ErrDelayReset
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusAccepted)
smap.Add("status_message", "Manually forced reset")
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
}
// ReleaseSubmissions invokes releaseSubmissions operation.
//
// Release a set of uploaded maps.
//
// POST /release-submissions
func (svc *Service) ReleaseSubmissions(ctx context.Context, request []api.ReleaseInfo) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionRelease()
if err != nil {
return err
}
// check if caller has required role
if !has_role {
return ErrPermissionDeniedNeedRoleSubmissionRelease
}
idList := make([]int64, len(request))
for i, releaseInfo := range request {
idList[i] = releaseInfo.SubmissionID
}
// fetch submissions
submissions, err := svc.DB.Submissions().GetList(ctx, idList)
if err != nil {
return err
}
// check each submission to make sure it is ready to release
for _,submission := range submissions{
if submission.StatusID != model.SubmissionStatusUploaded{
return ErrReleaseInvalidStatus
}
if submission.UploadedAssetID == 0{
return ErrReleaseNoUploadedAssetID
}
}
for i,submission := range submissions{
date := request[i].Date.Unix()
// create each map with go-grpc
_, err := svc.Client.Create(ctx, &maps.MapRequest{
ID: submission.UploadedAssetID,
DisplayName: &submission.DisplayName,
Creator: &submission.Creator,
GameID: &submission.GameID,
Date: &date,
})
if err != nil {
return err
}
// update each status to Released
smap := datastore.Optional()
smap.Add("status_id", model.SubmissionStatusReleased)
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, submission.ID, []model.SubmissionStatus{model.SubmissionStatusUploaded}, smap)
if err != nil {
return err
}
}
return nil
}

View File

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

View File

@@ -19,8 +19,7 @@ func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*a
Name: req.Name, Name: req.Name,
Hash: int64(model.HashSource(req.Source)), Hash: int64(model.HashSource(req.Source)),
Source: req.Source, Source: req.Source,
ResourceType: model.ResourceType(req.ResourceType), SubmissionID: req.SubmissionID.Or(0),
ResourceID: req.ResourceID.Or(0),
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@@ -65,13 +64,12 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
} }
var resp []api.Script var resp []api.Script
for _, item := range items { for i := 0; i < len(items); i++ {
resp = append(resp, api.Script{ resp = append(resp, api.Script{
ID: item.ID, ID: items[i].ID,
Hash: model.HashFormat(uint64(item.Hash)), Hash: model.HashFormat(uint64(items[i].Hash)),
Source: item.Source, Source: items[i].Source,
ResourceType: int32(item.ResourceType), SubmissionID: items[i].SubmissionID,
ResourceID: item.ResourceID,
}) })
} }
@@ -94,7 +92,6 @@ func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (
Name: script.Name, Name: script.Name,
Hash: model.HashFormat(uint64(script.Hash)), Hash: model.HashFormat(uint64(script.Hash)),
Source: script.Source, Source: script.Source,
ResourceType: int32(script.ResourceType), SubmissionID: script.SubmissionID,
ResourceID: script.ResourceID,
}, nil }, nil
} }

View File

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

1
validation/.gitignore vendored Normal file
View File

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

File diff suppressed because it is too large Load Diff

View File

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

1
validation/api/.gitignore vendored Normal file
View File

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

1463
validation/api/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -31,47 +31,6 @@ impl Context{
).await.map_err(Error::Response)? ).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest) .json().await.map_err(Error::Reqwest)
} }
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
let url_raw=format!("{}/scripts",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
let mut query_pairs=url.query_pairs_mut();
query_pairs.append_pair("Page",config.Page.to_string().as_str());
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
if let Some(name)=config.Name{
query_pairs.append_pair("Name",name);
}
if let Some(hash)=config.Hash{
query_pairs.append_pair("Hash",hash);
}
if let Some(source)=config.Source{
query_pairs.append_pair("Source",source);
}
if let Some(submission_id)=config.SubmissionID{
query_pairs.append_pair("SubmissionID",submission_id.to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest)
}
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
let scripts=self.get_scripts(GetScriptsRequest{
Page:1,
Limit:2,
Hash:Some(config.hash),
Name:None,
Source:None,
SubmissionID:None,
}).await.map_err(SingleItemError::Other)?;
if 1<scripts.len(){
return Err(SingleItemError::DuplicateItems);
}
Ok(scripts.into_iter().next())
}
pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{ pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->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)?;
@@ -131,14 +90,14 @@ impl Context{
).await.map_err(Error::Response)? ).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest) .json().await.map_err(Error::Reqwest)
} }
pub async fn update_submission_validated_model(&self,config:UpdateSubmissionModelRequest)->Result<(),Error>{ pub async fn update_submission_model(&self,config:UpdateSubmissionModelRequest)->Result<(),Error>{
let url_raw=format!("{}/submissions/{}/validated-model",self.0.base_url,config.SubmissionID); let url_raw=format!("{}/submissions/{}/model",self.0.base_url,config.SubmissionID);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?; let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{ {
url.query_pairs_mut() url.query_pairs_mut()
.append_pair("ValidatedModelID",config.ModelID.to_string().as_str()) .append_pair("ModelID",config.ModelID.to_string().as_str())
.append_pair("ValidatedModelVersion",config.ModelVersion.to_string().as_str()); .append_pair("ModelVersion",config.ModelVersion.to_string().as_str());
} }
response_ok( response_ok(
@@ -151,9 +110,9 @@ impl Context{
let url_raw=format!("{}/submissions/{}/status/validator-uploaded",self.0.base_url,config.SubmissionID); let url_raw=format!("{}/submissions/{}/status/validator-uploaded",self.0.base_url,config.SubmissionID);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?; let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{ if let Some(target_asset_id)=config.TargetAssetID{
url.query_pairs_mut() url.query_pairs_mut()
.append_pair("UploadedAssetID",config.UploadedAssetID.to_string().as_str()); .append_pair("TargetAssetID",target_asset_id.to_string().as_str());
} }
response_ok( response_ok(
self.0.post_empty_body(url).await.map_err(Error::Reqwest)? self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
@@ -161,20 +120,7 @@ impl Context{
Ok(()) Ok(())
} }
pub async fn action_submission_accepted(&self,config:ActionSubmissionAcceptedRequest)->Result<(),Error>{
let url_raw=format!("{}/submissions/{}/status/validator-failed",self.0.base_url,config.SubmissionID);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
url.query_pairs_mut()
.append_pair("StatusMessage",config.StatusMessage.as_str());
}
response_ok(
self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
action!(action_submission_validated,"validator-validated"); action!(action_submission_validated,"validator-validated");
action!(action_submission_accepted,"validator-failed");
action!(action_submission_released,"releaser-released");
} }

View File

@@ -60,9 +60,9 @@ pub async fn response_ok(response:reqwest::Response)->Result<reqwest::Response,R
} }
} }
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde::Serialize,serde::Deserialize)] #[derive(Clone,Copy,PartialEq,Eq,serde::Serialize,serde::Deserialize)]
pub struct ScriptID(pub(crate)i64); pub struct ScriptID(pub(crate)i64);
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)] #[derive(Clone,Copy,serde::Serialize,serde::Deserialize)]
pub struct ScriptPolicyID(pub(crate)i64); pub struct ScriptPolicyID(pub(crate)i64);
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
@@ -70,7 +70,7 @@ pub struct GetScriptRequest{
pub ScriptID:ScriptID, pub ScriptID:ScriptID,
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)] #[derive(serde::Serialize)]
pub struct GetScriptsRequest<'a>{ pub struct GetScriptsRequest<'a>{
pub Page:u32, pub Page:u32,
pub Limit:u32, pub Limit:u32,
@@ -83,12 +83,11 @@ pub struct GetScriptsRequest<'a>{
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if="Option::is_none")]
pub SubmissionID:Option<i64>, pub SubmissionID:Option<i64>,
} }
#[derive(Clone,Copy,Debug)]
pub struct HashRequest<'a>{ pub struct HashRequest<'a>{
pub hash:&'a str, pub hash:&'a str,
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct ScriptResponse{ pub struct ScriptResponse{
pub ID:ScriptID, pub ID:ScriptID,
pub Name:String, pub Name:String,
@@ -96,28 +95,21 @@ pub struct ScriptResponse{
pub Source:String, pub Source:String,
pub SubmissionID:i64, pub SubmissionID:i64,
} }
#[derive(Clone,Debug,serde::Serialize)]
pub enum ResourceType{
Unknown=0,
Mapfix=1,
Submission=2,
}
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)] #[derive(serde::Serialize)]
pub struct CreateScriptRequest<'a>{ pub struct CreateScriptRequest<'a>{
pub Name:&'a str, pub Name:&'a str,
pub Source:&'a str, pub Source:&'a str,
pub ResourceType:ResourceType,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if="Option::is_none")]
pub ResourceID:Option<i64>, pub SubmissionID:Option<i64>,
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct ScriptIDResponse{ pub struct ScriptIDResponse{
pub ID:ScriptID, pub ID:ScriptID,
} }
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)] #[derive(PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
#[repr(i32)] #[repr(i32)]
pub enum Policy{ pub enum Policy{
None=0, // not yet reviewed None=0, // not yet reviewed
@@ -128,7 +120,7 @@ pub enum Policy{
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)] #[derive(serde::Serialize)]
pub struct GetScriptPoliciesRequest<'a>{ pub struct GetScriptPoliciesRequest<'a>{
pub Page:u32, pub Page:u32,
pub Limit:u32, pub Limit:u32,
@@ -140,7 +132,7 @@ pub struct GetScriptPoliciesRequest<'a>{
pub Policy:Option<Policy>, pub Policy:Option<Policy>,
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct ScriptPolicyResponse{ pub struct ScriptPolicyResponse{
pub ID:ScriptPolicyID, pub ID:ScriptPolicyID,
pub FromScriptHash:String, pub FromScriptHash:String,
@@ -148,20 +140,20 @@ pub struct ScriptPolicyResponse{
pub Policy:Policy pub Policy:Policy
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)] #[derive(serde::Serialize)]
pub struct CreateScriptPolicyRequest{ pub struct CreateScriptPolicyRequest{
pub FromScriptID:ScriptID, pub FromScriptID:ScriptID,
pub ToScriptID:ScriptID, pub ToScriptID:ScriptID,
pub Policy:Policy, pub Policy:Policy,
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct ScriptPolicyIDResponse{ pub struct ScriptPolicyIDResponse{
pub ID:ScriptPolicyID, pub ID:ScriptPolicyID,
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)] #[derive(serde::Serialize)]
pub struct UpdateScriptPolicyRequest{ pub struct UpdateScriptPolicyRequest{
pub ID:ScriptPolicyID, pub ID:ScriptPolicyID,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if="Option::is_none")]
@@ -173,7 +165,6 @@ pub struct UpdateScriptPolicyRequest{
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct UpdateSubmissionModelRequest{ pub struct UpdateSubmissionModelRequest{
pub SubmissionID:i64, pub SubmissionID:i64,
pub ModelID:u64, pub ModelID:u64,
@@ -181,42 +172,9 @@ pub struct UpdateSubmissionModelRequest{
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionUploadedRequest{ pub struct ActionSubmissionUploadedRequest{
pub SubmissionID:i64, pub SubmissionID:i64,
pub UploadedAssetID:u64, pub TargetAssetID:Option<u64>,
} }
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionAcceptedRequest{
pub SubmissionID:i64,
pub StatusMessage:String,
}
#[derive(Clone,Copy,Debug)]
pub struct SubmissionID(pub i64); pub struct SubmissionID(pub i64);
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct UpdateMapfixModelRequest{
pub MapfixID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixUploadedRequest{
pub MapfixID:i64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixAcceptedRequest{
pub MapfixID:i64,
pub StatusMessage:String,
}
#[derive(Clone,Copy,Debug)]
pub struct MapfixID(pub i64);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,27 +1,8 @@
"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 (<>
<Header/>
return <> {children}
<Header/> </>)
{children} }
</>
}

View File

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

View File

@@ -2,6 +2,15 @@
@use "../../globals.scss"; @use "../../globals.scss";
.container {
display: flex;
justify-content: center;
align-items: center;
height: calc(100vh - var(--header-height));
overflow: hidden;
position: relative;
}
a { a {
color:rgb(255, 255, 255); color:rgb(255, 255, 255);
@@ -12,64 +21,4 @@ a {
&:active { &:active {
color: rgb(192, 192, 192) color: rgb(192, 192, 192)
} }
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
grid-template-rows: repeat(3, 1fr);
gap: 16px;
max-width: 100%;
margin: 0 auto;
overflow-x: hidden;
box-sizing: border-box;
}
@media (max-width: 768px) {
.grid {
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
margin: 0.3rem;
}
.pagination button {
padding: 0.25rem 0.5rem;
font-size: 1.15rem;
border: none;
border-radius: 0.35rem;
background-color: #33333350;
color: #fff;
cursor: pointer;
}
.pagination button:disabled {
background-color: #5555559a;
cursor: not-allowed;
}
.pagination-dots {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
justify-content: center;
width: 100%;
}
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #bbb;
cursor: pointer;
}
.dot.active {
background-color: #333;
} }

View File

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

View File

@@ -9,10 +9,9 @@ interface CommentersProps {
} }
interface CreatorAndReviewStatus { 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"],
comments: Comment[], comments: Comment[],
name: string name: string
} }

View File

@@ -1,40 +1,24 @@
import { Button, ButtonOwnProps } from "@mui/material"; import { Button, ButtonOwnProps } from "@mui/material";
import { SubmissionInfo } from "@/app/ts/Submission";
type Actions = "Completed" | "Submit" | "Reject" | "Revoke" type Actions = "Completed" | "Submit" | "Reject" | "Revoke"
type ApiActions = Lowercase<Actions> | "trigger-validate" | "retry-validate" | "trigger-upload" | "reset-uploading" | "reset-validating" type Review = Actions | "Accept" | "Validate" | "Upload"
type Review = Actions | "Accept" | "Validate" | "Upload" | "Reset Uploading (fix softlocked status)" | "Reset Validating (fix softlocked status)" | "Request Changes" type Action = Lowercase<Actions> | "trigger-validate" | "trigger-upload"
interface ReviewButton { interface ReviewButton {
name: Review, name: Review,
action: ApiActions, action: Action,
submissionId: string, submissionId: string,
color: ButtonOwnProps["color"] color: ButtonOwnProps["color"]
} }
interface ReviewId { function ReviewButtonClicked(action: Action, submissionId: string) {
submissionId: string fetch(`/api/submissions/${submissionId}/status/${action}`, {
} method: "POST",
headers: {
async function ReviewButtonClicked(action: ApiActions, submissionId: string) { "Content-type": "application/json",
try {
const response = await fetch(`/api/submissions/${submissionId}/status/${action}`, {
method: "POST",
headers: {
"Content-type": "application/json",
}
});
// Check if the HTTP request was successful
if (!response.ok) {
const errorDetails = await response.text();
// Throw an error with detailed information
throw new Error(`HTTP error! status: ${response.status}, details: ${errorDetails}`);
} }
}).then(() => { window.location.reload(); })
window.location.reload();
} catch (error) {
console.error("Error updating submission status:", error);
}
} }
function ReviewButton(props: ReviewButton) { function ReviewButton(props: ReviewButton) {
@@ -44,8 +28,8 @@ function ReviewButton(props: ReviewButton) {
onClick={() => { ReviewButtonClicked(props.action, props.submissionId) }}>{props.name}</Button> onClick={() => { ReviewButtonClicked(props.action, props.submissionId) }}>{props.name}</Button>
} }
export default function ReviewButtons(props: ReviewId) { export default function ReviewButtons(props: SubmissionInfo) {
const submissionId = props.submissionId const submissionId = props.ID.toString()
// When is each button visible? // When is each button visible?
// Multiple buttons can be visible at once. // Multiple buttons can be visible at once.
// Action | Role | When Current Status is One of: // Action | Role | When Current Status is One of:
@@ -54,21 +38,17 @@ export default function ReviewButtons(props: ReviewId) {
// Revoke | Submitter | Submitted, ChangesRequested // Revoke | Submitter | Submitted, ChangesRequested
// Accept | Reviewer | Submitted // Accept | Reviewer | Submitted
// Validate | Reviewer | Accepted // Validate | Reviewer | Accepted
// ResetValidating| Reviewer | Validating
// Reject | Reviewer | Submitted // Reject | Reviewer | Submitted
// RequestChanges | Reviewer | Validated, Accepted, Submitted // RequestChanges | Reviewer | Validated, Accepted, Submitted
// Upload | MapAdmin | Validated // Upload | MapAdmin | Validated
// ResetUploading | MapAdmin | Uploading
return ( return (
<section className="review-set"> <section className="review-set">
<ReviewButton color="info" name="Submit" action="submit" submissionId={submissionId}/> <ReviewButton color="info" name="Submit" action="submit" submissionId={submissionId}/>
<ReviewButton color="info" name="Revoke" action="revoke" submissionId={submissionId}/> <ReviewButton color="info" name="Revoke" action="revoke" submissionId={submissionId}/>
<ReviewButton color="info" name="Accept" action="trigger-validate" submissionId={submissionId}/> <ReviewButton color="info" name="Accept" action="trigger-validate" submissionId={submissionId}/>
<ReviewButton color="info" name="Validate" action="retry-validate" submissionId={submissionId}/> <ReviewButton color="info" name="Validate" action="trigger-validate" submissionId={submissionId}/>
<ReviewButton color="error" name="Reject" action="reject" submissionId={submissionId}/> <ReviewButton color="error" name="Reject" action="reject" submissionId={submissionId}/>
<ReviewButton color="info" name="Upload" action="trigger-upload" submissionId={submissionId}/> <ReviewButton color="info" name="Upload" action="trigger-upload" submissionId={submissionId}/>
<ReviewButton color="error" name="Reset Uploading (fix softlocked status)" action="reset-uploading" submissionId={submissionId}/>
<ReviewButton color="error" name="Reset Validating (fix softlocked status)" action="reset-validating" submissionId={submissionId}/>
</section> </section>
) )
} }

View File

@@ -14,10 +14,6 @@ import { useState, useEffect } from "react";
import "./(styles)/page.scss"; import "./(styles)/page.scss";
interface ReviewId {
submissionId: string
}
function Ratings() { function Ratings() {
return ( return (
<Window className="rating-window" title="Rating"> <Window className="rating-window" title="Rating">
@@ -39,14 +35,15 @@ function Ratings() {
) )
} }
function RatingArea(submission: ReviewId) { function RatingArea(submission: SubmissionInfo) {
return ( return (
<aside className="review-area"> <aside className="review-area">
<section className="map-image-area"> <section className="map-image-area">
<MapImage/> <MapImage/>
</section> </section>
<Ratings/> <Ratings/>
<ReviewButtons submissionId={submission.submissionId}/> {/* TODO: NOT DO!!! */} {ReviewButtons(submission)}
{/* <ReviewButtons submissionId={submission.ID}/> */}
</aside> </aside>
) )
} }
@@ -54,7 +51,6 @@ function RatingArea(submission: ReviewId) {
function TitleAndComments(stats: CreatorAndReviewStatus) { function TitleAndComments(stats: CreatorAndReviewStatus) {
const Review = SubmissionStatusToString(stats.review) const Review = SubmissionStatusToString(stats.review)
// TODO: hide status message when status is not "Accepted"
return ( return (
<main className="review-info"> <main className="review-info">
<div> <div>
@@ -65,7 +61,6 @@ function TitleAndComments(stats: CreatorAndReviewStatus) {
</div> </div>
<p className="by-creator">by <Link href="" target="_blank">{stats.creator}</Link></p> <p className="by-creator">by <Link href="" target="_blank">{stats.creator}</Link></p>
<p className="asset-id">Model Asset ID {stats.asset_id}</p> <p className="asset-id">Model Asset ID {stats.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>
@@ -96,8 +91,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 submissionId={dynamicId.submissionId}/> {RatingArea(submission)}
<TitleAndComments name={submission.DisplayName} creator={submission.Creator} review={submission.StatusID} status_message={submission.StatusMessage} asset_id={submission.AssetID} comments={[]}/> <TitleAndComments name={submission.DisplayName} creator={submission.Creator} review={submission.StatusID} asset_id={submission.AssetID} comments={[]}/>
</section> </section>
</main> </main>
</Webpage> </Webpage>

View File

@@ -1,7 +1,6 @@
import React from "react"; import React from "react";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { Rating } from "@mui/material";
interface SubmissionCardProps { interface SubmissionCardProps {
displayName: string; displayName: string;
@@ -15,26 +14,11 @@ export default function SubmissionCard(props: SubmissionCardProps) {
return ( return (
<Link href={`/submissions/${props.id}`}> <Link href={`/submissions/${props.id}`}>
<div className="submissionCard"> <div className="submissionCard">
<div className="content"> {/* TODO: Grab image of model */}
<div className="map-image"> <Image height={200} width={200} priority={true} src="https://api.ic3.space/strafe/map-images/11222350808" style={{ width: `100%` }} alt={props.displayName} />
{/* TODO: Grab image of model */} <h3>{props.displayName}</h3>
<Image height={200} width={200} priority={true} src="https://api.ic3.space/strafe/map-images/11222350808" style={{ width: `100%` }} alt={props.displayName} /> <p>By {props.author}</p>
</div> <p> {props.rating}</p> {/* TODO: paste the star element from submission/1 page */}
<div className="details">
<div className="header">
<span className="displayName">{props.displayName}</span>
<div className="rating">
<Rating value={props.rating} readOnly size="small" />
</div>
</div>
<div className="footer">
<div className="author">
<Image className="avatar" width={28} height={28} priority={true} src="https://api.ic3.space/strafe/map-images/11222350808" alt={props.author}/>
<span>{props.author}</span>
</div>
</div>
</div>
</div>
</div> </div>
</Link> </Link>
); );

View File

@@ -0,0 +1,43 @@
//This can all be solved using 0 JavaScript,
//display: grid, ->1fr unit<-
import React, { useState, useEffect } from 'react';
import { Grid, Skeleton } from '@mui/material';
const elementWidth = 220;
function calculateSkeletonCount(setState: React.Dispatch<React.SetStateAction<number>>) {
const viewportWidth = window.innerWidth - 100 * 2;
setState(Math.floor(viewportWidth / elementWidth) * 2);
};
function SkeletonGrid() {
const [skeletonCount, setSkeletonCount] = useState(0);
useEffect(() => {
calculateSkeletonCount(setSkeletonCount);
window.addEventListener('resize', () => { calculateSkeletonCount(setSkeletonCount) });
return () => {
window.removeEventListener('resize', () => { calculateSkeletonCount(setSkeletonCount) });
};
}, []);
return (
<Grid
container
spacing={2}
alignItems="center"
justifyContent="center"
style={{ maxWidth: 'calc(100vw - 100px)', margin: '0 auto' }}
>
{Array.from({ length: skeletonCount }).map((_, index) => (
<Grid item key={index}>
<Skeleton variant="rectangular" width={215} height={340} />
</Grid>
))}
</Grid>
);
};
export default SkeletonGrid;

View File

@@ -1,61 +1,33 @@
'use client' 'use client'
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from 'react'
import { SubmissionInfo } from "../ts/Submission"; import { SubmissionInfo } from '../ts/Submission';
import { Grid2 as Grid } from '@mui/material';
import SubmissionCard from "./_card"; import SubmissionCard from "./_card";
import SkeletonGrid from './_loading';
import Webpage from "@/app/_components/webpage"; import Webpage from "@/app/_components/webpage";
import "./(styles)/page.scss"; import "./(styles)/page.scss";
export default function SubmissionInfoPage() { export default function SubmissionInfoPage() {
const [submissions, setSubmissions] = useState<SubmissionInfo[]>([]) const [submissions, setSubmissions] = useState<SubmissionInfo[]>([])
const [currentPage, setCurrentPage] = useState(0);
const cardsPerPage = 24; // built to fit on a 1920x1080 monitor
const totalPages = Math.ceil(submissions.length / cardsPerPage); 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)
const currentCards = submissions.slice(
currentPage * cardsPerPage,
(currentPage + 1) * cardsPerPage
);
const nextPage = () => {
if (currentPage < totalPages - 1) {
setCurrentPage(currentPage + 1);
}
};
const prevPage = () => {
if (currentPage > 0) {
setCurrentPage(currentPage - 1);
}
};
useEffect(() => {
async function fetchSubmissions() { async function fetchSubmissions() {
const res = await fetch('/api/submissions?Page=1&Limit=100') const res = await fetch('/api/submissions?Page=1&Limit=100')
if (res.ok) { if (res.ok) {
setSubmissions(await res.json()) setSubmissions(await res.json())
} }
} }
setTimeout(() => { // testing loading screen made by chatGerbertPT
setTimeout(() => {
fetchSubmissions() fetchSubmissions()
}, 50); }, 250);
}, []) }, [])
if (!submissions) { if (!submissions) {
return <Webpage> return <Webpage>
<main> <main style={{ display: 'flex', justifyContent: 'center', padding: '1rem' }}>
Loading... <SkeletonGrid />
</main>
</Webpage>
}
if (submissions && submissions.length == 0) {
return <Webpage>
<main>
Submissions list is empty.
</main> </main>
</Webpage> </Webpage>
} }
@@ -63,48 +35,43 @@ export default function SubmissionInfoPage() {
return ( return (
// TODO: Add filter settings & searchbar & page selector // TODO: Add filter settings & searchbar & page selector
<Webpage> <Webpage>
<main <main style={{ display: 'flex', justifyContent: 'center', padding: '1rem' }}>
style={{ <Grid
display: 'flex', container
flexDirection: 'column', spacing={2}
justifyContent: 'center', alignItems="center"
alignItems: 'center', justifyContent="center"
padding: '1rem', style={{ maxWidth: 'calc(100vw - 100px)', margin: '0 auto' }}
width: '100%', >
maxWidth: '100vw', {submissions.map((submission) => (
boxSizing: 'border-box', <Grid key={submission.ID}>
overflowX: 'hidden' <SubmissionCard
}} id={submission.ID}
> assetId={submission.AssetID}
<div className="pagination-dots"> displayName={submission.DisplayName}
{Array.from({ length: totalPages }).map((_, index) => ( author={submission.Creator}
<span rating={submission.StatusID}
key={index} />
className={`dot ${index === currentPage ? 'active' : ''}`} </Grid>
onClick={() => setCurrentPage(index)}
></span>
))} ))}
</div> </Grid>
<div className="pagination">
<button onClick={prevPage} disabled={currentPage === 0}>&lt;</button>
<span>
Page {currentPage + 1} of {totalPages}
</span>
<button onClick={nextPage} disabled={currentPage === totalPages - 1}>&gt;</button>
</div>
<div className="grid">
{currentCards.map((submission) => (
<SubmissionCard
key={submission.ID}
id={submission.ID}
assetId={submission.AssetID}
displayName={submission.DisplayName}
author={submission.Creator}
rating={submission.StatusID}
/>
))}
</div>
</main> </main>
</Webpage> </Webpage>
) )
}
// {
// "ID": 1,
// "DisplayName": "81bfc7a",
// "Creator": "79fbe8d",
// "GameID": 1073741824,
// "CreatedAt": 1734490019,
// "UpdatedAt": 1734565641,
// "Submitter": 1,
// "AssetID": 6438937102481093,
// "AssetVersion": 1225679040570074,
// "Completed": false,
// "SubmissionType": 0,
// "TargetAssetID": 1057095197389979,
// "StatusID": 4
// }
}

View File

@@ -22,6 +22,7 @@ const BootstrapInput = styled(InputBase)(({ theme }) => ({
fontSize: 16, fontSize: 16,
padding: '10px 26px 10px 12px', padding: '10px 26px 10px 12px',
transition: theme.transitions.create(['border-color', 'box-shadow']), transition: theme.transitions.create(['border-color', 'box-shadow']),
// Use the system font instead of the default Roboto font.
fontFamily: [ fontFamily: [
'-apple-system', '-apple-system',
'BlinkMacSystemFont', 'BlinkMacSystemFont',

View File

@@ -1,6 +1,6 @@
"use client" "use client"
import { Button, TextField } from "@mui/material" import { FormControl, FormControlLabel, Button, TextField, Checkbox } from "@mui/material"
import GameSelection from "./_game"; import GameSelection from "./_game";
import SendIcon from '@mui/icons-material/Send'; import SendIcon from '@mui/icons-material/Send';
@@ -15,6 +15,7 @@ interface SubmissionPayload {
GameID: number; GameID: number;
AssetID: number; AssetID: number;
AssetVersion: number; AssetVersion: number;
TargetAssetID: number;
} }
interface IdResponse { interface IdResponse {
ID: number; ID: number;
@@ -22,6 +23,7 @@ interface IdResponse {
export default function SubmissionInfoPage() { export default function SubmissionInfoPage() {
const [game, setGame] = useState(1); const [game, setGame] = useState(1);
const [isFixingMap, setIsFixingMap] = useState(false);
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => { const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
@@ -35,6 +37,7 @@ export default function SubmissionInfoPage() {
GameID: game, GameID: game,
AssetID: Number((formData.get("asset-id") as string) ?? "0"), AssetID: Number((formData.get("asset-id") as string) ?? "0"),
AssetVersion: 0, AssetVersion: 0,
TargetAssetID: isFixingMap ? Number((formData.get("target-asset-id") as string) ?? "0") : 0,
}; };
console.log(payload) console.log(payload)
@@ -48,14 +51,6 @@ export default function SubmissionInfoPage() {
body: JSON.stringify(payload), 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 // Allow any HTTP status
const id_response:IdResponse = await response.json(); const id_response:IdResponse = await response.json();
@@ -81,6 +76,15 @@ export default function SubmissionInfoPage() {
<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"/>
{/* I think this is Quat's job to figure this one out (to be set when someone clicks review(?)) */} {/* <TextField className="form-field" id="asset-version" label="Asset Version" variant="outlined"/> */} {/* I think this is Quat's job to figure this one out (to be set when someone clicks review(?)) */} {/* <TextField className="form-field" id="asset-version" label="Asset Version" variant="outlined"/> */}
<GameSelection game={game} setGame={setGame} /> <GameSelection game={game} setGame={setGame} />
<FormControl>
<FormControlLabel control={<Checkbox sx={{
color: "#646464",
'&.Mui-checked': {
color: "#66BB6A",
},
}} onChange={(e) => setIsFixingMap(e.target.checked)} />} label="Fixing an Existing Map?" />
</FormControl>
<TextField className="form-field" id="target-asset-id" name="target-asset-id" label="Target Asset ID (group model)" 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",

View File

@@ -20,12 +20,9 @@ interface SubmissionInfo {
readonly Submitter: number, readonly Submitter: number,
readonly AssetID: number, readonly AssetID: number,
readonly AssetVersion: number, readonly AssetVersion: number,
readonly ValidatedAssetID: number,
readonly ValidatedAssetVersion: number,
readonly Completed: boolean, readonly Completed: boolean,
readonly TargetAssetID: number, readonly TargetAssetID: number,
readonly StatusID: SubmissionStatus readonly StatusID: SubmissionStatus
readonly StatusMessage: string,
} }
function SubmissionStatusToString(submission_status: SubmissionStatus): string { function SubmissionStatusToString(submission_status: SubmissionStatus): string {

View File

@@ -1,13 +0,0 @@
import { NextRequest, NextResponse } from "next/server"
export const config = {
matcher: ["/api/:path*"],
}
export function middleware(request: NextRequest) {
if (!process.env.API_HOST) {
throw new Error("env variable \"API_HOST\" is not set")
}
const url = new URL(process.env.API_HOST + request.nextUrl.pathname.replace(/^\/api/, '') + request.nextUrl.search)
return NextResponse.rewrite(url, { request })
}