Compare commits

..

3 Commits

Author SHA1 Message Date
89b3ba44b5 use staging 2024-12-30 05:08:41 -08:00
706f5bad3f run auth with debug 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
image: argoproj/argocd:latest
commands:
- argocd login --grpc-web cd.stricity.com --username $USERNAME --password $PASSWORD
- argocd app --grpc-web set ${DRONE_BRANCH}-maps-service --kustomize-image registry.itzana.me/strafesnet/maptest-api:${DRONE_BRANCH}-${DRONE_BUILD_NUMBER}
- argocd app --grpc-web set ${DRONE_BRANCH}-maps-service --kustomize-image registry.itzana.me/strafesnet/maptest-frontend:${DRONE_BRANCH}-${DRONE_BUILD_NUMBER}
- argocd app --grpc-web set ${DRONE_BRANCH}-maps-service --kustomize-image registry.itzana.me/strafesnet/maptest-validator:${DRONE_BRANCH}-${DRONE_BUILD_NUMBER}
- echo "Deploy!" # Not going to do actually do this until
environment:
USERNAME:
from_secret: ARGO_USER
@@ -86,6 +83,6 @@ steps:
- staging
---
kind: signature
hmac: 1162b329a9cad12b4c5db0ccf8b8998072b0de9279326f76a493fd0af6794095
hmac: 9958fd5b01af1ebcc75f7277fe71eb5336b899445c359cecf1b14e83b3d05059
...

3
.gitignore vendored
View File

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

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,6 @@ import (
"github.com/go-faster/errors"
"github.com/go-faster/jx"
"github.com/ogen-go/ogen/json"
"github.com/ogen-go/ogen/validate"
)
@@ -327,215 +326,6 @@ func (s *OptString) UnmarshalJSON(data []byte) error {
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.
func (s *Script) Encode(e *jx.Encoder) {
e.ObjStart()
@@ -562,22 +352,17 @@ func (s *Script) encodeFields(e *jx.Encoder) {
e.Str(s.Source)
}
{
e.FieldStart("ResourceType")
e.Int32(s.ResourceType)
}
{
e.FieldStart("ResourceID")
e.Int64(s.ResourceID)
e.FieldStart("SubmissionID")
e.Int64(s.SubmissionID)
}
}
var jsonFieldsNameOfScript = [6]string{
var jsonFieldsNameOfScript = [5]string{
0: "ID",
1: "Name",
2: "Hash",
3: "Source",
4: "ResourceType",
5: "ResourceID",
4: "SubmissionID",
}
// Decode decodes Script from json.
@@ -637,29 +422,17 @@ func (s *Script) Decode(d *jx.Decoder) error {
}(); err != nil {
return errors.Wrap(err, "decode field \"Source\"")
}
case "ResourceType":
case "SubmissionID":
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 {
v, err := d.Int64()
s.ResourceID = int64(v)
s.SubmissionID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
return errors.Wrap(err, "decode field \"SubmissionID\"")
}
default:
return d.Skip()
@@ -671,7 +444,7 @@ func (s *Script) Decode(d *jx.Decoder) error {
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00111111,
0b00011111,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// 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.FieldStart("ResourceType")
e.Int32(s.ResourceType)
}
{
if s.ResourceID.Set {
e.FieldStart("ResourceID")
s.ResourceID.Encode(e)
if s.SubmissionID.Set {
e.FieldStart("SubmissionID")
s.SubmissionID.Encode(e)
}
}
}
var jsonFieldsNameOfScriptCreate = [4]string{
var jsonFieldsNameOfScriptCreate = [3]string{
0: "Name",
1: "Source",
2: "ResourceType",
3: "ResourceID",
2: "SubmissionID",
}
// Decode decodes ScriptCreate from json.
@@ -786,27 +554,15 @@ func (s *ScriptCreate) Decode(d *jx.Decoder) error {
}(); err != nil {
return errors.Wrap(err, "decode field \"Source\"")
}
case "ResourceType":
requiredBitSet[0] |= 1 << 2
case "SubmissionID":
if err := func() error {
v, err := d.Int32()
s.ResourceType = int32(v)
if err != nil {
s.SubmissionID.Reset()
if err := s.SubmissionID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceType\"")
}
case "ResourceID":
if err := func() error {
s.ResourceID.Reset()
if err := s.ResourceID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
return errors.Wrap(err, "decode field \"SubmissionID\"")
}
default:
return d.Skip()
@@ -818,7 +574,7 @@ func (s *ScriptCreate) Decode(d *jx.Decoder) error {
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00000111,
0b00000011,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// 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 {
e.FieldStart("ResourceType")
s.ResourceType.Encode(e)
}
}
{
if s.ResourceID.Set {
e.FieldStart("ResourceID")
s.ResourceID.Encode(e)
if s.SubmissionID.Set {
e.FieldStart("SubmissionID")
s.SubmissionID.Encode(e)
}
}
}
var jsonFieldsNameOfScriptUpdate = [5]string{
var jsonFieldsNameOfScriptUpdate = [4]string{
0: "ID",
1: "Name",
2: "Source",
3: "ResourceType",
4: "ResourceID",
3: "SubmissionID",
}
// Decode decodes ScriptUpdate from json.
@@ -1376,25 +1125,15 @@ func (s *ScriptUpdate) Decode(d *jx.Decoder) error {
}(); err != nil {
return errors.Wrap(err, "decode field \"Source\"")
}
case "ResourceType":
case "SubmissionID":
if err := func() error {
s.ResourceType.Reset()
if err := s.ResourceType.Decode(d); err != nil {
s.SubmissionID.Reset()
if err := s.SubmissionID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceType\"")
}
case "ResourceID":
if err := func() error {
s.ResourceID.Reset()
if err := s.ResourceID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
return errors.Wrap(err, "decode field \"SubmissionID\"")
}
default:
return d.Skip()
@@ -1502,19 +1241,19 @@ func (s *Submission) encodeFields(e *jx.Encoder) {
e.Bool(s.Completed)
}
{
if s.UploadedAssetID.Set {
e.FieldStart("UploadedAssetID")
s.UploadedAssetID.Encode(e)
e.FieldStart("SubmissionType")
e.Int32(s.SubmissionType)
}
{
if s.TargetAssetID.Set {
e.FieldStart("TargetAssetID")
s.TargetAssetID.Encode(e)
}
}
{
e.FieldStart("StatusID")
e.Int32(s.StatusID)
}
{
e.FieldStart("StatusMessage")
e.Str(s.StatusMessage)
}
}
var jsonFieldsNameOfSubmission = [13]string{
@@ -1528,9 +1267,9 @@ var jsonFieldsNameOfSubmission = [13]string{
7: "AssetID",
8: "AssetVersion",
9: "Completed",
10: "UploadedAssetID",
11: "StatusID",
12: "StatusMessage",
10: "SubmissionType",
11: "TargetAssetID",
12: "StatusID",
}
// Decode decodes Submission from json.
@@ -1662,18 +1401,30 @@ func (s *Submission) Decode(d *jx.Decoder) error {
}(); err != nil {
return errors.Wrap(err, "decode field \"Completed\"")
}
case "UploadedAssetID":
case "SubmissionType":
requiredBitSet[1] |= 1 << 2
if err := func() error {
s.UploadedAssetID.Reset()
if err := s.UploadedAssetID.Decode(d); err != nil {
v, err := d.Int32()
s.SubmissionType = int32(v)
if err != nil {
return err
}
return 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":
requiredBitSet[1] |= 1 << 3
requiredBitSet[1] |= 1 << 4
if err := func() error {
v, err := d.Int32()
s.StatusID = int32(v)
@@ -1684,18 +1435,6 @@ func (s *Submission) Decode(d *jx.Decoder) error {
}(); err != nil {
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:
return d.Skip()
}
@@ -1707,7 +1446,7 @@ func (s *Submission) Decode(d *jx.Decoder) error {
var failures []validate.FieldError
for i, mask := range [2]uint8{
0b11111111,
0b00011011,
0b00010111,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// 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.Int64(s.AssetVersion)
}
{
if s.TargetAssetID.Set {
e.FieldStart("TargetAssetID")
s.TargetAssetID.Encode(e)
}
}
}
var jsonFieldsNameOfSubmissionCreate = [5]string{
var jsonFieldsNameOfSubmissionCreate = [6]string{
0: "DisplayName",
1: "Creator",
2: "GameID",
3: "AssetID",
4: "AssetVersion",
5: "TargetAssetID",
}
// Decode decodes SubmissionCreate from json.
@@ -1861,6 +1607,16 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
}(); err != nil {
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:
return d.Skip()
}
@@ -1916,133 +1672,3 @@ func (s *SubmissionCreate) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
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
const (
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject"
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
ActionSubmissionRetryValidateOperation OperationName = "ActionSubmissionRetryValidate"
ActionSubmissionRevokeOperation OperationName = "ActionSubmissionRevoke"
ActionSubmissionSubmitOperation OperationName = "ActionSubmissionSubmit"
ActionSubmissionTriggerUploadOperation OperationName = "ActionSubmissionTriggerUpload"
ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
@@ -26,10 +23,6 @@ const (
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"
ListSubmissionsOperation OperationName = "ListSubmissions"
ReleaseSubmissionsOperation OperationName = "ReleaseSubmissions"
SessionRolesOperation OperationName = "SessionRoles"
SessionUserOperation OperationName = "SessionUser"
SessionValidateOperation OperationName = "SessionValidate"
SetSubmissionCompletedOperation OperationName = "SetSubmissionCompleted"
UpdateScriptOperation OperationName = "UpdateScript"
UpdateScriptPolicyOperation OperationName = "UpdateScriptPolicy"

View File

@@ -15,72 +15,6 @@ import (
"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.
type ActionSubmissionRejectParams struct {
// The unique identifier for a submission.
@@ -213,72 +147,6 @@ func decodeActionSubmissionRequestChangesParams(args [1]string, argsEscaped bool
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.
type ActionSubmissionRevokeParams struct {
// The unique identifier for a submission.
@@ -543,72 +411,6 @@ func decodeActionSubmissionTriggerValidateParams(args [1]string, argsEscaped boo
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.
type DeleteScriptParams struct {
// The unique identifier for a script.
@@ -1038,7 +840,7 @@ func decodeListScriptPolicyParams(args [0]string, argsEscaped bool, r *http.Requ
return err
}
} else {
return err
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
@@ -1091,7 +893,7 @@ func decodeListScriptPolicyParams(args [0]string, argsEscaped bool, r *http.Requ
return err
}
} else {
return err
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
@@ -1359,7 +1161,7 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
return err
}
} else {
return err
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
@@ -1412,7 +1214,7 @@ func decodeListScriptsParams(args [0]string, argsEscaped bool, r *http.Request)
return err
}
} else {
return err
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
@@ -1665,7 +1467,6 @@ type ListSubmissionsParams struct {
DisplayName OptString
Creator OptString
GameID OptInt32
Sort OptInt32
}
func unpackListSubmissionsParams(packed middleware.Parameters) (params ListSubmissionsParams) {
@@ -1710,15 +1511,6 @@ func unpackListSubmissionsParams(packed middleware.Parameters) (params ListSubmi
params.GameID = v.(OptInt32)
}
}
{
key := middleware.ParameterKey{
Name: "Sort",
In: "query",
}
if v, ok := packed[key]; ok {
params.Sort = v.(OptInt32)
}
}
return params
}
@@ -1767,7 +1559,7 @@ func decodeListSubmissionsParams(args [0]string, argsEscaped bool, r *http.Reque
return err
}
} else {
return err
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
@@ -1820,7 +1612,7 @@ func decodeListSubmissionsParams(args [0]string, argsEscaped bool, r *http.Reque
return err
}
} else {
return err
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
@@ -1999,47 +1791,6 @@ func decodeListSubmissionsParams(args [0]string, argsEscaped bool, r *http.Reque
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
}
@@ -2347,7 +2098,7 @@ func decodeUpdateSubmissionModelParams(args [1]string, argsEscaped bool, r *http
return err
}
} else {
return err
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
@@ -2383,7 +2134,7 @@ func decodeUpdateSubmissionModelParams(args [1]string, argsEscaped bool, r *http
return err
}
} else {
return err
return validate.ErrFieldRequired
}
return 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) (
req *ScriptUpdate,
close func() error,

View File

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

View File

@@ -15,57 +15,6 @@ import (
"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) {
switch resp.StatusCode {
case 204:
@@ -168,57 +117,6 @@ func decodeActionSubmissionRequestChangesResponse(resp *http.Response) (res *Act
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) {
switch resp.StatusCode {
case 204:
@@ -423,57 +321,6 @@ func decodeActionSubmissionTriggerValidateResponse(resp *http.Response) (res *Ac
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) {
switch resp.StatusCode {
case 201:
@@ -1452,317 +1299,6 @@ func decodeListSubmissionsResponse(resp *http.Response) (res []Submission, _ err
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) {
switch resp.StatusCode {
case 204:

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -53,7 +53,7 @@ func (env *Scripts) Update(ctx context.Context, id int64, values datastore.Optio
}
// the update can only occur if the status matches one of the provided values.
func (env *Scripts) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.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 == gorm.ErrRecordNotFound {
return datastore.ErrNotExist

View File

@@ -54,7 +54,7 @@ func (env *Submissions) Update(ctx context.Context, id int64, values datastore.O
}
// the update can only occur if the status matches one of the provided values.
func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.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 == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
@@ -67,7 +67,7 @@ func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, status
// the update can only occur if the status matches one of the provided values.
// returns the updated value
func (env *Submissions) IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.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
result := env.db.Model(&submission).
Clauses(clause.Returning{}).
@@ -99,32 +99,9 @@ func (env *Submissions) Delete(ctx context.Context, id int64) error {
return nil
}
func (env *Submissions) List(ctx context.Context, filters datastore.OptionalMap, page model.Page, 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
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 {
if err := env.db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&maps).Error; err != nil {
return nil, err
}

View File

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

View File

@@ -128,10 +128,6 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
Name: "SubmissionID",
In: "path",
}: params.SubmissionID,
{
Name: "StatusMessage",
In: "query",
}: params.StatusMessage,
},
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.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@@ -282,9 +427,9 @@ func (s *Server) handleActionSubmissionUploadedRequest(args [1]string, argsEscap
In: "path",
}: params.SubmissionID,
{
Name: "UploadedAssetID",
Name: "TargetAssetID",
In: "query",
}: params.UploadedAssetID,
}: params.TargetAssetID,
},
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
func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
// POST /submissions/{SubmissionID}/model
func (s *Server) handleUpdateSubmissionModelRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("updateSubmissionValidatedModel"),
otelogen.OperationID("updateSubmissionModel"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/validated-model"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/model"),
}
// 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...),
serverSpanKind,
)
@@ -1336,11 +1481,11 @@ func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, arg
}
err error
opErrContext = ogenerrors.OperationContext{
Name: UpdateSubmissionValidatedModelOperation,
ID: "updateSubmissionValidatedModel",
Name: UpdateSubmissionModelOperation,
ID: "updateSubmissionModel",
}
)
params, err := decodeUpdateSubmissionValidatedModelParams(args, argsEscaped, r)
params, err := decodeUpdateSubmissionModelParams(args, argsEscaped, r)
if err != nil {
err = &ogenerrors.DecodeParamsError{
OperationContext: opErrContext,
@@ -1351,13 +1496,13 @@ func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, arg
return
}
var response *UpdateSubmissionValidatedModelNoContent
var response *UpdateSubmissionModelNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: UpdateSubmissionValidatedModelOperation,
OperationSummary: "Update validated model",
OperationID: "updateSubmissionValidatedModel",
OperationName: UpdateSubmissionModelOperation,
OperationSummary: "Update model following role restrictions",
OperationID: "updateSubmissionModel",
Body: nil,
Params: middleware.Parameters{
{
@@ -1365,21 +1510,21 @@ func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, arg
In: "path",
}: params.SubmissionID,
{
Name: "ValidatedModelID",
Name: "ModelID",
In: "query",
}: params.ValidatedModelID,
}: params.ModelID,
{
Name: "ValidatedModelVersion",
Name: "VersionID",
In: "query",
}: params.ValidatedModelVersion,
}: params.VersionID,
},
Raw: r,
}
type (
Request = struct{}
Params = UpdateSubmissionValidatedModelParams
Response = *UpdateSubmissionValidatedModelNoContent
Params = UpdateSubmissionModelParams
Response = *UpdateSubmissionModelNoContent
)
response, err = middleware.HookMiddleware[
Request,
@@ -1388,14 +1533,14 @@ func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, arg
](
m,
mreq,
unpackUpdateSubmissionValidatedModelParams,
unpackUpdateSubmissionModelParams,
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
},
)
} else {
err = s.h.UpdateSubmissionValidatedModel(ctx, params)
err = s.h.UpdateSubmissionModel(ctx, params)
}
if err != nil {
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {
@@ -1414,7 +1559,7 @@ func (s *Server) handleUpdateSubmissionValidatedModelRequest(args [1]string, arg
return
}
if err := encodeUpdateSubmissionValidatedModelResponse(response, w, span); err != nil {
if err := encodeUpdateSubmissionModelResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)

View File

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

View File

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

View File

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

View File

@@ -66,6 +66,57 @@ func decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSub
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) {
switch resp.StatusCode {
case 204:
@@ -660,11 +711,11 @@ func decodeListScriptsResponse(resp *http.Response) (res []Script, _ 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 {
case 204:
// Code 204.
return &UpdateSubmissionValidatedModelNoContent{}, nil
return &UpdateSubmissionModelNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {

View File

@@ -20,6 +20,13 @@ func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNo
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 {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@@ -112,7 +119,7 @@ func encodeListScriptsResponse(response []Script, w http.ResponseWriter, span tr
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)
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] {
case '/': // Prefix: "/s"
origElem := elem
if l := len("/s"); len(elem) >= l && elem[0:l] == "/s" {
elem = elem[l:]
} else {
@@ -62,7 +62,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
switch elem[0] {
case 'c': // Prefix: "cript"
origElem := elem
if l := len("cript"); len(elem) >= l && elem[0:l] == "cript" {
elem = elem[l:]
} else {
@@ -74,7 +74,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
switch elem[0] {
case '-': // Prefix: "-policy"
origElem := elem
if l := len("-policy"); len(elem) >= l && elem[0:l] == "-policy" {
elem = elem[l:]
} else {
@@ -95,8 +95,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
elem = origElem
case 's': // Prefix: "s"
origElem := elem
if l := len("s"); len(elem) >= l && elem[0:l] == "s" {
elem = elem[l:]
} else {
@@ -117,7 +118,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
switch elem[0] {
case '/': // Prefix: "/"
origElem := elem
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:]
} else {
@@ -125,11 +126,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// Param: "ScriptID"
// Leaf parameter, slashes are prohibited
idx := strings.IndexByte(elem, '/')
if idx >= 0 {
break
}
// Leaf parameter
args[0] = elem
elem = ""
@@ -147,12 +144,15 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
elem = origElem
}
elem = origElem
}
elem = origElem
case 'u': // Prefix: "ubmissions/"
origElem := elem
if l := len("ubmissions/"); len(elem) >= l && elem[0:l] == "ubmissions/" {
elem = elem[l:]
} else {
@@ -173,7 +173,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
switch elem[0] {
case '/': // Prefix: "/"
origElem := elem
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:]
} else {
@@ -184,89 +184,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break
}
switch elem[0] {
case 's': // Prefix: "status/validator-"
if l := len("status/validator-"); len(elem) >= l && elem[0:l] == "status/validator-" {
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" {
case 'm': // Prefix: "model"
origElem := elem
if l := len("model"); len(elem) >= l && elem[0:l] == "model" {
elem = elem[l:]
} else {
break
@@ -276,7 +196,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Leaf node.
switch r.Method {
case "POST":
s.handleUpdateSubmissionValidatedModelRequest([1]string{
s.handleUpdateSubmissionModelRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
@@ -286,12 +206,138 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
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)
@@ -373,7 +419,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
switch elem[0] {
case '/': // Prefix: "/s"
origElem := elem
if l := len("/s"); len(elem) >= l && elem[0:l] == "/s" {
elem = elem[l:]
} else {
@@ -385,7 +431,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
switch elem[0] {
case 'c': // Prefix: "cript"
origElem := elem
if l := len("cript"); len(elem) >= l && elem[0:l] == "cript" {
elem = elem[l:]
} else {
@@ -397,7 +443,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
switch elem[0] {
case '-': // Prefix: "-policy"
origElem := elem
if l := len("-policy"); len(elem) >= l && elem[0:l] == "-policy" {
elem = elem[l:]
} else {
@@ -428,8 +474,9 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
}
elem = origElem
case 's': // Prefix: "s"
origElem := elem
if l := len("s"); len(elem) >= l && elem[0:l] == "s" {
elem = elem[l:]
} else {
@@ -460,7 +507,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
switch elem[0] {
case '/': // Prefix: "/"
origElem := elem
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:]
} else {
@@ -468,11 +515,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
// Param: "ScriptID"
// Leaf parameter, slashes are prohibited
idx := strings.IndexByte(elem, '/')
if idx >= 0 {
break
}
// Leaf parameter
args[0] = 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/"
origElem := elem
if l := len("ubmissions/"); len(elem) >= l && elem[0:l] == "ubmissions/" {
elem = elem[l:]
} else {
@@ -518,7 +564,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
switch elem[0] {
case '/': // Prefix: "/"
origElem := elem
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:]
} else {
@@ -529,95 +575,9 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break
}
switch elem[0] {
case 's': // Prefix: "status/validator-"
if l := len("status/validator-"); len(elem) >= l && elem[0:l] == "status/validator-" {
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" {
case 'm': // Prefix: "model"
origElem := elem
if l := len("model"); len(elem) >= l && elem[0:l] == "model" {
elem = elem[l:]
} else {
break
@@ -627,10 +587,10 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
// Leaf node.
switch method {
case "POST":
r.name = UpdateSubmissionValidatedModelOperation
r.summary = "Update validated model"
r.operationID = "updateSubmissionValidatedModel"
r.pathPattern = "/submissions/{SubmissionID}/validated-model"
r.name = UpdateSubmissionModelOperation
r.summary = "Update model following role restrictions"
r.operationID = "updateSubmissionModel"
r.pathPattern = "/submissions/{SubmissionID}/model"
r.args = args
r.count = 1
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,145 +14,34 @@ var (
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 (
// has ScriptWrite
RoleQuat GroupRole = 255
RoleItzaname GroupRole = 254
RoleStagingDeveloper GroupRole = 240
RolesAll Roles = RolesScriptWrite|RolesSubmissionRelease|RolesMapUpload|RolesMapReview|RolesMapDownload
// has SubmissionUpload
RoleMapAdmin GroupRole = 128
RolesMapAdmin Roles = RolesSubmissionRelease|RolesMapUpload|RolesMapReview|RolesMapDownload
RoleQuat int32 = 255
// has SubmissionPublish
RoleMapAdmin int32 = 128
// has SubmissionReview
RoleMapCouncil GroupRole = 64
RolesMapCouncil Roles = RolesMapReview|RolesMapUpload|RolesMapDownload
// access to downloading maps
RoleMapAccess GroupRole = 32
RolesMapAccess Roles = RolesMapDownload
RoleMapCouncil int32 = 64
)
type UserInfoHandle struct {
// Would love to know a better way to do this
svc *SecurityHandler
ctx *context.Context
sessionId string
type Roles struct {
// human roles
SubmissionRelease bool
SubmissionReview bool
ScriptWrite bool
// Thumbnail bool
// MapDownload
// automated roles
Maptest bool
}
type UserInfo struct {
UserID uint64
Username string
AvatarURL string
Roles Roles
UserID uint64
}
func (usr UserInfoHandle) GetUserInfo() (userInfo UserInfo, err error) {
session, err := usr.svc.Client.GetSessionUser(*usr.ctx, &auth.IdMessage{
SessionID: usr.sessionId,
})
if err != nil {
return userInfo, err
}
userInfo.UserID = session.UserID
userInfo.Username = session.Username
userInfo.AvatarURL = session.AvatarURL
return userInfo, nil
}
func (usr UserInfoHandle) GetUserID() (uint64, error) {
session, err := usr.svc.Client.GetSessionUser(*usr.ctx, &auth.IdMessage{
SessionID: usr.sessionId,
})
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
func (usr UserInfo) IsSubmitter(submitter uint64) bool {
return usr.UserID == submitter
}
type SecurityHandler struct {
@@ -165,10 +54,48 @@ func (svc SecurityHandler) HandleCookieAuth(ctx context.Context, operationName a
return nil, ErrMissingSessionID
}
newCtx := context.WithValue(ctx, "UserInfo", UserInfoHandle{
svc: &svc,
ctx: &ctx,
sessionId: sessionId,
session, err := svc.Client.GetSessionUser(ctx, &auth.IdMessage{
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

View File

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

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

View File

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

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]
name = "maps-validation"
version = "0.1.1"
version = "0.1.0"
edition = "2021"
[dependencies]
submissions-api = { path = "api", features = ["internal"], default-features = false, registry = "strafesnet" }
async-nats = "0.40.0"
submissions-api = { path = "api", features = ["internal"], registry = "strafesnet" }
async-nats = "0.38.0"
futures = "0.3.31"
rbx_asset = { version = "0.2.5", 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]
name = "submissions-api"
version = "0.6.1"
version = "0.3.0"
edition = "2021"
publish = ["strafesnet"]
repository = "https://git.itzana.me/StrafesNET/maps-service"
@@ -18,6 +18,6 @@ serde_repr = "0.1.19"
url = "2"
[features]
default = ["external"]
default = ["internal"]
internal = []
external = []

View File

@@ -14,7 +14,8 @@ pub struct 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{
base_url,
client:{

View File

@@ -31,47 +31,6 @@ impl Context{
).await.map_err(Error::Response)?
.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>{
let url_raw=format!("{}/scripts",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
@@ -131,14 +90,14 @@ impl Context{
).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest)
}
pub async fn update_submission_validated_model(&self,config:UpdateSubmissionModelRequest)->Result<(),Error>{
let url_raw=format!("{}/submissions/{}/validated-model",self.0.base_url,config.SubmissionID);
pub async fn update_submission_model(&self,config:UpdateSubmissionModelRequest)->Result<(),Error>{
let url_raw=format!("{}/submissions/{}/model",self.0.base_url,config.SubmissionID);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
url.query_pairs_mut()
.append_pair("ValidatedModelID",config.ModelID.to_string().as_str())
.append_pair("ValidatedModelVersion",config.ModelVersion.to_string().as_str());
.append_pair("ModelID",config.ModelID.to_string().as_str())
.append_pair("ModelVersion",config.ModelVersion.to_string().as_str());
}
response_ok(
@@ -151,9 +110,9 @@ impl Context{
let url_raw=format!("{}/submissions/{}/status/validator-uploaded",self.0.base_url,config.SubmissionID);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
if let Some(target_asset_id)=config.TargetAssetID{
url.query_pairs_mut()
.append_pair("UploadedAssetID",config.UploadedAssetID.to_string().as_str());
.append_pair("TargetAssetID",target_asset_id.to_string().as_str());
}
response_ok(
self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
@@ -161,20 +120,7 @@ impl Context{
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_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);
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
#[derive(Clone,Copy,serde::Serialize,serde::Deserialize)]
pub struct ScriptPolicyID(pub(crate)i64);
#[allow(nonstandard_style)]
@@ -70,7 +70,7 @@ pub struct GetScriptRequest{
pub ScriptID:ScriptID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
#[derive(serde::Serialize)]
pub struct GetScriptsRequest<'a>{
pub Page:u32,
pub Limit:u32,
@@ -83,12 +83,11 @@ pub struct GetScriptsRequest<'a>{
#[serde(skip_serializing_if="Option::is_none")]
pub SubmissionID:Option<i64>,
}
#[derive(Clone,Copy,Debug)]
pub struct HashRequest<'a>{
pub hash:&'a str,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
#[derive(serde::Deserialize)]
pub struct ScriptResponse{
pub ID:ScriptID,
pub Name:String,
@@ -96,28 +95,21 @@ pub struct ScriptResponse{
pub Source:String,
pub SubmissionID:i64,
}
#[derive(Clone,Debug,serde::Serialize)]
pub enum ResourceType{
Unknown=0,
Mapfix=1,
Submission=2,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
#[derive(serde::Serialize)]
pub struct CreateScriptRequest<'a>{
pub Name:&'a str,
pub Source:&'a str,
pub ResourceType:ResourceType,
#[serde(skip_serializing_if="Option::is_none")]
pub ResourceID:Option<i64>,
pub SubmissionID:Option<i64>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
#[derive(serde::Deserialize)]
pub struct ScriptIDResponse{
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)]
pub enum Policy{
None=0, // not yet reviewed
@@ -128,7 +120,7 @@ pub enum Policy{
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
#[derive(serde::Serialize)]
pub struct GetScriptPoliciesRequest<'a>{
pub Page:u32,
pub Limit:u32,
@@ -140,7 +132,7 @@ pub struct GetScriptPoliciesRequest<'a>{
pub Policy:Option<Policy>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
#[derive(serde::Deserialize)]
pub struct ScriptPolicyResponse{
pub ID:ScriptPolicyID,
pub FromScriptHash:String,
@@ -148,20 +140,20 @@ pub struct ScriptPolicyResponse{
pub Policy:Policy
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
#[derive(serde::Serialize)]
pub struct CreateScriptPolicyRequest{
pub FromScriptID:ScriptID,
pub ToScriptID:ScriptID,
pub Policy:Policy,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
#[derive(serde::Deserialize)]
pub struct ScriptPolicyIDResponse{
pub ID:ScriptPolicyID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
#[derive(serde::Serialize)]
pub struct UpdateScriptPolicyRequest{
pub ID:ScriptPolicyID,
#[serde(skip_serializing_if="Option::is_none")]
@@ -173,7 +165,6 @@ pub struct UpdateScriptPolicyRequest{
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct UpdateSubmissionModelRequest{
pub SubmissionID:i64,
pub ModelID:u64,
@@ -181,42 +172,9 @@ pub struct UpdateSubmissionModelRequest{
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionUploadedRequest{
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);
#[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;
mod message_handler;
mod nats_types;
mod types;
mod uploader;
mod upload_mapfix;
mod upload_submission;
mod validator;
mod validate_mapfix;
mod validate_submission;
mod publish_new;
mod publish_fix;
mod message_handler;
#[allow(dead_code)]
#[derive(Debug)]
@@ -56,7 +52,7 @@ async fn main()->Result<(),StartupError>{
.get_or_create_consumer("validation",async_nats::jetstream::consumer::pull::Config{
name:Some("validation".to_owned()),
durable_name:Some("validation".to_owned()),
filter_subject:"maptest.>".to_owned(),
filter_subject:"maptest.submissions.>".to_owned(),
..Default::default()
}).await.map_err(StartupError::NatsConsumer)?
.messages().await.map_err(StartupError::NatsStream)

View File

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

View File

@@ -6,7 +6,7 @@
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct ValidateSubmissionRequest{
pub struct ValidateRequest{
// submission_id is passed back in the response message
pub SubmissionID:i64,
pub ModelID:u64,
@@ -14,20 +14,10 @@ pub struct ValidateSubmissionRequest{
pub ValidatedModelID:Option<u64>,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct ValidateMapfixRequest{
// submission_id is passed back in the response message
pub MapfixID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
pub ValidatedModelID:Option<u64>,
}
// Create a new map
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct UploadSubmissionRequest{
pub struct PublishNewRequest{
pub SubmissionID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
@@ -36,8 +26,8 @@ pub struct UploadSubmissionRequest{
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct UploadMapfixRequest{
pub MapfixID:i64,
pub struct PublishFixRequest{
pub SubmissionID:i64,
pub ModelID:u64,
pub ModelVersion: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 submissions_api::types::ResourceType;
use crate::types::ResourceID;
use crate::nats_types::ValidateRequest;
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()
}
fn hash_source(source:&str)->String{
let mut hasher=siphasher::sip::SipHasher::new();
std::hash::Hasher::write(&mut hasher,source.as_bytes());
let hash=std::hash::Hasher::finish(&hasher);
format!("{:016x}",hash)
}
// #[allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug)]
pub enum ValidateError{
ScriptFlaggedIllegalKeyword(String),
ScriptBlocked(Option<submissions_api::types::ScriptID>),
ScriptNotYetReviewed(Option<submissions_api::types::ScriptID>),
ModelFileDownload(rbx_asset::cookie::GetError),
ModelFileDecode(ReadDomError),
ApiGetScriptPolicyFromHash(submissions_api::types::SingleItemError),
Flagged,
Blocked,
NotAllowed,
Get(rbx_asset::cookie::GetError),
ReadDom(ReadDomError),
ApiGetScriptPolicy(submissions_api::types::SingleItemError),
ApiGetScript(submissions_api::Error),
ApiCreateScript(submissions_api::Error),
ApiCreateScriptPolicy(submissions_api::Error),
ApiGetScriptFromHash(submissions_api::types::SingleItemError),
ApiUpdateMapfixModel(submissions_api::Error),
ApiUpdateSubmissionModel(submissions_api::Error),
ModelFileRootMustHaveOneChild,
ModelFileChildRefIsNil,
ModelFileEncode(rbx_binary::EncodeError),
AssetUpload(rbx_asset::cookie::UploadError),
AssetCreate(rbx_asset::cookie::CreateError),
ApiActionSubmissionValidate(submissions_api::Error),
WriteDom(rbx_binary::EncodeError),
Upload(rbx_asset::cookie::UploadError),
Create(rbx_asset::cookie::CreateError),
}
impl std::fmt::Display for ValidateError{
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{}
#[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(crate) roblox_cookie:rbx_asset::cookie::CookieContext,
pub(crate) api:submissions_api::internal::Context,
roblox_cookie:rbx_asset::cookie::CookieContext,
api:submissions_api::internal::Context,
}
impl Validator{
@@ -102,14 +62,36 @@ impl Validator{
}
}
pub async fn validate(&self,validate_info:ValidateRequest)->Result<(),ValidateError>{
let submission_id=validate_info.SubmissionID;
let validate_result=self.validate_inner(validate_info).await;
// update the submission depending on the result
match &validate_result{
Ok(())=>{
// update the submission model status to validated
self.api.action_submission_validated(
submissions_api::types::SubmissionID(submission_id)
).await.map_err(ValidateError::ApiActionSubmissionValidate)?;
},
Err(_)=>{
// update the submission model status to accepted
self.api.action_submission_accepted(
submissions_api::types::SubmissionID(submission_id)
).await.map_err(ValidateError::ApiActionSubmissionValidate)?;
},
}
validate_result
}
pub async fn validate_inner(&self,validate_info:ValidateRequest)->Result<(),ValidateError>{
// download map
let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:validate_info.ModelID,
version:Some(validate_info.ModelVersion),
}).await.map_err(ValidateError::ModelFileDownload)?;
}).await.map_err(ValidateError::Get)?;
// 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 */
@@ -122,14 +104,12 @@ impl Validator{
// check the source for illegal keywords
if source_has_illegal_keywords(source){
// immediately abort
// grab path to offending script
let path=get_partial_path(&dom,script);
return Err(ValidateError::ScriptFlaggedIllegalKeyword(path));
return Err(ValidateError::Flagged);
}
// associate a name and policy with the source code
// policy will be fetched from the database to replace the default policy
script_map.insert(source.clone(),NamePolicy{
name:get_partial_path(&dom,script),
name:script.name.clone(),
policy:Policy::None,
});
}
@@ -140,12 +120,14 @@ impl Validator{
futures::stream::iter(script_map.iter_mut().map(Ok))
.try_for_each_concurrent(Some(SCRIPT_CONCURRENCY),|(source,NamePolicy{policy,name})|async{
// get the hash
let 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
let script_policy=self.api.get_script_policy_from_hash(submissions_api::types::HashRequest{
hash:hash.as_str(),
}).await.map_err(ValidateError::ApiGetScriptPolicyFromHash)?;
hash:format!("{:016x}",hash).as_str(),
}).await.map_err(ValidateError::ApiGetScriptPolicy)?;
// write the policy to the script_map, fetching the replacement code if necessary
if let Some(script_policy)=script_policy{
@@ -162,17 +144,11 @@ impl Validator{
},
};
}else{
let (resource_type,resource_id)=match validate_info.ResourceID{
ResourceID::Mapfix(mapfix_id)=>(ResourceType::Mapfix,mapfix_id),
ResourceID::Submission(submission_id)=>(ResourceType::Submission,submission_id),
};
// upload the script
let script=self.api.create_script(submissions_api::types::CreateScriptRequest{
Name:name.as_str(),
Source:source.as_str(),
ResourceType:resource_type,
ResourceID:Some(resource_id),
SubmissionID:Some(validate_info.SubmissionID),
}).await.map_err(ValidateError::ApiCreateScript)?;
// 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(rbx_dom_weak::types::Variant::String(source))=script.properties.get_mut("Source"){
match script_map.get(source.as_str()).map(|p|&p.policy){
Some(Policy::Blocked)=>{
let hash=hash_source(source.as_str());
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)));
},
Some(Policy::Blocked)=>return Err(ValidateError::Blocked),
None
|Some(Policy::None)
=>{
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)));
},
=>return Err(ValidateError::NotAllowed),
Some(Policy::Allowed)=>(),
Some(Policy::Delete)=>{
modified=true;
@@ -230,10 +194,7 @@ impl Validator{
if modified{
// serialize model (slow!)
let mut data=Vec::new();
let &[map_ref]=dom.root().children()else{
return Err(ValidateError::ModelFileRootMustHaveOneChild);
};
rbx_binary::to_writer(&mut data,&dom,&[map_ref]).map_err(ValidateError::ModelFileEncode)?;
rbx_binary::to_writer(&mut data,&dom,dom.root().children()).map_err(ValidateError::WriteDom)?;
// upload a model lol
let model_id=if let Some(model_id)=validate_info.ValidatedModelID{
@@ -245,45 +206,29 @@ impl Validator{
ispublic:None,
allowComments:None,
groupId:None,
},data).await.map_err(ValidateError::AssetUpload)?;
},data).await.map_err(ValidateError::Upload)?;
response.AssetId
}else{
// grab the map instance from the map re
let Some(map_instance)=dom.get_by_ref(map_ref)else{
return Err(ValidateError::ModelFileChildRefIsNil);
};
// create new model
let response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
name:map_instance.name.clone(),
name:dom.root().name.clone(),
description:"".to_owned(),
ispublic:true,
allowComments:true,
groupId:None,
},data).await.map_err(ValidateError::AssetCreate)?;
},data).await.map_err(ValidateError::Create)?;
response.AssetId
};
match validate_info.ResourceID{
ResourceID::Mapfix(mapfix_id)=>{
// update the mapfix to use the validated model
self.api.update_mapfix_validated_model(submissions_api::types::UpdateMapfixModelRequest{
MapfixID:mapfix_id,
ModelID:model_id,
ModelVersion:1, //TODO
}).await.map_err(ValidateError::ApiUpdateMapfixModel)?;
},
ResourceID::Submission(submission_id)=>{
// update the submission to use the validated model
self.api.update_submission_validated_model(submissions_api::types::UpdateSubmissionModelRequest{
SubmissionID:submission_id,
ModelID:model_id,
ModelVersion:1, //TODO
}).await.map_err(ValidateError::ApiUpdateSubmissionModel)?;
},
}
}
// update the submission to use the validated model
self.api.update_submission_model(submissions_api::types::UpdateSubmissionModelRequest{
SubmissionID:validate_info.SubmissionID,
ModelID:model_id,
ModelVersion:1, //TODO
}).await.map_err(ValidateError::ApiUpdateSubmissionModel)?;
};
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>{
let mut scripts=std::vec::Vec::new();
recursive_collect_superclass(&mut scripts,dom,dom.root(),"LuaSourceContainer");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,10 +9,9 @@ interface CommentersProps {
}
interface CreatorAndReviewStatus {
asset_id: SubmissionInfo["AssetID"],
asset_id: SubmissionInfo["AssetID"],
creator: SubmissionInfo["DisplayName"],
review: SubmissionInfo["StatusID"],
status_message: SubmissionInfo["StatusMessage"],
comments: Comment[],
name: string
}

View File

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

View File

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

View File

@@ -1,7 +1,6 @@
import React from "react";
import Image from "next/image";
import Link from "next/link";
import { Rating } from "@mui/material";
interface SubmissionCardProps {
displayName: string;
@@ -15,26 +14,11 @@ export default function SubmissionCard(props: SubmissionCardProps) {
return (
<Link href={`/submissions/${props.id}`}>
<div className="submissionCard">
<div className="content">
<div className="map-image">
{/* TODO: Grab image of model */}
<Image height={200} width={200} priority={true} src="https://api.ic3.space/strafe/map-images/11222350808" style={{ width: `100%` }} alt={props.displayName} />
</div>
<div className="details">
<div className="header">
<span className="displayName">{props.displayName}</span>
<div className="rating">
<Rating value={props.rating} readOnly size="small" />
</div>
</div>
<div className="footer">
<div className="author">
<Image className="avatar" width={28} height={28} priority={true} src="https://api.ic3.space/strafe/map-images/11222350808" alt={props.author}/>
<span>{props.author}</span>
</div>
</div>
</div>
</div>
{/* TODO: Grab image of model */}
<Image height={200} width={200} priority={true} src="https://api.ic3.space/strafe/map-images/11222350808" style={{ width: `100%` }} alt={props.displayName} />
<h3>{props.displayName}</h3>
<p>By {props.author}</p>
<p> {props.rating}</p> {/* TODO: paste the star element from submission/1 page */}
</div>
</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'
import React, { useState, useEffect } from "react";
import { SubmissionInfo } from "../ts/Submission";
import React, { useState, useEffect } from 'react'
import { SubmissionInfo } from '../ts/Submission';
import { Grid2 as Grid } from '@mui/material';
import SubmissionCard from "./_card";
import SkeletonGrid from './_loading';
import Webpage from "@/app/_components/webpage";
import "./(styles)/page.scss";
export default function SubmissionInfoPage() {
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);
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(() => {
useEffect(() => { // needs to be client sided since server doesn't have a session, nextjs got mad at me for exporting an async function: (https://nextjs.org/docs/messages/no-async-client-component)
async function fetchSubmissions() {
const res = await fetch('/api/submissions?Page=1&Limit=100')
if (res.ok) {
setSubmissions(await res.json())
}
}
setTimeout(() => {
setTimeout(() => { // testing loading screen made by chatGerbertPT
fetchSubmissions()
}, 50);
}, 250);
}, [])
if (!submissions) {
return <Webpage>
<main>
Loading...
</main>
</Webpage>
}
if (submissions && submissions.length == 0) {
return <Webpage>
<main>
Submissions list is empty.
<main style={{ display: 'flex', justifyContent: 'center', padding: '1rem' }}>
<SkeletonGrid />
</main>
</Webpage>
}
@@ -63,48 +35,43 @@ export default function SubmissionInfoPage() {
return (
// TODO: Add filter settings & searchbar & page selector
<Webpage>
<main
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
padding: '1rem',
width: '100%',
maxWidth: '100vw',
boxSizing: 'border-box',
overflowX: 'hidden'
}}
>
<div className="pagination-dots">
{Array.from({ length: totalPages }).map((_, index) => (
<span
key={index}
className={`dot ${index === currentPage ? 'active' : ''}`}
onClick={() => setCurrentPage(index)}
></span>
<main style={{ display: 'flex', justifyContent: 'center', padding: '1rem' }}>
<Grid
container
spacing={2}
alignItems="center"
justifyContent="center"
style={{ maxWidth: 'calc(100vw - 100px)', margin: '0 auto' }}
>
{submissions.map((submission) => (
<Grid key={submission.ID}>
<SubmissionCard
id={submission.ID}
assetId={submission.AssetID}
displayName={submission.DisplayName}
author={submission.Creator}
rating={submission.StatusID}
/>
</Grid>
))}
</div>
<div className="pagination">
<button onClick={prevPage} disabled={currentPage === 0}>&lt;</button>
<span>
Page {currentPage + 1} of {totalPages}
</span>
<button onClick={nextPage} disabled={currentPage === totalPages - 1}>&gt;</button>
</div>
<div className="grid">
{currentCards.map((submission) => (
<SubmissionCard
key={submission.ID}
id={submission.ID}
assetId={submission.AssetID}
displayName={submission.DisplayName}
author={submission.Creator}
rating={submission.StatusID}
/>
))}
</div>
</Grid>
</main>
</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,
padding: '10px 26px 10px 12px',
transition: theme.transitions.create(['border-color', 'box-shadow']),
// Use the system font instead of the default Roboto font.
fontFamily: [
'-apple-system',
'BlinkMacSystemFont',

View File

@@ -1,6 +1,6 @@
"use client"
import { Button, TextField } from "@mui/material"
import { FormControl, FormControlLabel, Button, TextField, Checkbox } from "@mui/material"
import GameSelection from "./_game";
import SendIcon from '@mui/icons-material/Send';
@@ -15,6 +15,7 @@ interface SubmissionPayload {
GameID: number;
AssetID: number;
AssetVersion: number;
TargetAssetID: number;
}
interface IdResponse {
ID: number;
@@ -22,6 +23,7 @@ interface IdResponse {
export default function SubmissionInfoPage() {
const [game, setGame] = useState(1);
const [isFixingMap, setIsFixingMap] = useState(false);
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
@@ -35,6 +37,7 @@ export default function SubmissionInfoPage() {
GameID: game,
AssetID: Number((formData.get("asset-id") as string) ?? "0"),
AssetVersion: 0,
TargetAssetID: isFixingMap ? Number((formData.get("target-asset-id") as string) ?? "0") : 0,
};
console.log(payload)
@@ -48,14 +51,6 @@ export default function SubmissionInfoPage() {
body: JSON.stringify(payload),
});
// Check if the HTTP request was successful
if (!response.ok) {
const errorDetails = await response.text();
// Throw an error with detailed information
throw new Error(`HTTP error! status: ${response.status}, details: ${errorDetails}`);
}
// Allow any HTTP status
const id_response:IdResponse = await response.json();
@@ -81,6 +76,15 @@ export default function SubmissionInfoPage() {
<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"/> */}
<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>
<Button type="submit" variant="contained" startIcon={<SendIcon/>} sx={{
width: "400px",

View File

@@ -20,12 +20,9 @@ interface SubmissionInfo {
readonly Submitter: number,
readonly AssetID: number,
readonly AssetVersion: number,
readonly ValidatedAssetID: number,
readonly ValidatedAssetVersion: number,
readonly Completed: boolean,
readonly TargetAssetID: number,
readonly StatusID: SubmissionStatus
readonly StatusMessage: 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 })
}