Compare commits

..

2 Commits
master ... nats

Author SHA1 Message Date
eb9eeb518f something like this 2024-12-02 23:29:13 -08:00
371271d084 nasty 2024-12-02 23:29:13 -08:00
83 changed files with 2645 additions and 11638 deletions

View File

@ -1,36 +0,0 @@
# Stage 1: Build
FROM docker.io/golang:1.23 AS builder
# Set the working directory in the container
WORKDIR /app
# Copy go.mod and go.sum files
COPY go.mod go.sum ./
# Download dependencies
RUN --mount=type=secret,id=netrc,dst=/root/.netrc go mod download
# Copy the entire project
COPY . .
# Build the Go application
RUN CGO_ENABLED=0 GOOS=linux go build -o service ./cmd/maps-service/service.go
# Stage 2: Run
FROM alpine
# Set up a non-root user for security
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# Set the working directory in the container
WORKDIR /home/appuser
# Copy the built application from the builder stage
COPY --from=builder /app/service .
# Expose application port (adjust if needed)
EXPOSE 8081
# Command to run the application
ENTRYPOINT ["./service"]

23
LICENSE
View File

@ -1,23 +0,0 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -1,8 +0,0 @@
.PHONY: maps-service web validation
maps-service:
DOCKER_BUILDKIT=1 docker build . -f Containerfile -t maps-service \
--secret id=netrc,src=/home/quat/.netrc
web:
docker build web -f web/Containerfile -t maps-service-web
validation:
docker build validation -f validation/Containerfile -t maps-service-validation

View File

@ -1,15 +1,6 @@
# STRAFES.NET Map Submission System
# STRAFES.NET API
## Components
- Submissions API (golang) `pkg/` `cmd/`
- Website `/web/`
- Script Validation (rust) `validation/`
## How to Begin Development on Each Component
### Submissions API
Prerequisite: golang installed
## How to Begin Development
1. Run `go generate` to ensure the generated API is up-to-date. This project uses [ogen](https://github.com/ogen-go/ogen).
```bash
@ -21,38 +12,3 @@ Prerequisite: golang installed
```
By default, the project opens at `localhost:8080`.
### Website
Prerequisite: bun installed
1. `cd web`
2. `bun install`
#### For development:
3. `bun run dev`
#### For production:
3. `bun run build`
4. `bun run start` (optionally start a node server)
### Script Validation
Prerequisite: rust installed
1. `cd validation`
2. `cargo run --release`
#### License
<sup>
Licensed under <a href="LICENSE">MIT license</a>.
</sup>
<br>
<sub>
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this project by you, shall be licensed as above, without any
additional terms or conditions.
</sub>

View File

@ -1,119 +0,0 @@
version: '3.9'
networks:
maps-service-network:
driver: bridge
services:
nats:
image: docker.io/nats:latest
container_name: nats
command: ["-js"] #"-DVV"
networks:
- maps-service-network
mapsservice:
image:
maps-service
container_name: mapsservice
command: [
# debug
"--debug","serve",
# http service port
"--port","8082",
# postgres
"--pg-host","10.0.0.29",
"--pg-port","5432",
"--pg-db","maps",
"--pg-user","quat",
"--pg-password","happypostgresuser",
# other hosts
"--nats-host","nats:4222",
"--auth-rpc-host","authrpc:8081"
]
depends_on:
- authrpc
- nats
networks:
- maps-service-network
ports:
- "8082:8082"
web:
image:
maps-service-web
networks:
- maps-service-network
ports:
- "3000:3000"
validation:
image:
maps-service-validation
container_name: validation
environment:
- RBXCOOKIE
- API_HOST=http://mapsservice:8082
- NATS_HOST=nats:4222
- DATA_HOST=http://dataservice:9000
depends_on:
- nats
# note: this races the mapsservice which creates a nats stream
# the validation will panic if the nats stream is not created
- mapsservice
- dataservice
networks:
- maps-service-network
dataservice:
image: registry.itzana.me/strafesnet/data-service:master
container_name: dataservice
environment:
- DEBUG=true
- PG_HOST=10.0.0.29
- PG_PORT=5432
- PG_USER=quat
- PG_DB=data
- PG_PASS=happypostgresuser
networks:
- maps-service-network
authredis:
image: docker.io/redis:latest
container_name: authredis
volumes:
- redis-data:/data
command: ["redis-server", "--appendonly", "yes"]
networks:
- maps-service-network
authrpc:
image: registry.itzana.me/strafesnet/auth-service:master
container_name: authrpc
command: ["serve", "rpc"]
environment:
- REDIS_ADDR=authredis:6379
env_file:
- ../auth-compose/auth-service.env
depends_on:
- authredis
networks:
- maps-service-network
logging:
driver: "none"
auth-web:
image: registry.itzana.me/strafesnet/auth-service:master
command: ["serve", "web"]
environment:
- REDIS_ADDR=authredis:6379
env_file:
- ../auth-compose/auth-service.env
depends_on:
- authredis
networks:
- maps-service-network
ports:
- "8080:8080"
volumes:
redis-data:

1
go.mod
View File

@ -7,7 +7,6 @@ toolchain go1.23.3
require (
git.itzana.me/strafesnet/go-grpc v0.0.0-20241129081229-9e166b3d11f7
git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9
github.com/dchest/siphash v1.2.3
github.com/go-faster/errors v0.7.1
github.com/go-faster/jx v1.1.0
github.com/nats-io/nats.go v1.37.0

2
go.sum
View File

@ -20,8 +20,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=

View File

@ -1,18 +1,21 @@
openapi: 3.1.0
info:
title: StrafesNET Submissions - OpenAPI 3.1
description: Browse and manage map submissions.
description: Browse and manage map submissions in the staging pipeline.
version: 0.1.0
servers:
- url: https://submissions.strafes.net/v1
tags:
- name: Submissions
description: Submission operations
- name: Scripts
description: Script operations
- name: ScriptPolicy
description: Script policy operations
security:
- cookieAuth: []
paths:
# status
# submit
# accept
# publish
# complete
/submissions:
get:
summary: Get list of submissions
@ -30,8 +33,6 @@ paths:
required: false
schema:
$ref: "#/components/schemas/SubmissionFilter"
security:
- cookieAuth: []
responses:
"200":
description: Successful response
@ -53,15 +54,12 @@ paths:
tags:
- Submissions
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SubmissionCreate'
security:
- cookieAuth: []
responses:
"201":
"200":
description: Successful response
content:
application/json:
@ -80,9 +78,12 @@ paths:
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
security:
- cookieAuth: []
- name: SubmissionID
in: path
required: true
schema:
type: integer
format: int64
responses:
"200":
description: Successful response
@ -97,13 +98,18 @@ paths:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/model:
post:
patch:
summary: Update model following role restrictions
operationId: updateSubmissionModel
operationId: patchSubmissionModel
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: SubmissionID
in: path
required: true
schema:
type: integer
format: int64
- name: ModelID
in: query
required: true
@ -116,10 +122,8 @@ paths:
schema:
type: integer
format: int64
security:
- cookieAuth: []
responses:
"204":
"200":
description: Successful response
default:
description: General Error
@ -128,17 +132,20 @@ paths:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/completed:
post:
patch:
summary: Retrieve map with ID
operationId: setSubmissionCompleted
operationId: patchSubmissionCompleted
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
security:
- cookieAuth: []
- name: SubmissionID
in: path
required: true
schema:
type: integer
format: int64
responses:
"204":
"200":
description: Successful response
default:
description: General Error
@ -147,17 +154,20 @@ paths:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/submit:
post:
patch:
summary: Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted
operationId: actionSubmissionSubmit
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
security:
- cookieAuth: []
- name: SubmissionID
in: path
required: true
schema:
type: integer
format: int64
responses:
"204":
"200":
description: Successful response
default:
description: General Error
@ -166,17 +176,20 @@ paths:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/revoke:
post:
patch:
summary: Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction
operationId: actionSubmissionRevoke
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
security:
- cookieAuth: []
- name: SubmissionID
in: path
required: true
schema:
type: integer
format: int64
responses:
"204":
"200":
description: Successful response
default:
description: General Error
@ -185,17 +198,20 @@ paths:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/trigger-validate:
post:
patch:
summary: Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating
operationId: actionSubmissionTriggerValidate
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
security:
- cookieAuth: []
- name: SubmissionID
in: path
required: true
schema:
type: integer
format: int64
responses:
"204":
"200":
description: Successful response
default:
description: General Error
@ -204,17 +220,20 @@ paths:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/reject:
post:
patch:
summary: Role Reviewer changes status from Submitted -> Rejected
operationId: actionSubmissionReject
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
security:
- cookieAuth: []
- name: SubmissionID
in: path
required: true
schema:
type: integer
format: int64
responses:
"204":
"200":
description: Successful response
default:
description: General Error
@ -223,17 +242,20 @@ paths:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/request-changes:
post:
patch:
summary: Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested
operationId: actionSubmissionRequestChanges
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
security:
- cookieAuth: []
- name: SubmissionID
in: path
required: true
schema:
type: integer
format: int64
responses:
"204":
"200":
description: Successful response
default:
description: General Error
@ -241,16 +263,21 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/validator-validated:
post:
summary: (Internal endpoint) Role Validator changes status from Validating -> Validated
/submissions/{SubmissionID}/status/validate:
patch:
summary: Role Validator changes status from Validating -> Validated
operationId: actionSubmissionValidate
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: SubmissionID
in: path
required: true
schema:
type: integer
format: int64
responses:
"204":
"200":
description: Successful response
default:
description: General Error
@ -258,16 +285,21 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/validator-published:
post:
summary: (Internal endpoint) Role Validator changes status from Publishing -> Published
/submissions/{SubmissionID}/status/publish:
patch:
summary: Role Validator changes status from Publishing -> Published
operationId: actionSubmissionPublish
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: SubmissionID
in: path
required: true
schema:
type: integer
format: int64
responses:
"204":
"200":
description: Successful response
default:
description: General Error
@ -276,231 +308,21 @@ paths:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/trigger-publish:
post:
patch:
summary: Role Admin changes status from Validated -> Publishing
operationId: actionSubmissionTriggerPublish
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
security:
- cookieAuth: []
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/script-policy:
post:
summary: Create a new script policy
operationId: createScriptPolicy
tags:
- ScriptPolicy
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ScriptPolicyCreate'
security:
- cookieAuth: []
responses:
"201":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/Id"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/script-policy/hash/{FromScriptHash}:
get:
summary: Get the policy for the given hash of script source code
operationId: getScriptPolicyFromHash
tags:
- ScriptPolicy
parameters:
- name: FromScriptHash
- name: SubmissionID
in: path
required: true
schema:
type: string
minLength: 16
maxLength: 16
security:
- cookieAuth: []
type: integer
format: int64
responses:
"200":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/ScriptPolicy"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/script-policy/id/{ScriptPolicyID}:
get:
summary: Get the specified script policy by ID
operationId: getScriptPolicy
tags:
- ScriptPolicy
parameters:
- $ref: '#/components/parameters/ScriptPolicyID'
security:
- cookieAuth: []
responses:
"200":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/ScriptPolicy"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Update the specified script policy by ID
operationId: updateScriptPolicy
tags:
- ScriptPolicy
parameters:
- $ref: '#/components/parameters/ScriptPolicyID'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ScriptPolicyUpdate'
security:
- cookieAuth: []
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
delete:
summary: Delete the specified script policy by ID
operationId: deleteScriptPolicy
tags:
- ScriptPolicy
parameters:
- $ref: '#/components/parameters/ScriptPolicyID'
security:
- cookieAuth: []
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/scripts:
post:
summary: Create a new script
operationId: createScript
tags:
- Scripts
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ScriptCreate'
security:
- cookieAuth: []
responses:
"201":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/Id"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/scripts/{ScriptID}:
get:
summary: Get the specified script by ID
operationId: getScript
tags:
- Scripts
parameters:
- $ref: '#/components/parameters/ScriptID'
security:
- cookieAuth: []
responses:
"200":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/Script"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Update the specified script by ID
operationId: updateScript
tags:
- Scripts
parameters:
- $ref: '#/components/parameters/ScriptID'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ScriptUpdate'
security:
- cookieAuth: []
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
delete:
summary: Delete the specified script by ID
operationId: deleteScript
tags:
- Scripts
parameters:
- $ref: '#/components/parameters/ScriptID'
security:
- cookieAuth: []
responses:
"204":
description: Successful response
default:
description: General Error
content:
@ -512,56 +334,26 @@ components:
cookieAuth:
type: apiKey
in: cookie
name: session_id
parameters:
SubmissionID:
name: SubmissionID
in: path
required: true
description: The unique identifier for a submission.
schema:
type: integer
format: int64
ScriptID:
name: ScriptID
in: path
required: true
description: The unique identifier for a script.
schema:
type: integer
format: int64
ScriptPolicyID:
name: ScriptPolicyID
in: path
required: true
description: The unique identifier for a script policy.
schema:
type: integer
format: int64
name: SESSIONID
schemas:
Id:
required:
- ID
type: object
properties:
ID:
type: integer
format: int64
User:
type: object
properties:
ID:
type: integer
format: int64
Username:
type: string
StateID:
type: integer
format: int32
Submission:
required:
- ID
- DisplayName
- Creator
- GameID
- CreatedAt
- UpdatedAt
- Submitter
- AssetID
- AssetVersion
- Completed
- SubmissionType
# - TargetAssetID
- StatusID
type: object
properties:
ID:
@ -569,17 +361,12 @@ components:
format: int64
DisplayName:
type: string
maxLength: 128
Creator:
type: string
maxLength: 128
GameID:
type: integer
format: int32
CreatedAt:
type: integer
format: int64
UpdatedAt:
Date:
type: integer
format: int64
Submitter:
@ -603,8 +390,6 @@ components:
type: integer
format: int32
SubmissionFilter:
required:
- ID
type: object
properties:
ID:
@ -612,142 +397,39 @@ components:
format: int64
DisplayName:
type: string
maxLength: 128
Creator:
type: string
maxLength: 128
GameID:
type: integer
format: int32
Date:
type: integer
format: int64
SubmissionCreate:
required:
- DisplayName
- Creator
- GameID
- AssetID
- AssetVersion
# - TargetAssetID
type: object
properties:
DisplayName:
type: string
maxLength: 128
Creator:
type: string
maxLength: 128
GameID:
type: integer
format: int32
Submitter:
type: integer
format: int64
AssetID:
type: integer
format: int64
AssetVersion:
type: integer
format: int64
SubmissionType:
type: integer
format: int32
TargetAssetID:
type: integer
format: int64
Script:
required:
- ID
- Hash
- Source
- SubmissionID
type: object
properties:
ID:
type: integer
format: int64
Hash:
type: string
minLength: 16
maxLength: 16
Source:
type: string
maxLength: 1048576
SubmissionID:
type: integer
format: int64
ScriptCreate:
required:
- Source
# - SubmissionID
type: object
properties:
Source:
type: string
maxLength: 1048576
SubmissionID:
type: integer
format: int64
ScriptUpdate:
required:
- ID
type: object
properties:
ID:
type: integer
format: int64
Source:
type: string
maxLength: 1048576
SubmissionID:
type: integer
format: int64
ScriptPolicy:
required:
- ID
- FromScriptHash
- ToScriptID
- Policy
type: object
properties:
ID:
type: integer
format: int64
FromScriptHash:
type: string
minLength: 16
maxLength: 16
ToScriptID:
type: integer
format: int64
Policy:
type: integer
format: int32
ScriptPolicyCreate:
required:
- FromScriptID
- ToScriptID
- Policy
type: object
properties:
FromScriptID:
type: integer
format: int64
ToScriptID:
type: integer
format: int64
Policy:
type: integer
format: int32
ScriptPolicyUpdate:
required:
- ID
type: object
properties:
ID:
type: integer
format: int64
FromScriptID:
type: integer
format: int64
ToScriptID:
type: integer
format: int64
Policy:
type: integer
format: int32
Pagination:
type: object
required:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -14,18 +14,9 @@ const (
ActionSubmissionTriggerPublishOperation OperationName = "ActionSubmissionTriggerPublish"
ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate"
ActionSubmissionValidateOperation OperationName = "ActionSubmissionValidate"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
DeleteScriptOperation OperationName = "DeleteScript"
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"
GetScriptOperation OperationName = "GetScript"
GetScriptPolicyOperation OperationName = "GetScriptPolicy"
GetScriptPolicyFromHashOperation OperationName = "GetScriptPolicyFromHash"
GetSubmissionOperation OperationName = "GetSubmission"
ListSubmissionsOperation OperationName = "ListSubmissions"
SetSubmissionCompletedOperation OperationName = "SetSubmissionCompleted"
UpdateScriptOperation OperationName = "UpdateScript"
UpdateScriptPolicyOperation OperationName = "UpdateScriptPolicy"
UpdateSubmissionModelOperation OperationName = "UpdateSubmissionModel"
PatchSubmissionCompletedOperation OperationName = "PatchSubmissionCompleted"
PatchSubmissionModelOperation OperationName = "PatchSubmissionModel"
)

View File

@ -17,7 +17,6 @@ import (
// ActionSubmissionPublishParams is parameters of actionSubmissionPublish operation.
type ActionSubmissionPublishParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
@ -83,7 +82,6 @@ func decodeActionSubmissionPublishParams(args [1]string, argsEscaped bool, r *ht
// ActionSubmissionRejectParams is parameters of actionSubmissionReject operation.
type ActionSubmissionRejectParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
@ -149,7 +147,6 @@ func decodeActionSubmissionRejectParams(args [1]string, argsEscaped bool, r *htt
// ActionSubmissionRequestChangesParams is parameters of actionSubmissionRequestChanges operation.
type ActionSubmissionRequestChangesParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
@ -215,7 +212,6 @@ func decodeActionSubmissionRequestChangesParams(args [1]string, argsEscaped bool
// ActionSubmissionRevokeParams is parameters of actionSubmissionRevoke operation.
type ActionSubmissionRevokeParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
@ -281,7 +277,6 @@ func decodeActionSubmissionRevokeParams(args [1]string, argsEscaped bool, r *htt
// ActionSubmissionSubmitParams is parameters of actionSubmissionSubmit operation.
type ActionSubmissionSubmitParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
@ -347,7 +342,6 @@ func decodeActionSubmissionSubmitParams(args [1]string, argsEscaped bool, r *htt
// ActionSubmissionTriggerPublishParams is parameters of actionSubmissionTriggerPublish operation.
type ActionSubmissionTriggerPublishParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
@ -413,7 +407,6 @@ func decodeActionSubmissionTriggerPublishParams(args [1]string, argsEscaped bool
// ActionSubmissionTriggerValidateParams is parameters of actionSubmissionTriggerValidate operation.
type ActionSubmissionTriggerValidateParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
@ -479,7 +472,6 @@ func decodeActionSubmissionTriggerValidateParams(args [1]string, argsEscaped boo
// ActionSubmissionValidateParams is parameters of actionSubmissionValidate operation.
type ActionSubmissionValidateParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
@ -543,354 +535,8 @@ func decodeActionSubmissionValidateParams(args [1]string, argsEscaped bool, r *h
return params, nil
}
// DeleteScriptParams is parameters of deleteScript operation.
type DeleteScriptParams struct {
// The unique identifier for a script.
ScriptID int64
}
func unpackDeleteScriptParams(packed middleware.Parameters) (params DeleteScriptParams) {
{
key := middleware.ParameterKey{
Name: "ScriptID",
In: "path",
}
params.ScriptID = packed[key].(int64)
}
return params
}
func decodeDeleteScriptParams(args [1]string, argsEscaped bool, r *http.Request) (params DeleteScriptParams, _ error) {
// Decode path: ScriptID.
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: "ScriptID",
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.ScriptID = c
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ScriptID",
In: "path",
Err: err,
}
}
return params, nil
}
// DeleteScriptPolicyParams is parameters of deleteScriptPolicy operation.
type DeleteScriptPolicyParams struct {
// The unique identifier for a script policy.
ScriptPolicyID int64
}
func unpackDeleteScriptPolicyParams(packed middleware.Parameters) (params DeleteScriptPolicyParams) {
{
key := middleware.ParameterKey{
Name: "ScriptPolicyID",
In: "path",
}
params.ScriptPolicyID = packed[key].(int64)
}
return params
}
func decodeDeleteScriptPolicyParams(args [1]string, argsEscaped bool, r *http.Request) (params DeleteScriptPolicyParams, _ error) {
// Decode path: ScriptPolicyID.
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: "ScriptPolicyID",
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.ScriptPolicyID = c
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ScriptPolicyID",
In: "path",
Err: err,
}
}
return params, nil
}
// GetScriptParams is parameters of getScript operation.
type GetScriptParams struct {
// The unique identifier for a script.
ScriptID int64
}
func unpackGetScriptParams(packed middleware.Parameters) (params GetScriptParams) {
{
key := middleware.ParameterKey{
Name: "ScriptID",
In: "path",
}
params.ScriptID = packed[key].(int64)
}
return params
}
func decodeGetScriptParams(args [1]string, argsEscaped bool, r *http.Request) (params GetScriptParams, _ error) {
// Decode path: ScriptID.
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: "ScriptID",
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.ScriptID = c
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ScriptID",
In: "path",
Err: err,
}
}
return params, nil
}
// GetScriptPolicyParams is parameters of getScriptPolicy operation.
type GetScriptPolicyParams struct {
// The unique identifier for a script policy.
ScriptPolicyID int64
}
func unpackGetScriptPolicyParams(packed middleware.Parameters) (params GetScriptPolicyParams) {
{
key := middleware.ParameterKey{
Name: "ScriptPolicyID",
In: "path",
}
params.ScriptPolicyID = packed[key].(int64)
}
return params
}
func decodeGetScriptPolicyParams(args [1]string, argsEscaped bool, r *http.Request) (params GetScriptPolicyParams, _ error) {
// Decode path: ScriptPolicyID.
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: "ScriptPolicyID",
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.ScriptPolicyID = c
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ScriptPolicyID",
In: "path",
Err: err,
}
}
return params, nil
}
// GetScriptPolicyFromHashParams is parameters of getScriptPolicyFromHash operation.
type GetScriptPolicyFromHashParams struct {
FromScriptHash string
}
func unpackGetScriptPolicyFromHashParams(packed middleware.Parameters) (params GetScriptPolicyFromHashParams) {
{
key := middleware.ParameterKey{
Name: "FromScriptHash",
In: "path",
}
params.FromScriptHash = packed[key].(string)
}
return params
}
func decodeGetScriptPolicyFromHashParams(args [1]string, argsEscaped bool, r *http.Request) (params GetScriptPolicyFromHashParams, _ error) {
// Decode path: FromScriptHash.
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: "FromScriptHash",
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)
if err != nil {
return err
}
params.FromScriptHash = c
return nil
}(); err != nil {
return err
}
if err := func() error {
if err := (validate.String{
MinLength: 16,
MinLengthSet: true,
MaxLength: 16,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(params.FromScriptHash)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "FromScriptHash",
In: "path",
Err: err,
}
}
return params, nil
}
// GetSubmissionParams is parameters of getSubmission operation.
type GetSubmissionParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
@ -1022,7 +668,7 @@ func decodeListSubmissionsParams(args [0]string, argsEscaped bool, r *http.Reque
Name: "filter",
Style: uri.QueryStyleForm,
Explode: true,
Fields: []uri.QueryParameterObjectField{{Name: "ID", Required: true}, {Name: "DisplayName", Required: false}, {Name: "Creator", Required: false}, {Name: "GameID", Required: false}},
Fields: []uri.QueryParameterObjectField{{Name: "ID", Required: false}, {Name: "DisplayName", Required: false}, {Name: "Creator", Required: false}, {Name: "GameID", Required: false}, {Name: "Date", Required: false}},
}
if err := q.HasParam(cfg); err == nil {
@ -1038,21 +684,6 @@ func decodeListSubmissionsParams(args [0]string, argsEscaped bool, r *http.Reque
}); err != nil {
return err
}
if err := func() error {
if value, ok := params.Filter.Get(); ok {
if err := func() error {
if err := value.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return err
}
}
return nil
}(); err != nil {
return err
}
}
return nil
}(); err != nil {
@ -1065,13 +696,12 @@ func decodeListSubmissionsParams(args [0]string, argsEscaped bool, r *http.Reque
return params, nil
}
// SetSubmissionCompletedParams is parameters of setSubmissionCompleted operation.
type SetSubmissionCompletedParams struct {
// The unique identifier for a submission.
// PatchSubmissionCompletedParams is parameters of patchSubmissionCompleted operation.
type PatchSubmissionCompletedParams struct {
SubmissionID int64
}
func unpackSetSubmissionCompletedParams(packed middleware.Parameters) (params SetSubmissionCompletedParams) {
func unpackPatchSubmissionCompletedParams(packed middleware.Parameters) (params PatchSubmissionCompletedParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
@ -1082,7 +712,7 @@ func unpackSetSubmissionCompletedParams(packed middleware.Parameters) (params Se
return params
}
func decodeSetSubmissionCompletedParams(args [1]string, argsEscaped bool, r *http.Request) (params SetSubmissionCompletedParams, _ error) {
func decodePatchSubmissionCompletedParams(args [1]string, argsEscaped bool, r *http.Request) (params PatchSubmissionCompletedParams, _ error) {
// Decode path: SubmissionID.
if err := func() error {
param := args[0]
@ -1131,147 +761,14 @@ func decodeSetSubmissionCompletedParams(args [1]string, argsEscaped bool, r *htt
return params, nil
}
// UpdateScriptParams is parameters of updateScript operation.
type UpdateScriptParams struct {
// The unique identifier for a script.
ScriptID int64
}
func unpackUpdateScriptParams(packed middleware.Parameters) (params UpdateScriptParams) {
{
key := middleware.ParameterKey{
Name: "ScriptID",
In: "path",
}
params.ScriptID = packed[key].(int64)
}
return params
}
func decodeUpdateScriptParams(args [1]string, argsEscaped bool, r *http.Request) (params UpdateScriptParams, _ error) {
// Decode path: ScriptID.
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: "ScriptID",
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.ScriptID = c
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ScriptID",
In: "path",
Err: err,
}
}
return params, nil
}
// UpdateScriptPolicyParams is parameters of updateScriptPolicy operation.
type UpdateScriptPolicyParams struct {
// The unique identifier for a script policy.
ScriptPolicyID int64
}
func unpackUpdateScriptPolicyParams(packed middleware.Parameters) (params UpdateScriptPolicyParams) {
{
key := middleware.ParameterKey{
Name: "ScriptPolicyID",
In: "path",
}
params.ScriptPolicyID = packed[key].(int64)
}
return params
}
func decodeUpdateScriptPolicyParams(args [1]string, argsEscaped bool, r *http.Request) (params UpdateScriptPolicyParams, _ error) {
// Decode path: ScriptPolicyID.
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: "ScriptPolicyID",
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.ScriptPolicyID = c
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "ScriptPolicyID",
In: "path",
Err: err,
}
}
return params, nil
}
// UpdateSubmissionModelParams is parameters of updateSubmissionModel operation.
type UpdateSubmissionModelParams struct {
// The unique identifier for a submission.
// PatchSubmissionModelParams is parameters of patchSubmissionModel operation.
type PatchSubmissionModelParams struct {
SubmissionID int64
ModelID int64
VersionID int64
}
func unpackUpdateSubmissionModelParams(packed middleware.Parameters) (params UpdateSubmissionModelParams) {
func unpackPatchSubmissionModelParams(packed middleware.Parameters) (params PatchSubmissionModelParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
@ -1296,7 +793,7 @@ func unpackUpdateSubmissionModelParams(packed middleware.Parameters) (params Upd
return params
}
func decodeUpdateSubmissionModelParams(args [1]string, argsEscaped bool, r *http.Request) (params UpdateSubmissionModelParams, _ error) {
func decodePatchSubmissionModelParams(args [1]string, argsEscaped bool, r *http.Request) (params PatchSubmissionModelParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query())
// Decode path: SubmissionID.
if err := func() error {

View File

@ -15,142 +15,8 @@ import (
"github.com/ogen-go/ogen/validate"
)
func (s *Server) decodeCreateScriptRequest(r *http.Request) (
req *ScriptCreate,
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 ScriptCreate
if err := func() error {
if err := request.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
req *ScriptPolicyCreate,
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 ScriptPolicyCreate
if err := func() error {
if err := request.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
req *SubmissionCreate,
req OptSubmissionCreate,
close func() error,
rerr error,
) {
@ -169,6 +35,9 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
rerr = multierr.Append(rerr, close())
}
}()
if _, ok := r.Header["Content-Type"]; !ok && r.ContentLength == 0 {
return req, close, nil
}
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
@ -176,7 +45,7 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
switch {
case ct == "application/json":
if r.ContentLength == 0 {
return req, close, validate.ErrBodyRequired
return req, close, nil
}
buf, err := io.ReadAll(r.Body)
if err != nil {
@ -184,13 +53,14 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
}
if len(buf) == 0 {
return req, close, validate.ErrBodyRequired
return req, close, nil
}
d := jx.DecodeBytes(buf)
var request SubmissionCreate
var request OptSubmissionCreate
if err := func() error {
request.Reset()
if err := request.Decode(d); err != nil {
return err
}
@ -206,149 +76,7 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeUpdateScriptRequest(r *http.Request) (
req *ScriptUpdate,
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 ScriptUpdate
if err := func() error {
if err := request.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeUpdateScriptPolicyRequest(r *http.Request) (
req *ScriptPolicyUpdate,
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 ScriptPolicyUpdate
if err := func() error {
if err := request.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
return &request, close, nil
return request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}

View File

@ -11,70 +11,20 @@ import (
ht "github.com/ogen-go/ogen/http"
)
func encodeCreateScriptRequest(
req *ScriptCreate,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeCreateScriptPolicyRequest(
req *ScriptPolicyCreate,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeCreateSubmissionRequest(
req *SubmissionCreate,
req OptSubmissionCreate,
r *http.Request,
) error {
const contentType = "application/json"
if !req.Set {
// Keep request with empty body if value is not set.
return nil
}
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeUpdateScriptRequest(
req *ScriptUpdate,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeUpdateScriptPolicyRequest(
req *ScriptPolicyUpdate,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
if req.Set {
req.Encode(e)
}
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)

View File

@ -3,7 +3,6 @@
package api
import (
"fmt"
"io"
"mime"
"net/http"
@ -15,11 +14,11 @@ import (
"github.com/ogen-go/ogen/validate"
)
func decodeActionSubmissionPublishResponse(resp *http.Response) (res *ActionSubmissionPublishNoContent, _ error) {
func decodeActionSubmissionPublishResponse(resp *http.Response) (res *ActionSubmissionPublishOK, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionPublishNoContent{}, nil
case 200:
// Code 200.
return &ActionSubmissionPublishOK{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
@ -66,11 +65,11 @@ func decodeActionSubmissionPublishResponse(resp *http.Response) (res *ActionSubm
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionRejectResponse(resp *http.Response) (res *ActionSubmissionRejectNoContent, _ error) {
func decodeActionSubmissionRejectResponse(resp *http.Response) (res *ActionSubmissionRejectOK, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionRejectNoContent{}, nil
case 200:
// Code 200.
return &ActionSubmissionRejectOK{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
@ -117,11 +116,11 @@ func decodeActionSubmissionRejectResponse(resp *http.Response) (res *ActionSubmi
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionRequestChangesResponse(resp *http.Response) (res *ActionSubmissionRequestChangesNoContent, _ error) {
func decodeActionSubmissionRequestChangesResponse(resp *http.Response) (res *ActionSubmissionRequestChangesOK, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionRequestChangesNoContent{}, nil
case 200:
// Code 200.
return &ActionSubmissionRequestChangesOK{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
@ -168,11 +167,11 @@ func decodeActionSubmissionRequestChangesResponse(resp *http.Response) (res *Act
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionRevokeResponse(resp *http.Response) (res *ActionSubmissionRevokeNoContent, _ error) {
func decodeActionSubmissionRevokeResponse(resp *http.Response) (res *ActionSubmissionRevokeOK, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionRevokeNoContent{}, nil
case 200:
// Code 200.
return &ActionSubmissionRevokeOK{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
@ -219,11 +218,11 @@ func decodeActionSubmissionRevokeResponse(resp *http.Response) (res *ActionSubmi
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionSubmitResponse(resp *http.Response) (res *ActionSubmissionSubmitNoContent, _ error) {
func decodeActionSubmissionSubmitResponse(resp *http.Response) (res *ActionSubmissionSubmitOK, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionSubmitNoContent{}, nil
case 200:
// Code 200.
return &ActionSubmissionSubmitOK{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
@ -270,11 +269,11 @@ func decodeActionSubmissionSubmitResponse(resp *http.Response) (res *ActionSubmi
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionTriggerPublishResponse(resp *http.Response) (res *ActionSubmissionTriggerPublishNoContent, _ error) {
func decodeActionSubmissionTriggerPublishResponse(resp *http.Response) (res *ActionSubmissionTriggerPublishOK, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionTriggerPublishNoContent{}, nil
case 200:
// Code 200.
return &ActionSubmissionTriggerPublishOK{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
@ -321,11 +320,11 @@ func decodeActionSubmissionTriggerPublishResponse(resp *http.Response) (res *Act
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionTriggerValidateResponse(resp *http.Response) (res *ActionSubmissionTriggerValidateNoContent, _ error) {
func decodeActionSubmissionTriggerValidateResponse(resp *http.Response) (res *ActionSubmissionTriggerValidateOK, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionTriggerValidateNoContent{}, nil
case 200:
// Code 200.
return &ActionSubmissionTriggerValidateOK{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
@ -372,177 +371,11 @@ func decodeActionSubmissionTriggerValidateResponse(resp *http.Response) (res *Ac
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionValidateResponse(resp *http.Response) (res *ActionSubmissionValidateNoContent, _ error) {
func decodeActionSubmissionValidateResponse(resp *http.Response) (res *ActionSubmissionValidateOK, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionValidateNoContent{}, 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:
// Code 201.
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response ID
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 decodeCreateScriptPolicyResponse(resp *http.Response) (res *ID, _ error) {
switch resp.StatusCode {
case 201:
// Code 201.
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response ID
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)
}
case 200:
// Code 200.
return &ActionSubmissionValidateOK{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
@ -591,8 +424,8 @@ func decodeCreateScriptPolicyResponse(resp *http.Response) (res *ID, _ error) {
func decodeCreateSubmissionResponse(resp *http.Response) (res *ID, _ error) {
switch resp.StatusCode {
case 201:
// Code 201.
case 200:
// Code 200.
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
@ -672,384 +505,6 @@ func decodeCreateSubmissionResponse(resp *http.Response) (res *ID, _ error) {
return res, errors.Wrap(defRes, "error")
}
func decodeDeleteScriptResponse(resp *http.Response) (res *DeleteScriptNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &DeleteScriptNoContent{}, 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 decodeDeleteScriptPolicyResponse(resp *http.Response) (res *DeleteScriptPolicyNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &DeleteScriptPolicyNoContent{}, 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 decodeGetScriptResponse(resp *http.Response) (res *Script, _ 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 Script
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 decodeGetScriptPolicyResponse(resp *http.Response) (res *ScriptPolicy, _ 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 ScriptPolicy
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 decodeGetScriptPolicyFromHashResponse(resp *http.Response) (res *ScriptPolicy, _ 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 ScriptPolicy
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 decodeGetSubmissionResponse(resp *http.Response) (res *Submission, _ error) {
switch resp.StatusCode {
case 200:
@ -1083,15 +538,6 @@ func decodeGetSubmissionResponse(resp *http.Response) (res *Submission, _ error)
}
return res, err
}
// Validate response.
if err := func() error {
if err := response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
}
return &response, nil
default:
return res, validate.InvalidContentType(ct)
@ -1188,23 +634,6 @@ func decodeListSubmissionsResponse(resp *http.Response) (res []Submission, _ err
if response == nil {
return errors.New("nil is invalid value")
}
var failures []validate.FieldError
for i, elem := range response {
if err := func() error {
if err := elem.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: fmt.Sprintf("[%d]", i),
Error: err,
})
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
@ -1259,11 +688,11 @@ func decodeListSubmissionsResponse(resp *http.Response) (res []Submission, _ err
return res, errors.Wrap(defRes, "error")
}
func decodeSetSubmissionCompletedResponse(resp *http.Response) (res *SetSubmissionCompletedNoContent, _ error) {
func decodePatchSubmissionCompletedResponse(resp *http.Response) (res *PatchSubmissionCompletedOK, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &SetSubmissionCompletedNoContent{}, nil
case 200:
// Code 200.
return &PatchSubmissionCompletedOK{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
@ -1310,113 +739,11 @@ func decodeSetSubmissionCompletedResponse(resp *http.Response) (res *SetSubmissi
return res, errors.Wrap(defRes, "error")
}
func decodeUpdateScriptResponse(resp *http.Response) (res *UpdateScriptNoContent, _ error) {
func decodePatchSubmissionModelResponse(resp *http.Response) (res *PatchSubmissionModelOK, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &UpdateScriptNoContent{}, 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 decodeUpdateScriptPolicyResponse(resp *http.Response) (res *UpdateScriptPolicyNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &UpdateScriptPolicyNoContent{}, 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 decodeUpdateSubmissionModelResponse(resp *http.Response) (res *UpdateSubmissionModelNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &UpdateSubmissionModelNoContent{}, nil
case 200:
// Code 200.
return &PatchSubmissionModelOK{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {

View File

@ -13,147 +13,63 @@ import (
ht "github.com/ogen-go/ogen/http"
)
func encodeActionSubmissionPublishResponse(response *ActionSubmissionPublishNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
func encodeActionSubmissionPublishResponse(response *ActionSubmissionPublishOK, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
return nil
}
func encodeActionSubmissionRejectResponse(response *ActionSubmissionRejectNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
func encodeActionSubmissionRejectResponse(response *ActionSubmissionRejectOK, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
return nil
}
func encodeActionSubmissionRequestChangesResponse(response *ActionSubmissionRequestChangesNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
func encodeActionSubmissionRequestChangesResponse(response *ActionSubmissionRequestChangesOK, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
return nil
}
func encodeActionSubmissionRevokeResponse(response *ActionSubmissionRevokeNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
func encodeActionSubmissionRevokeResponse(response *ActionSubmissionRevokeOK, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
return nil
}
func encodeActionSubmissionSubmitResponse(response *ActionSubmissionSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
func encodeActionSubmissionSubmitResponse(response *ActionSubmissionSubmitOK, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
return nil
}
func encodeActionSubmissionTriggerPublishResponse(response *ActionSubmissionTriggerPublishNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
func encodeActionSubmissionTriggerPublishResponse(response *ActionSubmissionTriggerPublishOK, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
return nil
}
func encodeActionSubmissionTriggerValidateResponse(response *ActionSubmissionTriggerValidateNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
func encodeActionSubmissionTriggerValidateResponse(response *ActionSubmissionTriggerValidateOK, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
return nil
}
func encodeActionSubmissionValidateResponse(response *ActionSubmissionValidateNoContent, 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)
span.SetStatus(codes.Ok, http.StatusText(201))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeCreateScriptPolicyResponse(response *ID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
func encodeActionSubmissionValidateResponse(response *ActionSubmissionValidateOK, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
return nil
}
func encodeCreateSubmissionResponse(response *ID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeDeleteScriptResponse(response *DeleteScriptNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeDeleteScriptPolicyResponse(response *DeleteScriptPolicyNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeGetScriptResponse(response *Script, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
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 encodeGetScriptPolicyResponse(response *ScriptPolicy, 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 encodeGetScriptPolicyFromHashResponse(response *ScriptPolicy, 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))
@ -199,30 +115,16 @@ func encodeListSubmissionsResponse(response []Submission, w http.ResponseWriter,
return nil
}
func encodeSetSubmissionCompletedResponse(response *SetSubmissionCompletedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
func encodePatchSubmissionCompletedResponse(response *PatchSubmissionCompletedOK, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
return nil
}
func encodeUpdateScriptResponse(response *UpdateScriptNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeUpdateScriptPolicyResponse(response *UpdateScriptPolicyNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeUpdateSubmissionModelResponse(response *UpdateSubmissionModelNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
func encodePatchSubmissionModelResponse(response *PatchSubmissionModelOK, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -10,29 +10,29 @@ func (s *ErrorStatusCode) Error() string {
return fmt.Sprintf("code %d: %+v", s.StatusCode, s.Response)
}
// ActionSubmissionPublishNoContent is response for ActionSubmissionPublish operation.
type ActionSubmissionPublishNoContent struct{}
// ActionSubmissionPublishOK is response for ActionSubmissionPublish operation.
type ActionSubmissionPublishOK struct{}
// ActionSubmissionRejectNoContent is response for ActionSubmissionReject operation.
type ActionSubmissionRejectNoContent struct{}
// ActionSubmissionRejectOK is response for ActionSubmissionReject operation.
type ActionSubmissionRejectOK struct{}
// ActionSubmissionRequestChangesNoContent is response for ActionSubmissionRequestChanges operation.
type ActionSubmissionRequestChangesNoContent struct{}
// ActionSubmissionRequestChangesOK is response for ActionSubmissionRequestChanges operation.
type ActionSubmissionRequestChangesOK struct{}
// ActionSubmissionRevokeNoContent is response for ActionSubmissionRevoke operation.
type ActionSubmissionRevokeNoContent struct{}
// ActionSubmissionRevokeOK is response for ActionSubmissionRevoke operation.
type ActionSubmissionRevokeOK struct{}
// ActionSubmissionSubmitNoContent is response for ActionSubmissionSubmit operation.
type ActionSubmissionSubmitNoContent struct{}
// ActionSubmissionSubmitOK is response for ActionSubmissionSubmit operation.
type ActionSubmissionSubmitOK struct{}
// ActionSubmissionTriggerPublishNoContent is response for ActionSubmissionTriggerPublish operation.
type ActionSubmissionTriggerPublishNoContent struct{}
// ActionSubmissionTriggerPublishOK is response for ActionSubmissionTriggerPublish operation.
type ActionSubmissionTriggerPublishOK struct{}
// ActionSubmissionTriggerValidateNoContent is response for ActionSubmissionTriggerValidate operation.
type ActionSubmissionTriggerValidateNoContent struct{}
// ActionSubmissionTriggerValidateOK is response for ActionSubmissionTriggerValidate operation.
type ActionSubmissionTriggerValidateOK struct{}
// ActionSubmissionValidateNoContent is response for ActionSubmissionValidate operation.
type ActionSubmissionValidateNoContent struct{}
// ActionSubmissionValidateOK is response for ActionSubmissionValidate operation.
type ActionSubmissionValidateOK struct{}
type CookieAuth struct {
APIKey string
@ -48,12 +48,6 @@ func (s *CookieAuth) SetAPIKey(val string) {
s.APIKey = val
}
// DeleteScriptNoContent is response for DeleteScript operation.
type DeleteScriptNoContent struct{}
// DeleteScriptPolicyNoContent is response for DeleteScriptPolicy operation.
type DeleteScriptPolicyNoContent struct{}
// Represents error object.
// Ref: #/components/schemas/Error
type Error struct {
@ -109,19 +103,65 @@ func (s *ErrorStatusCode) SetResponse(val Error) {
// Ref: #/components/schemas/Id
type ID struct {
ID int64 `json:"ID"`
ID OptInt64 `json:"ID"`
}
// GetID returns the value of ID.
func (s *ID) GetID() int64 {
func (s *ID) GetID() OptInt64 {
return s.ID
}
// SetID sets the value of ID.
func (s *ID) SetID(val int64) {
func (s *ID) SetID(val OptInt64) {
s.ID = val
}
// NewOptBool returns new OptBool with value set to v.
func NewOptBool(v bool) OptBool {
return OptBool{
Value: v,
Set: true,
}
}
// OptBool is optional bool.
type OptBool struct {
Value bool
Set bool
}
// IsSet returns true if OptBool was set.
func (o OptBool) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptBool) Reset() {
var v bool
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptBool) SetTo(v bool) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptBool) Get() (v bool, ok bool) {
if !o.Set {
return v, false
}
return o.Value, true
}
// Or returns value if set, or given parameter if does not.
func (o OptBool) Or(d bool) bool {
if v, ok := o.Get(); ok {
return v
}
return d
}
// NewOptInt32 returns new OptInt32 with value set to v.
func NewOptInt32(v int32) OptInt32 {
return OptInt32{
@ -260,6 +300,52 @@ func (o OptString) Or(d string) string {
return d
}
// NewOptSubmissionCreate returns new OptSubmissionCreate with value set to v.
func NewOptSubmissionCreate(v SubmissionCreate) OptSubmissionCreate {
return OptSubmissionCreate{
Value: v,
Set: true,
}
}
// OptSubmissionCreate is optional SubmissionCreate.
type OptSubmissionCreate struct {
Value SubmissionCreate
Set bool
}
// IsSet returns true if OptSubmissionCreate was set.
func (o OptSubmissionCreate) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptSubmissionCreate) Reset() {
var v SubmissionCreate
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptSubmissionCreate) SetTo(v SubmissionCreate) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptSubmissionCreate) Get() (v SubmissionCreate, ok bool) {
if !o.Set {
return v, false
}
return o.Value, true
}
// Or returns value if set, or given parameter if does not.
func (o OptSubmissionCreate) Or(d SubmissionCreate) SubmissionCreate {
if v, ok := o.Get(); ok {
return v
}
return d
}
// NewOptSubmissionFilter returns new OptSubmissionFilter with value set to v.
func NewOptSubmissionFilter(v SubmissionFilter) OptSubmissionFilter {
return OptSubmissionFilter{
@ -332,322 +418,75 @@ func (s *Pagination) SetLimit(val int32) {
s.Limit = val
}
// Ref: #/components/schemas/Script
type Script struct {
ID int64 `json:"ID"`
Hash string `json:"Hash"`
Source string `json:"Source"`
SubmissionID int64 `json:"SubmissionID"`
}
// PatchSubmissionCompletedOK is response for PatchSubmissionCompleted operation.
type PatchSubmissionCompletedOK struct{}
// GetID returns the value of ID.
func (s *Script) GetID() int64 {
return s.ID
}
// GetHash returns the value of Hash.
func (s *Script) GetHash() string {
return s.Hash
}
// GetSource returns the value of Source.
func (s *Script) GetSource() string {
return s.Source
}
// GetSubmissionID returns the value of SubmissionID.
func (s *Script) GetSubmissionID() int64 {
return s.SubmissionID
}
// SetID sets the value of ID.
func (s *Script) SetID(val int64) {
s.ID = val
}
// SetHash sets the value of Hash.
func (s *Script) SetHash(val string) {
s.Hash = val
}
// SetSource sets the value of Source.
func (s *Script) SetSource(val string) {
s.Source = val
}
// SetSubmissionID sets the value of SubmissionID.
func (s *Script) SetSubmissionID(val int64) {
s.SubmissionID = val
}
// Ref: #/components/schemas/ScriptCreate
type ScriptCreate struct {
Source string `json:"Source"`
SubmissionID OptInt64 `json:"SubmissionID"`
}
// GetSource returns the value of Source.
func (s *ScriptCreate) GetSource() string {
return s.Source
}
// GetSubmissionID returns the value of SubmissionID.
func (s *ScriptCreate) GetSubmissionID() OptInt64 {
return s.SubmissionID
}
// SetSource sets the value of Source.
func (s *ScriptCreate) SetSource(val string) {
s.Source = val
}
// SetSubmissionID sets the value of SubmissionID.
func (s *ScriptCreate) SetSubmissionID(val OptInt64) {
s.SubmissionID = val
}
// Ref: #/components/schemas/ScriptPolicy
type ScriptPolicy struct {
ID int64 `json:"ID"`
FromScriptHash string `json:"FromScriptHash"`
ToScriptID int64 `json:"ToScriptID"`
Policy int32 `json:"Policy"`
}
// GetID returns the value of ID.
func (s *ScriptPolicy) GetID() int64 {
return s.ID
}
// GetFromScriptHash returns the value of FromScriptHash.
func (s *ScriptPolicy) GetFromScriptHash() string {
return s.FromScriptHash
}
// GetToScriptID returns the value of ToScriptID.
func (s *ScriptPolicy) GetToScriptID() int64 {
return s.ToScriptID
}
// GetPolicy returns the value of Policy.
func (s *ScriptPolicy) GetPolicy() int32 {
return s.Policy
}
// SetID sets the value of ID.
func (s *ScriptPolicy) SetID(val int64) {
s.ID = val
}
// SetFromScriptHash sets the value of FromScriptHash.
func (s *ScriptPolicy) SetFromScriptHash(val string) {
s.FromScriptHash = val
}
// SetToScriptID sets the value of ToScriptID.
func (s *ScriptPolicy) SetToScriptID(val int64) {
s.ToScriptID = val
}
// SetPolicy sets the value of Policy.
func (s *ScriptPolicy) SetPolicy(val int32) {
s.Policy = val
}
// Ref: #/components/schemas/ScriptPolicyCreate
type ScriptPolicyCreate struct {
FromScriptID int64 `json:"FromScriptID"`
ToScriptID int64 `json:"ToScriptID"`
Policy int32 `json:"Policy"`
}
// GetFromScriptID returns the value of FromScriptID.
func (s *ScriptPolicyCreate) GetFromScriptID() int64 {
return s.FromScriptID
}
// GetToScriptID returns the value of ToScriptID.
func (s *ScriptPolicyCreate) GetToScriptID() int64 {
return s.ToScriptID
}
// GetPolicy returns the value of Policy.
func (s *ScriptPolicyCreate) GetPolicy() int32 {
return s.Policy
}
// SetFromScriptID sets the value of FromScriptID.
func (s *ScriptPolicyCreate) SetFromScriptID(val int64) {
s.FromScriptID = val
}
// SetToScriptID sets the value of ToScriptID.
func (s *ScriptPolicyCreate) SetToScriptID(val int64) {
s.ToScriptID = val
}
// SetPolicy sets the value of Policy.
func (s *ScriptPolicyCreate) SetPolicy(val int32) {
s.Policy = val
}
// Ref: #/components/schemas/ScriptPolicyUpdate
type ScriptPolicyUpdate struct {
ID int64 `json:"ID"`
FromScriptID OptInt64 `json:"FromScriptID"`
ToScriptID OptInt64 `json:"ToScriptID"`
Policy OptInt32 `json:"Policy"`
}
// GetID returns the value of ID.
func (s *ScriptPolicyUpdate) GetID() int64 {
return s.ID
}
// GetFromScriptID returns the value of FromScriptID.
func (s *ScriptPolicyUpdate) GetFromScriptID() OptInt64 {
return s.FromScriptID
}
// GetToScriptID returns the value of ToScriptID.
func (s *ScriptPolicyUpdate) GetToScriptID() OptInt64 {
return s.ToScriptID
}
// GetPolicy returns the value of Policy.
func (s *ScriptPolicyUpdate) GetPolicy() OptInt32 {
return s.Policy
}
// SetID sets the value of ID.
func (s *ScriptPolicyUpdate) SetID(val int64) {
s.ID = val
}
// SetFromScriptID sets the value of FromScriptID.
func (s *ScriptPolicyUpdate) SetFromScriptID(val OptInt64) {
s.FromScriptID = val
}
// SetToScriptID sets the value of ToScriptID.
func (s *ScriptPolicyUpdate) SetToScriptID(val OptInt64) {
s.ToScriptID = val
}
// SetPolicy sets the value of Policy.
func (s *ScriptPolicyUpdate) SetPolicy(val OptInt32) {
s.Policy = val
}
// Ref: #/components/schemas/ScriptUpdate
type ScriptUpdate struct {
ID int64 `json:"ID"`
Source OptString `json:"Source"`
SubmissionID OptInt64 `json:"SubmissionID"`
}
// GetID returns the value of ID.
func (s *ScriptUpdate) GetID() int64 {
return s.ID
}
// GetSource returns the value of Source.
func (s *ScriptUpdate) GetSource() OptString {
return s.Source
}
// GetSubmissionID returns the value of SubmissionID.
func (s *ScriptUpdate) GetSubmissionID() OptInt64 {
return s.SubmissionID
}
// SetID sets the value of ID.
func (s *ScriptUpdate) SetID(val int64) {
s.ID = val
}
// SetSource sets the value of Source.
func (s *ScriptUpdate) SetSource(val OptString) {
s.Source = val
}
// SetSubmissionID sets the value of SubmissionID.
func (s *ScriptUpdate) SetSubmissionID(val OptInt64) {
s.SubmissionID = val
}
// SetSubmissionCompletedNoContent is response for SetSubmissionCompleted operation.
type SetSubmissionCompletedNoContent struct{}
// PatchSubmissionModelOK is response for PatchSubmissionModel operation.
type PatchSubmissionModelOK struct{}
// Ref: #/components/schemas/Submission
type Submission struct {
ID int64 `json:"ID"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
CreatedAt int64 `json:"CreatedAt"`
UpdatedAt int64 `json:"UpdatedAt"`
Submitter int64 `json:"Submitter"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
Completed bool `json:"Completed"`
SubmissionType int32 `json:"SubmissionType"`
TargetAssetID OptInt64 `json:"TargetAssetID"`
StatusID int32 `json:"StatusID"`
ID OptInt64 `json:"ID"`
DisplayName OptString `json:"DisplayName"`
Creator OptString `json:"Creator"`
GameID OptInt32 `json:"GameID"`
Date OptInt64 `json:"Date"`
Submitter OptInt64 `json:"Submitter"`
AssetID OptInt64 `json:"AssetID"`
AssetVersion OptInt64 `json:"AssetVersion"`
Completed OptBool `json:"Completed"`
SubmissionType OptInt32 `json:"SubmissionType"`
TargetAssetID OptInt64 `json:"TargetAssetID"`
StatusID OptInt32 `json:"StatusID"`
}
// GetID returns the value of ID.
func (s *Submission) GetID() int64 {
func (s *Submission) GetID() OptInt64 {
return s.ID
}
// GetDisplayName returns the value of DisplayName.
func (s *Submission) GetDisplayName() string {
func (s *Submission) GetDisplayName() OptString {
return s.DisplayName
}
// GetCreator returns the value of Creator.
func (s *Submission) GetCreator() string {
func (s *Submission) GetCreator() OptString {
return s.Creator
}
// GetGameID returns the value of GameID.
func (s *Submission) GetGameID() int32 {
func (s *Submission) GetGameID() OptInt32 {
return s.GameID
}
// GetCreatedAt returns the value of CreatedAt.
func (s *Submission) GetCreatedAt() int64 {
return s.CreatedAt
}
// GetUpdatedAt returns the value of UpdatedAt.
func (s *Submission) GetUpdatedAt() int64 {
return s.UpdatedAt
// GetDate returns the value of Date.
func (s *Submission) GetDate() OptInt64 {
return s.Date
}
// GetSubmitter returns the value of Submitter.
func (s *Submission) GetSubmitter() int64 {
func (s *Submission) GetSubmitter() OptInt64 {
return s.Submitter
}
// GetAssetID returns the value of AssetID.
func (s *Submission) GetAssetID() int64 {
func (s *Submission) GetAssetID() OptInt64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *Submission) GetAssetVersion() int64 {
func (s *Submission) GetAssetVersion() OptInt64 {
return s.AssetVersion
}
// GetCompleted returns the value of Completed.
func (s *Submission) GetCompleted() bool {
func (s *Submission) GetCompleted() OptBool {
return s.Completed
}
// GetSubmissionType returns the value of SubmissionType.
func (s *Submission) GetSubmissionType() int32 {
func (s *Submission) GetSubmissionType() OptInt32 {
return s.SubmissionType
}
@ -657,62 +496,57 @@ func (s *Submission) GetTargetAssetID() OptInt64 {
}
// GetStatusID returns the value of StatusID.
func (s *Submission) GetStatusID() int32 {
func (s *Submission) GetStatusID() OptInt32 {
return s.StatusID
}
// SetID sets the value of ID.
func (s *Submission) SetID(val int64) {
func (s *Submission) SetID(val OptInt64) {
s.ID = val
}
// SetDisplayName sets the value of DisplayName.
func (s *Submission) SetDisplayName(val string) {
func (s *Submission) SetDisplayName(val OptString) {
s.DisplayName = val
}
// SetCreator sets the value of Creator.
func (s *Submission) SetCreator(val string) {
func (s *Submission) SetCreator(val OptString) {
s.Creator = val
}
// SetGameID sets the value of GameID.
func (s *Submission) SetGameID(val int32) {
func (s *Submission) SetGameID(val OptInt32) {
s.GameID = val
}
// SetCreatedAt sets the value of CreatedAt.
func (s *Submission) SetCreatedAt(val int64) {
s.CreatedAt = val
}
// SetUpdatedAt sets the value of UpdatedAt.
func (s *Submission) SetUpdatedAt(val int64) {
s.UpdatedAt = val
// SetDate sets the value of Date.
func (s *Submission) SetDate(val OptInt64) {
s.Date = val
}
// SetSubmitter sets the value of Submitter.
func (s *Submission) SetSubmitter(val int64) {
func (s *Submission) SetSubmitter(val OptInt64) {
s.Submitter = val
}
// SetAssetID sets the value of AssetID.
func (s *Submission) SetAssetID(val int64) {
func (s *Submission) SetAssetID(val OptInt64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *Submission) SetAssetVersion(val int64) {
func (s *Submission) SetAssetVersion(val OptInt64) {
s.AssetVersion = val
}
// SetCompleted sets the value of Completed.
func (s *Submission) SetCompleted(val bool) {
func (s *Submission) SetCompleted(val OptBool) {
s.Completed = val
}
// SetSubmissionType sets the value of SubmissionType.
func (s *Submission) SetSubmissionType(val int32) {
func (s *Submission) SetSubmissionType(val OptInt32) {
s.SubmissionType = val
}
@ -722,75 +556,97 @@ func (s *Submission) SetTargetAssetID(val OptInt64) {
}
// SetStatusID sets the value of StatusID.
func (s *Submission) SetStatusID(val int32) {
func (s *Submission) SetStatusID(val OptInt32) {
s.StatusID = val
}
// Ref: #/components/schemas/SubmissionCreate
type SubmissionCreate struct {
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
TargetAssetID OptInt64 `json:"TargetAssetID"`
DisplayName OptString `json:"DisplayName"`
Creator OptString `json:"Creator"`
GameID OptInt32 `json:"GameID"`
Submitter OptInt64 `json:"Submitter"`
AssetID OptInt64 `json:"AssetID"`
AssetVersion OptInt64 `json:"AssetVersion"`
SubmissionType OptInt32 `json:"SubmissionType"`
TargetAssetID OptInt64 `json:"TargetAssetID"`
}
// GetDisplayName returns the value of DisplayName.
func (s *SubmissionCreate) GetDisplayName() string {
func (s *SubmissionCreate) GetDisplayName() OptString {
return s.DisplayName
}
// GetCreator returns the value of Creator.
func (s *SubmissionCreate) GetCreator() string {
func (s *SubmissionCreate) GetCreator() OptString {
return s.Creator
}
// GetGameID returns the value of GameID.
func (s *SubmissionCreate) GetGameID() int32 {
func (s *SubmissionCreate) GetGameID() OptInt32 {
return s.GameID
}
// GetSubmitter returns the value of Submitter.
func (s *SubmissionCreate) GetSubmitter() OptInt64 {
return s.Submitter
}
// GetAssetID returns the value of AssetID.
func (s *SubmissionCreate) GetAssetID() int64 {
func (s *SubmissionCreate) GetAssetID() OptInt64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *SubmissionCreate) GetAssetVersion() int64 {
func (s *SubmissionCreate) GetAssetVersion() OptInt64 {
return s.AssetVersion
}
// GetSubmissionType returns the value of SubmissionType.
func (s *SubmissionCreate) GetSubmissionType() OptInt32 {
return s.SubmissionType
}
// 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) {
func (s *SubmissionCreate) SetDisplayName(val OptString) {
s.DisplayName = val
}
// SetCreator sets the value of Creator.
func (s *SubmissionCreate) SetCreator(val string) {
func (s *SubmissionCreate) SetCreator(val OptString) {
s.Creator = val
}
// SetGameID sets the value of GameID.
func (s *SubmissionCreate) SetGameID(val int32) {
func (s *SubmissionCreate) SetGameID(val OptInt32) {
s.GameID = val
}
// SetSubmitter sets the value of Submitter.
func (s *SubmissionCreate) SetSubmitter(val OptInt64) {
s.Submitter = val
}
// SetAssetID sets the value of AssetID.
func (s *SubmissionCreate) SetAssetID(val int64) {
func (s *SubmissionCreate) SetAssetID(val OptInt64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *SubmissionCreate) SetAssetVersion(val int64) {
func (s *SubmissionCreate) SetAssetVersion(val OptInt64) {
s.AssetVersion = val
}
// SetSubmissionType sets the value of SubmissionType.
func (s *SubmissionCreate) SetSubmissionType(val OptInt32) {
s.SubmissionType = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *SubmissionCreate) SetTargetAssetID(val OptInt64) {
s.TargetAssetID = val
@ -798,14 +654,15 @@ func (s *SubmissionCreate) SetTargetAssetID(val OptInt64) {
// Ref: #/components/schemas/SubmissionFilter
type SubmissionFilter struct {
ID int64 `json:"ID"`
ID OptInt64 `json:"ID"`
DisplayName OptString `json:"DisplayName"`
Creator OptString `json:"Creator"`
GameID OptInt32 `json:"GameID"`
Date OptInt64 `json:"Date"`
}
// GetID returns the value of ID.
func (s *SubmissionFilter) GetID() int64 {
func (s *SubmissionFilter) GetID() OptInt64 {
return s.ID
}
@ -824,8 +681,13 @@ func (s *SubmissionFilter) GetGameID() OptInt32 {
return s.GameID
}
// GetDate returns the value of Date.
func (s *SubmissionFilter) GetDate() OptInt64 {
return s.Date
}
// SetID sets the value of ID.
func (s *SubmissionFilter) SetID(val int64) {
func (s *SubmissionFilter) SetID(val OptInt64) {
s.ID = val
}
@ -844,11 +706,7 @@ func (s *SubmissionFilter) SetGameID(val OptInt32) {
s.GameID = val
}
// UpdateScriptNoContent is response for UpdateScript operation.
type UpdateScriptNoContent struct{}
// UpdateScriptPolicyNoContent is response for UpdateScriptPolicy operation.
type UpdateScriptPolicyNoContent struct{}
// UpdateSubmissionModelNoContent is response for UpdateSubmissionModel operation.
type UpdateSubmissionModelNoContent struct{}
// SetDate sets the value of Date.
func (s *SubmissionFilter) SetDate(val OptInt64) {
s.Date = val
}

View File

@ -35,7 +35,7 @@ func findAuthorization(h http.Header, prefix string) (string, bool) {
func (s *Server) securityCookieAuth(ctx context.Context, operationName OperationName, req *http.Request) (context.Context, bool, error) {
var t CookieAuth
const parameterName = "session_id"
const parameterName = "SESSIONID"
var value string
switch cookie, err := req.Cookie(parameterName); {
case err == nil: // if NO error
@ -67,7 +67,7 @@ func (s *Client) securityCookieAuth(ctx context.Context, operationName Operation
return errors.Wrap(err, "security source \"CookieAuth\"")
}
req.AddCookie(&http.Cookie{
Name: "session_id",
Name: "SESSIONID",
Value: t.APIKey,
})
return nil

View File

@ -10,100 +10,58 @@ import (
type Handler interface {
// ActionSubmissionPublish implements actionSubmissionPublish operation.
//
// (Internal endpoint) Role Validator changes status from Publishing -> Published.
// Role Validator changes status from Publishing -> Published.
//
// POST /submissions/{SubmissionID}/status/validator-published
// PATCH /submissions/{SubmissionID}/status/publish
ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error
// ActionSubmissionReject implements actionSubmissionReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
//
// POST /submissions/{SubmissionID}/status/reject
// PATCH /submissions/{SubmissionID}/status/reject
ActionSubmissionReject(ctx context.Context, params ActionSubmissionRejectParams) error
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
//
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
//
// POST /submissions/{SubmissionID}/status/request-changes
// PATCH /submissions/{SubmissionID}/status/request-changes
ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error
// ActionSubmissionRevoke implements actionSubmissionRevoke operation.
//
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
//
// POST /submissions/{SubmissionID}/status/revoke
// PATCH /submissions/{SubmissionID}/status/revoke
ActionSubmissionRevoke(ctx context.Context, params ActionSubmissionRevokeParams) error
// ActionSubmissionSubmit implements actionSubmissionSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/submit
// PATCH /submissions/{SubmissionID}/status/submit
ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error
// ActionSubmissionTriggerPublish implements actionSubmissionTriggerPublish operation.
//
// Role Admin changes status from Validated -> Publishing.
//
// POST /submissions/{SubmissionID}/status/trigger-publish
// PATCH /submissions/{SubmissionID}/status/trigger-publish
ActionSubmissionTriggerPublish(ctx context.Context, params ActionSubmissionTriggerPublishParams) error
// ActionSubmissionTriggerValidate implements actionSubmissionTriggerValidate operation.
//
// Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
//
// POST /submissions/{SubmissionID}/status/trigger-validate
// PATCH /submissions/{SubmissionID}/status/trigger-validate
ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error
// ActionSubmissionValidate implements actionSubmissionValidate operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
// Role Validator changes status from Validating -> Validated.
//
// POST /submissions/{SubmissionID}/status/validator-validated
// PATCH /submissions/{SubmissionID}/status/validate
ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
CreateScript(ctx context.Context, req *ScriptCreate) (*ID, error)
// CreateScriptPolicy implements createScriptPolicy operation.
//
// Create a new script policy.
//
// POST /script-policy
CreateScriptPolicy(ctx context.Context, req *ScriptPolicyCreate) (*ID, error)
// CreateSubmission implements createSubmission operation.
//
// Create new submission.
//
// POST /submissions
CreateSubmission(ctx context.Context, req *SubmissionCreate) (*ID, error)
// DeleteScript implements deleteScript operation.
//
// Delete the specified script by ID.
//
// DELETE /scripts/{ScriptID}
DeleteScript(ctx context.Context, params DeleteScriptParams) error
// DeleteScriptPolicy implements deleteScriptPolicy operation.
//
// Delete the specified script policy by ID.
//
// DELETE /script-policy/id/{ScriptPolicyID}
DeleteScriptPolicy(ctx context.Context, params DeleteScriptPolicyParams) error
// GetScript implements getScript operation.
//
// Get the specified script by ID.
//
// GET /scripts/{ScriptID}
GetScript(ctx context.Context, params GetScriptParams) (*Script, error)
// GetScriptPolicy implements getScriptPolicy operation.
//
// Get the specified script policy by ID.
//
// GET /script-policy/id/{ScriptPolicyID}
GetScriptPolicy(ctx context.Context, params GetScriptPolicyParams) (*ScriptPolicy, error)
// GetScriptPolicyFromHash implements getScriptPolicyFromHash operation.
//
// Get the policy for the given hash of script source code.
//
// GET /script-policy/hash/{FromScriptHash}
GetScriptPolicyFromHash(ctx context.Context, params GetScriptPolicyFromHashParams) (*ScriptPolicy, error)
CreateSubmission(ctx context.Context, req OptSubmissionCreate) (*ID, error)
// GetSubmission implements getSubmission operation.
//
// Retrieve map with ID.
@ -116,30 +74,18 @@ type Handler interface {
//
// GET /submissions
ListSubmissions(ctx context.Context, params ListSubmissionsParams) ([]Submission, error)
// SetSubmissionCompleted implements setSubmissionCompleted operation.
// PatchSubmissionCompleted implements patchSubmissionCompleted operation.
//
// Retrieve map with ID.
//
// POST /submissions/{SubmissionID}/completed
SetSubmissionCompleted(ctx context.Context, params SetSubmissionCompletedParams) error
// UpdateScript implements updateScript operation.
//
// Update the specified script by ID.
//
// POST /scripts/{ScriptID}
UpdateScript(ctx context.Context, req *ScriptUpdate, params UpdateScriptParams) error
// UpdateScriptPolicy implements updateScriptPolicy operation.
//
// Update the specified script policy by ID.
//
// POST /script-policy/id/{ScriptPolicyID}
UpdateScriptPolicy(ctx context.Context, req *ScriptPolicyUpdate, params UpdateScriptPolicyParams) error
// UpdateSubmissionModel implements updateSubmissionModel operation.
// PATCH /submissions/{SubmissionID}/completed
PatchSubmissionCompleted(ctx context.Context, params PatchSubmissionCompletedParams) error
// PatchSubmissionModel implements patchSubmissionModel operation.
//
// Update model following role restrictions.
//
// POST /submissions/{SubmissionID}/model
UpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) error
// PATCH /submissions/{SubmissionID}/model
PatchSubmissionModel(ctx context.Context, params PatchSubmissionModelParams) error
// NewError creates *ErrorStatusCode from error returned by handler.
//
// Used for common default response.

View File

@ -15,9 +15,9 @@ var _ Handler = UnimplementedHandler{}
// ActionSubmissionPublish implements actionSubmissionPublish operation.
//
// (Internal endpoint) Role Validator changes status from Publishing -> Published.
// Role Validator changes status from Publishing -> Published.
//
// POST /submissions/{SubmissionID}/status/validator-published
// PATCH /submissions/{SubmissionID}/status/publish
func (UnimplementedHandler) ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error {
return ht.ErrNotImplemented
}
@ -26,7 +26,7 @@ func (UnimplementedHandler) ActionSubmissionPublish(ctx context.Context, params
//
// Role Reviewer changes status from Submitted -> Rejected.
//
// POST /submissions/{SubmissionID}/status/reject
// PATCH /submissions/{SubmissionID}/status/reject
func (UnimplementedHandler) ActionSubmissionReject(ctx context.Context, params ActionSubmissionRejectParams) error {
return ht.ErrNotImplemented
}
@ -35,7 +35,7 @@ func (UnimplementedHandler) ActionSubmissionReject(ctx context.Context, params A
//
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
//
// POST /submissions/{SubmissionID}/status/request-changes
// PATCH /submissions/{SubmissionID}/status/request-changes
func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error {
return ht.ErrNotImplemented
}
@ -44,7 +44,7 @@ func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context,
//
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
//
// POST /submissions/{SubmissionID}/status/revoke
// PATCH /submissions/{SubmissionID}/status/revoke
func (UnimplementedHandler) ActionSubmissionRevoke(ctx context.Context, params ActionSubmissionRevokeParams) error {
return ht.ErrNotImplemented
}
@ -53,7 +53,7 @@ func (UnimplementedHandler) ActionSubmissionRevoke(ctx context.Context, params A
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/submit
// PATCH /submissions/{SubmissionID}/status/submit
func (UnimplementedHandler) ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error {
return ht.ErrNotImplemented
}
@ -62,7 +62,7 @@ func (UnimplementedHandler) ActionSubmissionSubmit(ctx context.Context, params A
//
// Role Admin changes status from Validated -> Publishing.
//
// POST /submissions/{SubmissionID}/status/trigger-publish
// PATCH /submissions/{SubmissionID}/status/trigger-publish
func (UnimplementedHandler) ActionSubmissionTriggerPublish(ctx context.Context, params ActionSubmissionTriggerPublishParams) error {
return ht.ErrNotImplemented
}
@ -71,89 +71,26 @@ func (UnimplementedHandler) ActionSubmissionTriggerPublish(ctx context.Context,
//
// Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
//
// POST /submissions/{SubmissionID}/status/trigger-validate
// PATCH /submissions/{SubmissionID}/status/trigger-validate
func (UnimplementedHandler) ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionValidate implements actionSubmissionValidate operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
// Role Validator changes status from Validating -> Validated.
//
// POST /submissions/{SubmissionID}/status/validator-validated
// PATCH /submissions/{SubmissionID}/status/validate
func (UnimplementedHandler) ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error {
return ht.ErrNotImplemented
}
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
func (UnimplementedHandler) CreateScript(ctx context.Context, req *ScriptCreate) (r *ID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateScriptPolicy implements createScriptPolicy operation.
//
// Create a new script policy.
//
// POST /script-policy
func (UnimplementedHandler) CreateScriptPolicy(ctx context.Context, req *ScriptPolicyCreate) (r *ID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateSubmission implements createSubmission operation.
//
// Create new submission.
//
// POST /submissions
func (UnimplementedHandler) CreateSubmission(ctx context.Context, req *SubmissionCreate) (r *ID, _ error) {
return r, ht.ErrNotImplemented
}
// DeleteScript implements deleteScript operation.
//
// Delete the specified script by ID.
//
// DELETE /scripts/{ScriptID}
func (UnimplementedHandler) DeleteScript(ctx context.Context, params DeleteScriptParams) error {
return ht.ErrNotImplemented
}
// DeleteScriptPolicy implements deleteScriptPolicy operation.
//
// Delete the specified script policy by ID.
//
// DELETE /script-policy/id/{ScriptPolicyID}
func (UnimplementedHandler) DeleteScriptPolicy(ctx context.Context, params DeleteScriptPolicyParams) error {
return ht.ErrNotImplemented
}
// GetScript implements getScript operation.
//
// Get the specified script by ID.
//
// GET /scripts/{ScriptID}
func (UnimplementedHandler) GetScript(ctx context.Context, params GetScriptParams) (r *Script, _ error) {
return r, ht.ErrNotImplemented
}
// GetScriptPolicy implements getScriptPolicy operation.
//
// Get the specified script policy by ID.
//
// GET /script-policy/id/{ScriptPolicyID}
func (UnimplementedHandler) GetScriptPolicy(ctx context.Context, params GetScriptPolicyParams) (r *ScriptPolicy, _ error) {
return r, ht.ErrNotImplemented
}
// GetScriptPolicyFromHash implements getScriptPolicyFromHash operation.
//
// Get the policy for the given hash of script source code.
//
// GET /script-policy/hash/{FromScriptHash}
func (UnimplementedHandler) GetScriptPolicyFromHash(ctx context.Context, params GetScriptPolicyFromHashParams) (r *ScriptPolicy, _ error) {
func (UnimplementedHandler) CreateSubmission(ctx context.Context, req OptSubmissionCreate) (r *ID, _ error) {
return r, ht.ErrNotImplemented
}
@ -175,39 +112,21 @@ func (UnimplementedHandler) ListSubmissions(ctx context.Context, params ListSubm
return r, ht.ErrNotImplemented
}
// SetSubmissionCompleted implements setSubmissionCompleted operation.
// PatchSubmissionCompleted implements patchSubmissionCompleted operation.
//
// Retrieve map with ID.
//
// POST /submissions/{SubmissionID}/completed
func (UnimplementedHandler) SetSubmissionCompleted(ctx context.Context, params SetSubmissionCompletedParams) error {
// PATCH /submissions/{SubmissionID}/completed
func (UnimplementedHandler) PatchSubmissionCompleted(ctx context.Context, params PatchSubmissionCompletedParams) error {
return ht.ErrNotImplemented
}
// UpdateScript implements updateScript operation.
//
// Update the specified script by ID.
//
// POST /scripts/{ScriptID}
func (UnimplementedHandler) UpdateScript(ctx context.Context, req *ScriptUpdate, params UpdateScriptParams) error {
return ht.ErrNotImplemented
}
// UpdateScriptPolicy implements updateScriptPolicy operation.
//
// Update the specified script policy by ID.
//
// POST /script-policy/id/{ScriptPolicyID}
func (UnimplementedHandler) UpdateScriptPolicy(ctx context.Context, req *ScriptPolicyUpdate, params UpdateScriptPolicyParams) error {
return ht.ErrNotImplemented
}
// UpdateSubmissionModel implements updateSubmissionModel operation.
// PatchSubmissionModel implements patchSubmissionModel operation.
//
// Update model following role restrictions.
//
// POST /submissions/{SubmissionID}/model
func (UnimplementedHandler) UpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) error {
// PATCH /submissions/{SubmissionID}/model
func (UnimplementedHandler) PatchSubmissionModel(ctx context.Context, params PatchSubmissionModelParams) error {
return ht.ErrNotImplemented
}

View File

@ -124,7 +124,10 @@ func (s *Pagination) DecodeURI(d uri.Decoder) error {
// EncodeURI encodes SubmissionFilter as URI form.
func (s *SubmissionFilter) EncodeURI(e uri.Encoder) error {
if err := e.EncodeField("ID", func(e uri.Encoder) error {
return e.EncodeValue(conv.Int64ToString(s.ID))
if val, ok := s.ID.Get(); ok {
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil {
return errors.Wrap(err, "encode field \"ID\"")
}
@ -152,14 +155,23 @@ func (s *SubmissionFilter) EncodeURI(e uri.Encoder) error {
}); err != nil {
return errors.Wrap(err, "encode field \"GameID\"")
}
if err := e.EncodeField("Date", func(e uri.Encoder) error {
if val, ok := s.Date.Get(); ok {
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil {
return errors.Wrap(err, "encode field \"Date\"")
}
return nil
}
var uriFieldsNameOfSubmissionFilter = [4]string{
var uriFieldsNameOfSubmissionFilter = [5]string{
0: "ID",
1: "DisplayName",
2: "Creator",
3: "GameID",
4: "Date",
}
// DecodeURI decodes SubmissionFilter from URI form.
@ -167,24 +179,29 @@ func (s *SubmissionFilter) DecodeURI(d uri.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode SubmissionFilter to nil")
}
var requiredBitSet [1]uint8
if err := d.DecodeFields(func(k string, d uri.Decoder) error {
switch k {
case "ID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
var sDotIDVal int64
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
sDotIDVal = c
return nil
}(); err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
s.ID = c
s.ID.SetTo(sDotIDVal)
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ID\"")
@ -261,6 +278,30 @@ func (s *SubmissionFilter) DecodeURI(d uri.Decoder) error {
}(); err != nil {
return errors.Wrap(err, "decode field \"GameID\"")
}
case "Date":
if err := func() error {
var sDotDateVal int64
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
sDotDateVal = c
return nil
}(); err != nil {
return err
}
s.Date.SetTo(sDotDateVal)
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Date\"")
}
default:
return nil
}
@ -268,38 +309,6 @@ func (s *SubmissionFilter) DecodeURI(d uri.Decoder) error {
}); err != nil {
return errors.Wrap(err, "decode SubmissionFilter")
}
// 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(uriFieldsNameOfSubmissionFilter) {
name = uriFieldsNameOfSubmissionFilter[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
}

View File

@ -59,317 +59,3 @@ func (s *Pagination) Validate() error {
}
return nil
}
func (s *Script) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.String{
MinLength: 16,
MinLengthSet: true,
MaxLength: 16,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Hash)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Hash",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 1048576,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Source)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Source",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ScriptCreate) 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: 1048576,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Source)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Source",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ScriptPolicy) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.String{
MinLength: 16,
MinLengthSet: true,
MaxLength: 16,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.FromScriptHash)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "FromScriptHash",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ScriptUpdate) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if value, ok := s.Source.Get(); ok {
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 1048576,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(value)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
return err
}
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Source",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *Submission) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.DisplayName)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "DisplayName",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Creator)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Creator",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *SubmissionCreate) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.DisplayName)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "DisplayName",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Creator)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Creator",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *SubmissionFilter) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if value, ok := s.DisplayName.Get(); ok {
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(value)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
return err
}
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "DisplayName",
Error: err,
})
}
if err := func() error {
if value, ok := s.Creator.Get(); ok {
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(value)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
return err
}
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Creator",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}

View File

@ -2,16 +2,15 @@ package cmds
import (
"fmt"
"git.itzana.me/strafesnet/go-grpc/auth"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore"
"git.itzana.me/strafesnet/maps-service/pkg/service"
"github.com/nats-io/nats.go"
log "github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"net/http"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"net/http"
"github.com/nats-io/nats.go"
)
func NewServeCommand() *cli.Command {
@ -62,18 +61,6 @@ func NewServeCommand() *cli.Command {
Value: 8080,
EnvVars: []string{"PORT"},
},
&cli.StringFlag{
Name: "auth-rpc-host",
Usage: "Host of auth rpc",
EnvVars: []string{"AUTH_RPC_HOST"},
Value: "auth-service:8090",
},
&cli.StringFlag{
Name: "nats-host",
Usage: "Host of nats",
EnvVars: []string{"NATS_HOST"},
Value: "nats:4222",
},
},
}
}
@ -83,35 +70,21 @@ func serve(ctx *cli.Context) error {
if err != nil {
log.WithError(err).Fatal("failed to connect database")
}
nc, err := nats.Connect(ctx.String("nats-host"))
nc, err := nats.Connect("nats:4222")
if err != nil {
log.WithError(err).Fatal("failed to connect nats")
}
js, err := nc.JetStream()
if err != nil {
log.WithError(err).Fatal("failed to start jetstream")
}
_, err = js.AddStream(&nats.StreamConfig{
Name: "maptest",
Subjects: []string{"maptest.>"},
Retention: nats.WorkQueuePolicy,
})
if err != nil {
log.WithError(err).Fatal("failed to add stream")
}
svc := &service.Service{
DB: db,
Nats: js,
DB: db,
Nats: nc,
}
conn, err := grpc.Dial(ctx.String("auth-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials()))
conn, err := grpc.Dial("auth-service:8090", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
sec := service.SecurityHandler{
Client: auth.NewAuthServiceClient(conn),
Client: conn,
}
srv, err := api.NewServer(svc, sec, api.WithPathPrefix("/v1"))

View File

@ -7,14 +7,11 @@ import (
)
var (
ErrNotExist = errors.New("resource does not exist")
ErroNoRowsAffected = errors.New("query did not affect any rows")
ErrNotExist = errors.New("resource does not exist")
)
type Datastore interface {
Submissions() Submissions
Scripts() Scripts
ScriptPolicy() ScriptPolicy
}
type Submissions interface {
@ -23,22 +20,6 @@ type Submissions interface {
Create(ctx context.Context, smap model.Submission) (model.Submission, error)
Update(ctx context.Context, id int64, values OptionalMap) error
IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values OptionalMap) error
IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.Status, values OptionalMap) (model.Submission, error)
Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page) ([]model.Submission, error)
}
type Scripts interface {
Get(ctx context.Context, id int64) (model.Script, error)
Create(ctx context.Context, smap model.Script) (model.Script, error)
Update(ctx context.Context, id int64, values OptionalMap) error
Delete(ctx context.Context, id int64) error
}
type ScriptPolicy interface {
Get(ctx context.Context, id int64) (model.ScriptPolicy, error)
GetFromHash(ctx context.Context, hash uint64) (model.ScriptPolicy, error)
Create(ctx context.Context, smap model.ScriptPolicy) (model.ScriptPolicy, error)
Update(ctx context.Context, id int64, values OptionalMap) error
Delete(ctx context.Context, id int64) error
}

View File

@ -30,11 +30,7 @@ func New(ctx *cli.Context) (datastore.Datastore, error) {
}
if ctx.Bool("migrate") {
if err := db.AutoMigrate(
&model.Submission{},
&model.Script{},
&model.ScriptPolicy{},
); err != nil {
if err := db.AutoMigrate(&model.Submission{}); err != nil {
log.WithError(err).Errorln("database migration failed")
return nil, err
}

View File

@ -12,11 +12,3 @@ type Gormstore struct {
func (g Gormstore) Submissions() datastore.Submissions {
return &Submissions{db: g.db}
}
func (g Gormstore) Scripts() datastore.Scripts {
return &Scripts{db: g.db}
}
func (g Gormstore) ScriptPolicy() datastore.ScriptPolicy {
return &ScriptPolicy{db: g.db}
}

View File

@ -1,64 +0,0 @@
package gormstore
import (
"context"
"errors"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"gorm.io/gorm"
)
type ScriptPolicy struct {
db *gorm.DB
}
func (env *ScriptPolicy) Get(ctx context.Context, id int64) (model.ScriptPolicy, error) {
var mdl model.ScriptPolicy
if err := env.db.First(&mdl, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return mdl, datastore.ErrNotExist
}
}
return mdl, nil
}
func (env *ScriptPolicy) GetFromHash(ctx context.Context, hash uint64) (model.ScriptPolicy, error) {
var mdl model.ScriptPolicy
if err := env.db.Model(&model.ScriptPolicy{}).Where("hash = ?", hash).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return mdl, datastore.ErrNotExist
}
}
return mdl, nil
}
func (env *ScriptPolicy) Create(ctx context.Context, smap model.ScriptPolicy) (model.ScriptPolicy, error) {
if err := env.db.Create(&smap).Error; err != nil {
return smap, err
}
return smap, nil
}
func (env *ScriptPolicy) Update(ctx context.Context, id int64, values datastore.OptionalMap) error {
if err := env.db.Model(&model.ScriptPolicy{}).Where("id = ?", id).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *ScriptPolicy) Delete(ctx context.Context, id int64) error {
if err := env.db.Delete(&model.ScriptPolicy{}, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}

View File

@ -1,84 +0,0 @@
package gormstore
import (
"context"
"errors"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"gorm.io/gorm"
)
type Scripts struct {
db *gorm.DB
}
func (env *Scripts) Get(ctx context.Context, id int64) (model.Script, error) {
var mdl model.Script
if err := env.db.First(&mdl, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return mdl, datastore.ErrNotExist
}
}
return mdl, nil
}
func (env *Scripts) GetList(ctx context.Context, id []int64) ([]model.Script, error) {
var mapList []model.Script
if err := env.db.Find(&mapList, "id IN ?", id).Error; err != nil {
return mapList, err
}
return mapList, nil
}
func (env *Scripts) Create(ctx context.Context, smap model.Script) (model.Script, error) {
if err := env.db.Create(&smap).Error; err != nil {
return smap, err
}
return smap, nil
}
func (env *Scripts) Update(ctx context.Context, id int64, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Script{}).Where("id = ?", id).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
// the update can only occur if the status matches one of the provided values.
func (env *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
}
return err
}
return nil
}
func (env *Scripts) Delete(ctx context.Context, id int64) error {
if err := env.db.Delete(&model.Script{}, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *Scripts) List(ctx context.Context, filters datastore.OptionalMap, page model.Page) ([]model.Script, error) {
var maps []model.Script
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
}
return maps, nil
}

View File

@ -7,7 +7,6 @@ import (
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type Submissions struct {
@ -54,7 +53,7 @@ func (env *Submissions) Update(ctx context.Context, id int64, values datastore.O
// the update can only occur if the status matches one of the provided values.
func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Submission{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map()).Error; err != nil {
if err := env.db.Model(&model.Submission{}).Where("id = ?", id).Where("status IN ?",statuses).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
@ -64,29 +63,6 @@ func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, status
return nil
}
// the update can only occur if the status matches one of the provided values.
// returns the updated value
func (env *Submissions) IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) (model.Submission, error) {
var submission model.Submission
result := env.db.Model(&submission).
Clauses(clause.Returning{}).
Where("id = ?", id).
Where("status_id IN ?", statuses).
Updates(values.Map())
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return submission, datastore.ErrNotExist
}
return submission, result.Error
}
if result.RowsAffected == 0 {
return submission, datastore.ErroNoRowsAffected
}
return submission, nil
}
func (env *Submissions) Delete(ctx context.Context, id int64) error {
if err := env.db.Delete(&model.Submission{}, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {

View File

@ -1,32 +0,0 @@
package model
// These represent the information needed in the nats message
// to perform the operation, they are encoded to json
// Requests are sent from maps-service to validator
type ValidateRequest struct {
// submission_id is passed back in the response message
SubmissionID int64
ModelID uint64
ModelVersion uint64
ValidatedModelID uint64 // optional value
}
// Create a new map
type PublishNewRequest struct {
SubmissionID int64
ModelID uint64
ModelVersion uint64
Creator string
DisplayName string
GameID uint32
//games HashSet<GameID>
}
type PublishFixRequest struct {
SubmissionID int64
ModelID uint64
ModelVersion uint64
TargetAssetID uint64
}

View File

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

View File

@ -1,12 +0,0 @@
package model
import "time"
type Script struct {
ID int64 `gorm:"primaryKey"`
Hash uint64
Source string
SubmissionID int64 // which submission did this script first appear in
CreatedAt time.Time
UpdatedAt time.Time
}

View File

@ -4,31 +4,30 @@ import "time"
type Status int32
const (
StatusPublished Status = 8
StatusRejected Status = 7
const(
StatusPublished Status=8
StatusRejected Status=7
StatusPublishing Status = 6
StatusValidated Status = 5
StatusValidating Status = 4
StatusAccepted Status = 3
StatusPublishing Status=6
StatusValidated Status=5
StatusValidating Status=4
StatusAccepted Status=3
StatusChangesRequested Status = 2
StatusSubmitted Status = 1
StatusUnderConstruction Status = 0
StatusChangesRequested Status=2
StatusSubmitted Status=1
StatusUnderConstruction Status=0
)
type Submission struct {
ID int64 `gorm:"primaryKey"`
DisplayName string
Creator string
GameID int32
CreatedAt time.Time
UpdatedAt time.Time
Submitter uint64 // UserID
AssetID uint64
AssetVersion uint64
Completed bool // Has this version of the map been completed at least once on maptest
TargetAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
StatusID Status
ID int64
DisplayName string
Creator string
GameID int32
Date time.Time
Submitter int64 // UserID
AssetID int64
AssetVersion int64
Completed bool
TargetAssetID int64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
StatusID Status
}

View File

@ -1,156 +0,0 @@
package service
import (
"context"
"fmt"
"strconv"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
// CreateScriptPolicy implements createScriptPolicy operation.
//
// Create a new script policy.
//
// POST /script-policy
func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolicyCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
return nil, ErrUserInfo
}
if !userInfo.Roles.ScriptWrite {
return nil, ErrPermissionDenied
}
from_script, err := svc.DB.Scripts().Get(ctx, req.FromScriptID)
if err != nil {
return nil, err
}
// the existence of ToScriptID does not need to be validated because it's checked by a foreign key constraint.
script, err := svc.DB.ScriptPolicy().Create(ctx, model.ScriptPolicy{
ID: 0,
FromScriptHash: from_script.Hash,
ToScriptID: req.ToScriptID,
Policy: model.Policy(req.Policy),
})
if err != nil {
return nil, err
}
return &api.ID{
ID: script.ID,
}, nil
}
// DeleteScriptPolicy implements deleteScriptPolicy operation.
//
// Delete the specified script policy by ID.
//
// DELETE /script-policy/id/{ScriptPolicyID}
func (svc *Service) DeleteScriptPolicy(ctx context.Context, params api.DeleteScriptPolicyParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
return ErrUserInfo
}
if !userInfo.Roles.ScriptWrite {
return ErrPermissionDenied
}
return svc.DB.ScriptPolicy().Delete(ctx, params.ScriptPolicyID)
}
// GetScriptPolicy implements getScriptPolicy operation.
//
// Get the specified script policy by ID.
//
// GET /script-policy/id/{ScriptPolicyID}
func (svc *Service) GetScriptPolicy(ctx context.Context, params api.GetScriptPolicyParams) (*api.ScriptPolicy, error) {
_, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
return nil, ErrUserInfo
}
// Read permission for script policy only requires you to be logged in
policy, err := svc.DB.ScriptPolicy().Get(ctx, params.ScriptPolicyID)
if err != nil {
return nil, err
}
return &api.ScriptPolicy{
ID: policy.ID,
FromScriptHash: fmt.Sprintf("%x", policy.FromScriptHash),
ToScriptID: policy.ToScriptID,
Policy: int32(policy.Policy),
}, nil
}
// GetScriptPolicyFromHash implements getScriptPolicyFromHash operation.
//
// Get the policy for the given hash of script source code.
//
// GET /script-policy/hash/{FromScriptHash}
func (svc *Service) GetScriptPolicyFromHash(ctx context.Context, params api.GetScriptPolicyFromHashParams) (*api.ScriptPolicy, error) {
_, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
return nil, ErrUserInfo
}
// Read permission for script policy only requires you to be logged in
// parse hash from hex
hash, err := strconv.ParseUint(params.FromScriptHash, 16, 64)
if err != nil {
return nil, err
}
policy, err := svc.DB.ScriptPolicy().GetFromHash(ctx, hash)
if err != nil {
return nil, err
}
return &api.ScriptPolicy{
ID: policy.ID,
FromScriptHash: fmt.Sprintf("%x", policy.FromScriptHash),
ToScriptID: policy.ToScriptID,
Policy: int32(policy.Policy),
}, nil
}
// UpdateScriptPolicy implements updateScriptPolicy operation.
//
// Update the specified script policy by ID.
//
// PATCH /script-policy/id/{ScriptPolicyID}
func (svc *Service) UpdateScriptPolicy(ctx context.Context, req *api.ScriptPolicyUpdate, params api.UpdateScriptPolicyParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
return ErrUserInfo
}
if !userInfo.Roles.ScriptWrite {
return ErrPermissionDenied
}
pmap := datastore.Optional()
if from_script_id, ok := req.FromScriptID.Get(); ok {
from_script, err := svc.DB.Scripts().Get(ctx, from_script_id)
if err != nil {
return err
}
pmap.Add("from_script_hash", from_script.Hash)
}
if to_script_id, ok := req.ToScriptID.Get(); ok {
pmap.Add("to_script_id", to_script_id)
}
if policy, ok := req.Policy.Get(); ok {
pmap.Add("policy", policy)
}
return svc.DB.ScriptPolicy().Update(ctx, req.ID, pmap)
}

View File

@ -1,111 +0,0 @@
package service
import (
"context"
"fmt"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"github.com/dchest/siphash"
)
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
return nil, ErrUserInfo
}
if !userInfo.Roles.ScriptWrite {
return nil, ErrPermissionDenied
}
script, err := svc.DB.Scripts().Create(ctx, model.Script{
ID: 0,
Hash: siphash.Hash(0, 0, []byte(req.Source)),
Source: req.Source,
SubmissionID: req.SubmissionID.Or(0),
})
if err != nil {
return nil, err
}
return &api.ID{
ID: script.ID,
}, nil
}
// DeleteScript implements deleteScript operation.
//
// Delete the specified script by ID.
//
// DELETE /scripts/{ScriptID}
func (svc *Service) DeleteScript(ctx context.Context, params api.DeleteScriptParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
return ErrUserInfo
}
if !userInfo.Roles.ScriptWrite {
return ErrPermissionDenied
}
return svc.DB.Scripts().Delete(ctx, params.ScriptID)
}
// GetScript implements getScript operation.
//
// Get the specified script by ID.
//
// GET /scripts/{ScriptID}
func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (*api.Script, error) {
_, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
return nil, ErrUserInfo
}
// Read permission for scripts only requires you to be logged in
script, err := svc.DB.Scripts().Get(ctx, params.ScriptID)
if err != nil {
return nil, err
}
return &api.Script{
ID: script.ID,
Hash: fmt.Sprintf("%x", script.Hash),
Source: script.Source,
SubmissionID: script.SubmissionID,
}, nil
}
// UpdateScript implements updateScript operation.
//
// Update the specified script by ID.
//
// PATCH /scripts/{ScriptID}
func (svc *Service) UpdateScript(ctx context.Context, req *api.ScriptUpdate, params api.UpdateScriptParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
return ErrUserInfo
}
if !userInfo.Roles.ScriptWrite {
return ErrPermissionDenied
}
pmap := datastore.Optional()
if source, ok := req.Source.Get(); ok {
pmap.Add("source", source)
pmap.Add("hash", siphash.Hash(0, 0, []byte(source)))
}
if SubmissionID, ok := req.SubmissionID.Get(); ok {
pmap.Add("submission_id", SubmissionID)
}
return svc.DB.Scripts().Update(ctx, req.ID, pmap)
}

View File

@ -1,10 +1,11 @@
package service
import (
"context"
"errors"
"git.itzana.me/strafesnet/go-grpc/auth"
"context"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/go-grpc/auth"
"google.golang.org/grpc"
)
var (
@ -15,80 +16,77 @@ var (
)
var (
// has SubmissionPublish
RoleMapAdmin int32 = 128
// has SubmissionReview
RoleMapCouncil int32 = 64
RoleAdmin = 128
RoleReviewer = 64
)
type Roles struct {
// human roles
SubmissionPublish bool
SubmissionReview bool
ScriptWrite bool
// automated roles
Maptest bool
Admin bool
Reviewer bool
Maptest bool
Validator bool
}
type UserInfo struct {
Roles Roles
UserID uint64
Roles Roles
UserID int64
}
func (usr UserInfo) IsSubmitter(submitter uint64) bool {
func (usr UserInfo) IsSubmitter(submitter int64) bool{
return usr.UserID == submitter
}
type SecurityHandler struct {
Client auth.AuthServiceClient
Client *grpc.ClientConn
}
func (svc SecurityHandler) HandleCookieAuth(ctx context.Context, operationName api.OperationName, t api.CookieAuth) (context.Context, error) {
func (svc SecurityHandler) HandleCookieAuth(ctx context.Context, operationName api.OperationName, t api.CookieAuth) (context.Context, error){
sessionId := t.GetAPIKey()
if sessionId == "" {
return nil, ErrMissingSessionID
}
session, err := svc.Client.GetSessionUser(ctx, &auth.IdMessage{
client := auth.NewAuthServiceClient(svc.Client)
session, err := client.GetSessionUser(ctx, &auth.IdMessage{
SessionID: sessionId,
})
if err != nil {
if err != nil{
return nil, err
}
role, err := svc.Client.GetGroupRole(ctx, &auth.IdMessage{
role, err := client.GetGroupRole(ctx, &auth.IdMessage{
SessionID: sessionId,
})
if err != nil {
if err != nil{
return nil, err
}
validate, err := svc.Client.ValidateSession(ctx, &auth.IdMessage{
validate, err := client.ValidateSession(ctx, &auth.IdMessage{
SessionID: sessionId,
})
if err != nil {
if err != nil{
return nil, err
}
if !validate.Valid {
if !validate.Valid{
return nil, ErrInvalidSession
}
roles := Roles{}
// fix this when roblox udpates group roles
for _, r := range role.Roles {
if RoleMapAdmin <= r.Rank {
roles.SubmissionPublish = true
for r := range role.Roles{
if RoleAdmin<=r{
roles.Admin = true
}
if RoleMapCouncil <= r.Rank {
roles.SubmissionReview = true
if RoleReviewer<=r{
roles.Reviewer = true
}
}
newCtx := context.WithValue(ctx, "UserInfo", UserInfo{
Roles: roles,
UserID: session.UserID,
Roles: roles,
UserID: int64(session.UserID),
})
return newCtx, nil

View File

@ -2,40 +2,22 @@ package service
import (
"context"
"errors"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"github.com/nats-io/nats.go"
)
var (
// ErrPermissionDenied caller does not have the required role
ErrPermissionDenied = errors.New("Permission denied")
// ErrUserInfo user info is missing for some reason
ErrUserInfo = errors.New("Missing user info")
)
type Service struct {
DB datastore.Datastore
Nats nats.JetStreamContext
DB datastore.Datastore
Nats *nats.Conn
}
// NewError creates *ErrorStatusCode from error returned by handler.
//
// Used for common default response.
func (svc *Service) NewError(ctx context.Context, err error) *api.ErrorStatusCode {
status := 500
if errors.Is(err, ErrPermissionDenied) {
status = 403
}
if errors.Is(err, ErrUserInfo) {
status = 401
}
return &api.ErrorStatusCode{
StatusCode: status,
Response: api.Error{
Code: int64(status),
Message: err.Error(),
},
StatusCode: 500,
Response: api.Error{Message: err.Error()},
}
}

View File

@ -1,38 +1,48 @@
package service
import (
"time"
"errors"
"context"
"encoding/json"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
)
var (
// ErrInvalidSourceStatus current submission status cannot change to destination status
ErrInvalidSourceStatus = errors.New("Invalid source status")
// ErrPermissionDenied caller does not have the required role
ErrPermissionDenied = errors.New("Permission denied")
// ErrUserInfo user info is missing for some reason
ErrUserInfo = errors.New("Missing user info")
)
// POST /submissions
func (svc *Service) CreateSubmission(ctx context.Context, request *api.SubmissionCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
func (svc *Service) CreateSubmission(ctx context.Context, request api.OptSubmissionCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(*UserInfo)
if !ok{
return nil, ErrUserInfo
}
submission, err := svc.DB.Submissions().Create(ctx, model.Submission{
ID: 0,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: request.GameID,
Submitter: userInfo.UserID,
AssetID: uint64(request.AssetID),
AssetVersion: uint64(request.AssetVersion),
DisplayName: request.Value.DisplayName.Value,
Creator: request.Value.Creator.Value,
GameID: request.Value.GameID.Value,
Date: time.Now(),
Submitter: int64(userInfo.UserID),
AssetID: request.Value.AssetID.Value,
AssetVersion: request.Value.AssetVersion.Value,
Completed: false,
TargetAssetID: uint64(request.TargetAssetID.Value),
TargetAssetID: request.Value.TargetAssetID.Value,
StatusID: model.StatusUnderConstruction,
})
if err != nil {
if err != nil{
return nil, err
}
return &api.ID{
ID: submission.ID,
ID:api.NewOptInt64(submission.ID),
}, nil
}
@ -43,22 +53,21 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
// GET /submissions/{SubmissionID}
func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionParams) (*api.Submission, error) {
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil {
if err != nil{
return nil, err
}
return &api.Submission{
ID: submission.ID,
DisplayName: submission.DisplayName,
Creator: submission.Creator,
GameID: submission.GameID,
CreatedAt: submission.CreatedAt.Unix(),
UpdatedAt: submission.UpdatedAt.Unix(),
Submitter: int64(submission.Submitter),
AssetID: int64(submission.AssetID),
AssetVersion: int64(submission.AssetVersion),
Completed: submission.Completed,
TargetAssetID: api.NewOptInt64(int64(submission.TargetAssetID)),
StatusID: int32(submission.StatusID),
ID: api.NewOptInt64(submission.ID),
DisplayName: api.NewOptString(submission.DisplayName),
Creator: api.NewOptString(submission.Creator),
GameID: api.NewOptInt32(submission.GameID),
Date: api.NewOptInt64(submission.Date.Unix()),
Submitter: api.NewOptInt64(submission.Submitter),
AssetID: api.NewOptInt64(submission.AssetID),
AssetVersion: api.NewOptInt64(submission.AssetVersion),
Completed: api.NewOptBool(submission.Completed),
TargetAssetID: api.NewOptInt64(submission.TargetAssetID),
StatusID: api.NewOptInt32(int32(submission.StatusID)),
}, nil
}
@ -80,25 +89,24 @@ func (svc *Service) ListSubmissions(ctx context.Context, request api.ListSubmiss
Number: request.Page.GetPage(),
Size: request.Page.GetLimit(),
})
if err != nil {
if err != nil{
return nil, err
}
var resp []api.Submission
for i := 0; i < len(items); i++ {
resp = append(resp, api.Submission{
ID: items[i].ID,
DisplayName: items[i].DisplayName,
Creator: items[i].Creator,
GameID: items[i].GameID,
CreatedAt: items[i].CreatedAt.Unix(),
UpdatedAt: items[i].UpdatedAt.Unix(),
Submitter: int64(items[i].Submitter),
AssetID: int64(items[i].AssetID),
AssetVersion: int64(items[i].AssetVersion),
Completed: items[i].Completed,
TargetAssetID: api.NewOptInt64(int64(items[i].TargetAssetID)),
StatusID: int32(items[i].StatusID),
ID: api.NewOptInt64(items[i].ID),
DisplayName: api.NewOptString(items[i].DisplayName),
Creator: api.NewOptString(items[i].Creator),
GameID: api.NewOptInt32(items[i].GameID),
Date: api.NewOptInt64(items[i].Date.Unix()),
Submitter: api.NewOptInt64(items[i].Submitter),
AssetID: api.NewOptInt64(items[i].AssetID),
AssetVersion: api.NewOptInt64(items[i].AssetVersion),
Completed: api.NewOptBool(items[i].Completed),
TargetAssetID: api.NewOptInt64(items[i].TargetAssetID),
StatusID: api.NewOptInt32(int32(items[i].StatusID)),
})
}
@ -109,15 +117,15 @@ func (svc *Service) ListSubmissions(ctx context.Context, request api.ListSubmiss
//
// Retrieve map with ID.
//
// POST /submissions/{SubmissionID}/completed
func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSubmissionCompletedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
// PATCH /submissions/{SubmissionID}/completed
func (svc *Service) PatchSubmissionCompleted(ctx context.Context, params api.PatchSubmissionCompletedParams) error {
userInfo, ok := ctx.Value("UserInfo").(*UserInfo)
if !ok{
return ErrUserInfo
}
// check if caller has MaptestGame role (request must originate from a maptest roblox game)
if !userInfo.Roles.Maptest {
if !userInfo.Roles.Maptest{
return ErrPermissionDenied
}
@ -131,21 +139,21 @@ func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSu
//
// Update model following role restrictions.
//
// POST /submissions/{SubmissionID}/model
func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.UpdateSubmissionModelParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
// PATCH /submissions/{SubmissionID}/model
func (svc *Service) PatchSubmissionModel(ctx context.Context, params api.PatchSubmissionModelParams) error {
userInfo, ok := ctx.Value("UserInfo").(*UserInfo)
if !ok{
return ErrUserInfo
}
// read submission (this could be done with a transaction WHERE clause)
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil {
if err != nil{
return err
}
// check if caller is the submitter
if !userInfo.IsSubmitter(submission.Submitter) {
if !userInfo.IsSubmitter(submission.Submitter){
return ErrPermissionDenied
}
@ -154,237 +162,201 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
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.StatusChangesRequested, model.StatusSubmitted, model.StatusUnderConstruction}, pmap)
pmap.Add("completed",false)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusChangesRequested,model.StatusSubmitted,model.StatusUnderConstruction}, pmap)
}
// ActionSubmissionPublish invokes actionSubmissionPublish operation.
//
// Role Validator changes status from Publishing -> Published.
//
// POST /submissions/{SubmissionID}/status/publish
// PATCH /submissions/{SubmissionID}/status/publish
func (svc *Service) ActionSubmissionPublish(ctx context.Context, params api.ActionSubmissionPublishParams) error {
println("[ActionSubmissionPublish] Implicit Validator permission granted!")
userInfo, ok := ctx.Value("UserInfo").(*UserInfo)
if !ok{
return ErrUserInfo
}
// check if caller has required role
if !userInfo.Roles.Validator{
return ErrPermissionDenied
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusPublished)
smap.Add("status_id",model.StatusPublished)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusPublishing}, smap)
}
// ActionSubmissionReject invokes actionSubmissionReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
//
// POST /submissions/{SubmissionID}/status/reject
// PATCH /submissions/{SubmissionID}/status/reject
func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.ActionSubmissionRejectParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
userInfo, ok := ctx.Value("UserInfo").(*UserInfo)
if !ok{
return ErrUserInfo
}
// check if caller has required role
if !userInfo.Roles.SubmissionReview {
if !userInfo.Roles.Reviewer{
return ErrPermissionDenied
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusRejected)
smap.Add("status_id",model.StatusRejected)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted}, smap)
}
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
//
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
//
// POST /submissions/{SubmissionID}/status/request-changes
// PATCH /submissions/{SubmissionID}/status/request-changes
func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params api.ActionSubmissionRequestChangesParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
userInfo, ok := ctx.Value("UserInfo").(*UserInfo)
if !ok{
return ErrUserInfo
}
// check if caller has required role
if !userInfo.Roles.SubmissionReview {
if !userInfo.Roles.Reviewer{
return ErrPermissionDenied
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusChangesRequested)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidated, model.StatusAccepted, model.StatusSubmitted}, smap)
smap.Add("status_id",model.StatusChangesRequested)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidated,model.StatusAccepted,model.StatusSubmitted}, smap)
}
// ActionSubmissionRevoke invokes actionSubmissionRevoke operation.
//
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
//
// POST /submissions/{SubmissionID}/status/revoke
// PATCH /submissions/{SubmissionID}/status/revoke
func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.ActionSubmissionRevokeParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
userInfo, ok := ctx.Value("UserInfo").(*UserInfo)
if !ok{
return ErrUserInfo
}
// read submission (this could be done with a transaction WHERE clause)
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil {
if err != nil{
return err
}
// check if caller is the submitter
if !userInfo.IsSubmitter(submission.Submitter) {
if !userInfo.IsSubmitter(submission.Submitter){
return ErrPermissionDenied
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusUnderConstruction)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted, model.StatusChangesRequested}, smap)
smap.Add("status_id",model.StatusUnderConstruction)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted,model.StatusChangesRequested}, smap)
}
// ActionSubmissionSubmit invokes actionSubmissionSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/submit
// PATCH /submissions/{SubmissionID}/status/submit
func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.ActionSubmissionSubmitParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
userInfo, ok := ctx.Value("UserInfo").(*UserInfo)
if !ok{
return ErrUserInfo
}
// read submission (this could be done with a transaction WHERE clause)
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil {
if err != nil{
return err
}
// check if caller is the submitter
if !userInfo.IsSubmitter(submission.Submitter) {
if !userInfo.IsSubmitter(submission.Submitter){
return ErrPermissionDenied
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusSubmitted)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUnderConstruction, model.StatusChangesRequested}, smap)
smap.Add("status_id",model.StatusSubmitted)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUnderConstruction,model.StatusChangesRequested}, smap)
}
// ActionSubmissionTriggerPublish invokes actionSubmissionTriggerPublish operation.
//
// Role Admin changes status from Validated -> Publishing.
//
// POST /submissions/{SubmissionID}/status/trigger-publish
// PATCH /submissions/{SubmissionID}/status/trigger-publish
func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params api.ActionSubmissionTriggerPublishParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
userInfo, ok := ctx.Value("UserInfo").(*UserInfo)
if !ok{
return ErrUserInfo
}
// check if caller has required role
if !userInfo.Roles.SubmissionPublish {
if !userInfo.Roles.Admin{
return ErrPermissionDenied
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusPublishing)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusValidated}, smap)
if err != nil {
smap.Add("status_id",model.StatusPublishing)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidated}, smap)
if err != nil{
return err
}
// sentinel value because we are not using rust
if submission.TargetAssetID == 0 {
// this is a new map
publish_new_request := model.PublishNewRequest{
SubmissionID: submission.ID,
ModelID: submission.AssetID,
ModelVersion: submission.AssetVersion,
Creator: submission.Creator,
DisplayName: submission.DisplayName,
GameID: uint32(submission.GameID),
}
j, err := json.Marshal(publish_new_request)
if err != nil {
return err
}
svc.Nats.Publish("maptest.submissions.publishnew", []byte(j))
} else {
// this is a map fix
publish_fix_request := model.PublishFixRequest{
SubmissionID: submission.ID,
ModelID: submission.AssetID,
ModelVersion: submission.AssetVersion,
TargetAssetID: submission.TargetAssetID,
}
j, err := json.Marshal(publish_fix_request)
if err != nil {
return err
}
svc.Nats.Publish("maptest.submissions.publishfix", []byte(j))
}
svc.Nats.Publish("publish", []byte(params.SubmissionID))
return nil
}
// ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation.
//
// Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
//
// POST /submissions/{SubmissionID}/status/trigger-validate
// PATCH /submissions/{SubmissionID}/status/trigger-validate
func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params api.ActionSubmissionTriggerValidateParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok {
userInfo, ok := ctx.Value("UserInfo").(*UserInfo)
if !ok{
return ErrUserInfo
}
// check if caller has required role
if !userInfo.Roles.SubmissionReview {
if !userInfo.Roles.Reviewer{
return ErrPermissionDenied
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusValidating)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted, model.StatusAccepted}, smap)
if err != nil {
smap.Add("status_id",model.StatusValidating)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted,model.StatusAccepted}, smap)
if err != nil{
return err
}
validate_request := model.ValidateRequest{
SubmissionID: submission.ID,
ModelID: submission.AssetID,
ModelVersion: submission.AssetVersion,
ValidatedModelID: 0, //TODO: reuse velidation models
}
j, err := json.Marshal(validate_request)
if err != nil {
return err
}
svc.Nats.Publish("maptest.submissions.validate", []byte(j))
svc.Nats.Publish("validate", []byte(params.SubmissionID))
return nil
}
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
//
// Role Validator changes status from Validating -> Validated.
//
// POST /submissions/{SubmissionID}/status/validate
// PATCH /submissions/{SubmissionID}/status/validate
func (svc *Service) ActionSubmissionValidate(ctx context.Context, params api.ActionSubmissionValidateParams) error {
println("[ActionSubmissionValidate] Implicit Validator permission granted!")
userInfo, ok := ctx.Value("UserInfo").(*UserInfo)
if !ok{
return ErrUserInfo
}
// check if caller has required role
if !userInfo.Roles.Validator{
return ErrPermissionDenied
}
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.StatusValidated)
smap.Add("status_id",model.StatusValidated)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, smap)
}

View File

@ -1,2 +0,0 @@
[registries.strafesnet]
index = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"

1123
validation/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -4,17 +4,13 @@ version = "0.1.0"
edition = "2021"
[dependencies]
api = { path = "api" }
async-nats = "0.38.0"
futures = "0.3.31"
rbx_asset = { version = "0.2.5", registry = "strafesnet" }
rbx_asset = { version = "0.2.3", registry = "strafesnet" }
rbx_binary = { version = "0.7.4", registry = "strafesnet"}
rbx_dom_weak = { version = "2.9.0", registry = "strafesnet"}
rbx_reflection_database = { version = "0.2.12", registry = "strafesnet"}
rbx_xml = { version = "0.13.3", registry = "strafesnet"}
rust-grpc = { version = "1.0.3", registry = "strafesnet" }
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.133"
siphasher = "1.0.1"
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread", "fs", "signal"] }
tonic = "0.12.3"
sqlx = "0.8.2"
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread", "fs"] }

View File

@ -1,24 +0,0 @@
# Using the `rust-musl-builder` as base image, instead of
# the official Rust toolchain
FROM docker.io/clux/muslrust:stable AS chef
USER root
RUN cargo install cargo-chef
WORKDIR /app
FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
FROM chef AS builder
COPY --from=planner /app/recipe.json recipe.json
COPY api ./api
# Notice that we are specifying the --target flag!
RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl --bin maps-validation
FROM docker.io/alpine:latest AS runtime
RUN addgroup -S myuser && adduser -S myuser -G myuser
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/maps-validation /usr/local/bin/
USER myuser
ENTRYPOINT ["/usr/local/bin/maps-validation"]

View File

@ -1,17 +0,0 @@
[package]
name = "api"
version = "0.1.0"
edition = "2021"
publish = ["strafesnet"]
repository = "https://git.itzana.me/StrafesNET/maps-service"
license = "MIT OR Apache-2.0"
description = "Browse and manage map submissions in the staging pipeline."
authors = ["Rhys Lloyd <krakow20@gmail.com>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
reqwest = { version = "0", features = ["json"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
url = "2"

View File

@ -1,176 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -1,23 +0,0 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -1,130 +0,0 @@
#[derive(Debug)]
pub enum Error{
ParseError(url::ParseError),
Reqwest(reqwest::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
#[derive(serde::Deserialize)]
pub struct ScriptID(i64);
#[allow(nonstandard_style)]
pub struct GetScriptRequest{
pub ScriptID:ScriptID,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct ScriptResponse{
pub ID:i64,
pub Hash:String,
pub Source:String,
pub SubmissionID:i64,
}
#[derive(serde::Deserialize)]
#[repr(i32)]
pub enum Policy{
Allowed=0,
Blocked=1,
Delete=2,
Replace=3,
}
pub struct ScriptPolicyHashRequest{
pub hash:String,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct ScriptPolicyResponse{
pub ID:i64,
pub FromScriptHash:String,
pub ToScriptID:ScriptID,
pub Policy:Policy
}
#[allow(nonstandard_style)]
pub struct UpdateSubmissionModelRequest{
pub ID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
}
pub struct SubmissionID(pub i64);
#[derive(Clone)]
pub struct Context{
base_url:String,
client:reqwest::Client,
}
pub type ReqwestError=reqwest::Error;
// there are lots of action endpoints and they all follow the same pattern
macro_rules! action{
($fname:ident,$action:expr)=>{
pub async fn $fname(&self,config:SubmissionID)->Result<(),Error>{
let url_raw=format!(concat!("{}/submissions/{}/status/",$action),self.base_url,config.0);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?;
self.post(url).await.map_err(Error::Reqwest)?
.error_for_status().map_err(Error::Reqwest)?;
Ok(())
}
};
}
impl Context{
pub fn new(mut base_url:String)->reqwest::Result<Self>{
base_url+="/v1";
Ok(Self{
base_url,
client:reqwest::Client::new(),
})
}
async fn get(&self,url:impl reqwest::IntoUrl)->Result<reqwest::Response,reqwest::Error>{
self.client.get(url)
.send().await
}
async fn post(&self,url:impl reqwest::IntoUrl)->Result<reqwest::Response,reqwest::Error>{
self.client.post(url)
.send().await
}
pub async fn get_script(&self,config:GetScriptRequest)->Result<ScriptResponse,Error>{
let url_raw=format!("{}/scripts/{}",self.base_url,config.ScriptID.0);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?;
self.get(url).await.map_err(Error::Reqwest)?
.error_for_status().map_err(Error::Reqwest)?
.json().await.map_err(Error::Reqwest)
}
pub async fn get_script_policy_from_hash(&self,config:ScriptPolicyHashRequest)->Result<ScriptPolicyResponse,Error>{
let url_raw=format!("{}/script-policy/hash/{}",self.base_url,config.hash);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?;
self.get(url).await.map_err(Error::Reqwest)?
.error_for_status().map_err(Error::Reqwest)?
.json().await.map_err(Error::Reqwest)
}
pub async fn update_submission_model(&self,config:UpdateSubmissionModelRequest)->Result<(),Error>{
let url_raw=format!("{}/submissions/{}/model",self.base_url,config.ID);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?;
{
url.query_pairs_mut()
.append_pair("ModelID",config.ModelID.to_string().as_str())
.append_pair("ModelVersion",config.ModelVersion.to_string().as_str());
}
self.post(url).await.map_err(Error::Reqwest)?
.error_for_status().map_err(Error::Reqwest)?;
Ok(())
}
action!(action_submission_validate,"validator-validated");
action!(action_submission_publish,"validator-published");
}

View File

@ -1,20 +1,12 @@
use futures::StreamExt;
mod nats_types;
mod publisher;
mod validator;
mod publish_new;
mod publish_fix;
mod message_handler;
#[allow(dead_code)]
#[derive(Debug)]
pub enum StartupError{
API(api::ReqwestError),
NatsConnect(async_nats::ConnectError),
NatsGetStream(async_nats::jetstream::context::GetStreamError),
NatsConsumer(async_nats::jetstream::stream::ConsumerError),
NatsStream(async_nats::jetstream::consumer::StreamError),
GRPCConnect(tonic::transport::Error),
enum StartupError{
Connect(async_nats::ConnectError),
Subscribe(async_nats::SubscribeError),
}
impl std::fmt::Display for StartupError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@ -23,76 +15,26 @@ impl std::fmt::Display for StartupError{
}
impl std::error::Error for StartupError{}
// annoying mile-long type
pub type MapsServiceClient=rust_grpc::maps::maps_service_client::MapsServiceClient<tonic::transport::channel::Channel>;
pub const GROUP_STRAFESNET:u64=6980477;
pub const PARALLEL_REQUESTS:usize=16;
#[tokio::main]
async fn main()->Result<(),StartupError>{
// talk to roblox through STRAFESNET_CI2 account
let cookie=std::env::var("RBXCOOKIE").expect("RBXCOOKIE env required");
let cookie_context=rbx_asset::cookie::CookieContext::new(rbx_asset::cookie::Cookie::new(cookie));
// maps-service api
let api_host=std::env::var("API_HOST").expect("API_HOST env required");
let api=api::Context::new(api_host).map_err(StartupError::API)?;
// cookies and clouds
let cookie_context=rbx_asset::cookie::CookieContext::new(rbx_asset::cookie::Cookie::new("".to_owned()));
let cloud_context=rbx_asset::cloud::CloudContext::new(rbx_asset::cloud::ApiKey::new("".to_owned()));
// nats
let nats_host=std::env::var("NATS_HOST").expect("NATS_HOST env required");
let nats_fut=async{
let nasty=async_nats::connect(nats_host).await.map_err(StartupError::NatsConnect)?;
// use nats jetstream
async_nats::jetstream::new(nasty)
.get_stream("maptest").await.map_err(StartupError::NatsGetStream)?
.get_or_create_consumer("validation",async_nats::jetstream::consumer::pull::Config{
name:Some("validation".to_owned()),
durable_name:Some("validation".to_owned()),
filter_subject:"maptest.submissions.>".to_owned(),
..Default::default()
}).await.map_err(StartupError::NatsConsumer)?
.messages().await.map_err(StartupError::NatsStream)
};
let nasty=async_nats::connect("nats").await.map_err(StartupError::Connect)?;
// data-service grpc for creating map entries
let data_host=std::env::var("DATA_HOST").expect("DATA_HOST env required");
let message_handler_fut=async{
let maps_grpc=crate::MapsServiceClient::connect(data_host).await.map_err(StartupError::GRPCConnect)?;
Ok(message_handler::MessageHandler::new(cookie_context,api,maps_grpc))
};
// connect to nats
let (publisher,validator)=tokio::try_join!(
publisher::Publisher::new(nasty.clone(),cookie_context.clone(),cloud_context),
validator::Validator::new(nasty,cookie_context)
).map_err(StartupError::Subscribe)?;
// Create a signal listener for SIGTERM
let mut sig_term=tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()).expect("Failed to create SIGTERM signal listener");
// publisher thread
tokio::spawn(publisher.run());
// run futures
let (mut messages,message_handler)=tokio::try_join!(nats_fut,message_handler_fut)?;
// process up to PARALLEL_REQUESTS in parallel
let main_loop=async move{
static SEM:tokio::sync::Semaphore=tokio::sync::Semaphore::const_new(PARALLEL_REQUESTS);
// use memory leak to make static lifetime
let message_handler=Box::leak(Box::new(message_handler));
// acquire a permit before attempting to receive a message, exit if either fails
while let (Ok(permit),Some(message_result))=(SEM.acquire().await,messages.next().await){
// handle the message on a new thread (mainly to decode the model file)
tokio::spawn(async{
match message_handler.handle_message_result(message_result).await{
Ok(())=>println!("[Validation] Success, hooray!"),
Err(e)=>println!("[Validation] There was an error, oopsie! {e}"),
}
// explicitly call drop to make the move semantics and permit release more obvious
core::mem::drop(permit);
});
}
};
// race sigkill and main loop termination and then die
tokio::select!{
_=sig_term.recv()=>(),
_=main_loop=>(),
};
// run validator on the main thread indefinitely
validator.run().await;
Ok(())
}

View File

@ -1,48 +0,0 @@
#[allow(dead_code)]
#[derive(Debug)]
pub enum HandleMessageError{
Messages(async_nats::jetstream::consumer::pull::MessagesError),
DoubleAck(async_nats::Error),
UnknownSubject(String),
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{
write!(f,"{self:?}")
}
}
impl std::error::Error for HandleMessageError{}
pub type MessageResult=Result<async_nats::jetstream::Message,async_nats::jetstream::consumer::pull::MessagesError>;
pub struct MessageHandler{
publish_new:crate::publish_new::Publisher,
publish_fix:crate::publish_fix::Publisher,
validator:crate::validator::Validator,
}
impl MessageHandler{
pub fn new(
cookie_context:rbx_asset::cookie::CookieContext,
api:api::Context,
maps_grpc:crate::MapsServiceClient,
)->Self{
Self{
publish_new:crate::publish_new::Publisher::new(cookie_context.clone(),api.clone(),maps_grpc),
publish_fix:crate::publish_fix::Publisher::new(cookie_context.clone(),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.submissions.publishnew"=>self.publish_new.publish(message).await.map_err(HandleMessageError::PublishNew),
"maptest.submissions.publishfix"=>self.publish_fix.publish(message).await.map_err(HandleMessageError::PublishFix),
"maptest.submissions.validate"=>self.validator.validate(message).await.map_err(HandleMessageError::Validation),
other=>Err(HandleMessageError::UnknownSubject(other.to_owned()))
}
}
}

View File

@ -2,36 +2,46 @@
// to perform the operation, not necessarily the over-the-wire format
// Requests are sent from maps-service to validator
// Validation invokes the REST api to update the submissions
// Responses are sent from validator to maps-service
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct ValidateRequest{
// submission_id is passed back in the response message
pub SubmissionID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
pub ValidatedModelID:Option<u64>,
pub submission_id:u64,
pub model_id:u64,
pub model_version:u64,
}
pub struct ValidateResponse{
pub submission_id:u64,
// model id will be changed if scripts were replaced
pub model_id:u64,
pub model_version:u64,
}
// an invalidate is sent instead if validation fails
pub struct InvalidateResponse{
pub submission_id:u64,
}
// Create a new map
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct PublishNewRequest{
pub SubmissionID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
pub Creator:String,
pub DisplayName:String,
pub GameID:u32,
pub submission_id:u64,
pub model_id:u64,
pub model_version:u64,
pub creator:String,
pub display_name:String,
//games:HashSet<GameID>,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct PublishFixRequest{
pub SubmissionID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
pub TargetAssetID:u64,
pub submission_id:u64,
pub model_id:u64,
pub model_version:u64,
pub target_asset_id:u64,
}
pub struct PublishResponse{
pub submission_id:u64,
pub success:bool,
}

View File

@ -1,62 +0,0 @@
use crate::nats_types::PublishFixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum PublishError{
Get(rbx_asset::cookie::GetError),
Json(serde_json::Error),
Upload(rbx_asset::cookie::UploadError),
ApiActionSubmissionPublish(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,
api:api::Context,
}
impl Publisher{
pub const fn new(
roblox_cookie:rbx_asset::cookie::CookieContext,
api:api::Context,
)->Self{
Self{
roblox_cookie,
api,
}
}
pub async fn publish(&self,message:async_nats::jetstream::Message)->Result<(),PublishError>{
println!("publish_fix {:?}",message.message.payload);
// decode json
let publish_info:PublishFixRequest=serde_json::from_slice(&message.payload).map_err(PublishError::Json)?;
// 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:Some(crate::GROUP_STRAFESNET),
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 published
self.api.action_submission_publish(
api::SubmissionID(publish_info.SubmissionID)
).await.map_err(PublishError::ApiActionSubmissionPublish)?;
Ok(())
}
}

View File

@ -1,83 +0,0 @@
use crate::nats_types::PublishNewRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum PublishError{
Get(rbx_asset::cookie::GetError),
Json(serde_json::Error),
Create(rbx_asset::cookie::CreateError),
SystemTime(std::time::SystemTimeError),
MapCreate(tonic::Status),
ApiActionSubmissionPublish(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,
api:api::Context,
maps_grpc:crate::MapsServiceClient,
}
impl Publisher{
pub const fn new(
roblox_cookie:rbx_asset::cookie::CookieContext,
api:api::Context,
maps_grpc:crate::MapsServiceClient,
)->Self{
Self{
roblox_cookie,
api,
maps_grpc,
}
}
pub async fn publish(&self,message:async_nats::jetstream::Message)->Result<(),PublishError>{
println!("publish_new {:?}",message.message.payload);
// decode json
let publish_info:PublishNewRequest=serde_json::from_slice(&message.payload).map_err(PublishError::Json)?;
// 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.DisplayName.clone(),
description:"".to_owned(),
ispublic:false,
allowComments:false,
groupId:Some(crate::GROUP_STRAFESNET),
},model_data).await.map_err(PublishError::Create)?;
// create the map entry in the game database, including release date
self.maps_grpc.clone().create(rust_grpc::maps::MapRequest{
id:upload_response.AssetId as i64,
display_name:Some(publish_info.DisplayName),
creator:Some(publish_info.Creator),
game_id:Some(publish_info.GameID as i32),
// TODO: scheduling algorithm
date:Some(
// Publish one week from now
(
std::time::SystemTime::now()
+std::time::Duration::from_secs(7*24*60*60)
)
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.map_err(PublishError::SystemTime)?
.as_secs() as i64
),
}).await.map_err(PublishError::MapCreate)?;
// mark submission as published
self.api.action_submission_publish(
api::SubmissionID(publish_info.SubmissionID)
).await.map_err(PublishError::ApiActionSubmissionPublish)?;
Ok(())
}
}

View File

@ -0,0 +1,40 @@
use futures::StreamExt;
#[derive(Debug)]
enum PublishError{
}
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{
nats:async_nats::Client,
subscriber:async_nats::Subscriber,
roblox_cookie:rbx_asset::cookie::CookieContext,
roblox_cloud:rbx_asset::cloud::CloudContext,
}
impl Publisher{
pub async fn new(
nats:async_nats::Client,
roblox_cookie:rbx_asset::cookie::CookieContext,
roblox_cloud:rbx_asset::cloud::CloudContext,
)->Result<Self,async_nats::SubscribeError>{
Ok(Self{
subscriber:nats.subscribe("publish").await?,
nats,
roblox_cookie,
roblox_cloud,
})
}
pub async fn run(mut self){
while let Some(message)=self.subscriber.next().await{
self.publish(message).await
}
}
async fn publish(&self,message:async_nats::Message){
println!("publish {:?}",message);
}
}

View File

@ -1,31 +1,22 @@
use futures::TryStreamExt;
use futures::StreamExt;
use crate::nats_types::ValidateRequest;
const SCRIPT_CONCURRENCY:usize=16;
struct ModelVersion{
model_id:u64,
model_version:u64,
}
enum Policy{
Allowed,
Blocked,
Delete,
Replace(String),
enum Valid{
Untouched,
Modified(ModelVersion),
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum ValidateError{
Blocked,
NotAllowed,
enum ValidateError{
Get(rbx_asset::cookie::GetError),
Json(serde_json::Error),
ReadDom(ReadDomError),
ApiGetScriptPolicy(api::Error),
ApiGetScript(api::Error),
ApiUpdateSubmissionModel(api::Error),
ApiActionSubmissionValidate(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{
@ -35,152 +26,63 @@ impl std::fmt::Display for ValidateError{
impl std::error::Error for ValidateError{}
pub struct Validator{
nats:async_nats::Client,
subscriber:async_nats::Subscriber,
roblox_cookie:rbx_asset::cookie::CookieContext,
api:api::Context,
}
impl Validator{
pub const fn new(
pub async fn new(
nats:async_nats::Client,
roblox_cookie:rbx_asset::cookie::CookieContext,
api:api::Context,
)->Self{
Self{
)->Result<Self,async_nats::SubscribeError>{
Ok(Self{
subscriber:nats.subscribe("validate").await?,
nats,
roblox_cookie,
api,
})
}
pub async fn run(mut self){
while let Some(message)=self.subscriber.next().await{
self.validate_supress_error(message).await
}
}
pub async fn validate(&self,message:async_nats::jetstream::Message)->Result<(),ValidateError>{
println!("validate {:?}",message.message.payload);
async fn validate_supress_error(&self,message:async_nats::Message){
match self.validate(message).await{
Ok(valid)=>{
unimplemented!();
// self.nats.publish("validated","yo it validated".into()).await.unwrap();
},
Err(e)=>{
println!("there was an error, oopsie! {e}");
}
}
}
async fn validate(&self,message:async_nats::Message)->Result<Valid,ValidateError>{
println!("validate {:?}",message);
// decode json
let validate_info:ValidateRequest=serde_json::from_slice(&message.payload).map_err(ValidateError::Json)?;
// download map
let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:validate_info.ModelID,
version:Some(validate_info.ModelVersion),
asset_id:validate_info.model_id,
version:Some(validate_info.model_version),
}).await.map_err(ValidateError::Get)?;
// decode dom (slow!)
let mut dom=read_dom(&mut std::io::Cursor::new(data)).map_err(ValidateError::ReadDom)?;
/* VALIDATE MAP */
// validate map
// validate(dom)
// collect unique scripts
let script_refs=get_script_refs(&dom);
let mut script_map=std::collections::HashMap::<String,Policy>::new();
for &script_ref in &script_refs{
if let Some(script)=dom.get_by_ref(script_ref){
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get("Source"){
script_map.insert(source.clone(),Policy::Blocked);
}
}
}
// send all script hashes to REST endpoint and retrieve the replacements
futures::stream::iter(script_map.iter_mut().map(Ok))
.try_for_each_concurrent(Some(SCRIPT_CONCURRENCY),|(source,replacement)|async{
// get the hash
let mut hasher=siphasher::sip::SipHasher::new();
std::hash::Hasher::write(&mut hasher,source.as_bytes());
let hash=std::hash::Hasher::finish(&hasher);
// fetch the script policy
let script_policy=self.api.get_script_policy_from_hash(api::ScriptPolicyHashRequest{
hash:format!("{:x}",hash),
}).await.map_err(ValidateError::ApiGetScriptPolicy)?;
// write the policy to the script_map, fetching the replacement code if necessary
*replacement=match script_policy.Policy{
api::Policy::Allowed=>Policy::Allowed,
api::Policy::Blocked=>Policy::Blocked,
api::Policy::Delete=>Policy::Delete,
api::Policy::Replace=>{
let script=self.api.get_script(api::GetScriptRequest{
ScriptID:script_policy.ToScriptID,
}).await.map_err(ValidateError::ApiGetScript)?;
Policy::Replace(script.Source)
},
};
Ok(())
})
.await?;
// make the replacements
let mut modified=false;
for &script_ref in &script_refs{
if let Some(script)=dom.get_by_ref_mut(script_ref){
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get_mut("Source"){
match script_map.get(source.as_str()){
Some(Policy::Blocked)=>return Err(ValidateError::Blocked),
None=>return Err(ValidateError::NotAllowed),
Some(Policy::Allowed)=>(),
Some(Policy::Delete)=>{
modified=true;
// delete script
dom.destroy(script_ref);
},
Some(Policy::Replace(replacement))=>{
modified=true;
*source=replacement.clone();
},
}
}
}
}
// if the model was validated, the submission must be changed to use the modified model
if modified{
// serialize model (slow!)
let mut data=Vec::new();
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{
// upload to existing id
let response=self.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{
assetid:model_id,
name:None,
description:None,
ispublic:None,
allowComments:None,
groupId:None,
},data).await.map_err(ValidateError::Upload)?;
response.AssetId
}else{
// create new model
let response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
name:dom.root().name.clone(),
description:"".to_owned(),
ispublic:true,
allowComments:true,
groupId:None,
},data).await.map_err(ValidateError::Create)?;
response.AssetId
};
// update the submission to use the validated model
self.api.update_submission_model(api::UpdateSubmissionModelRequest{
ID:validate_info.SubmissionID,
ModelID:model_id,
ModelVersion:1, //TODO
}).await.map_err(ValidateError::ApiUpdateSubmissionModel)?;
};
// update the submission model status to validated
self.api.action_submission_validate(
api::SubmissionID(validate_info.SubmissionID)
).await.map_err(ValidateError::ApiActionSubmissionValidate)?;
Ok(())
// reply with validity
Ok(Valid::Untouched)
}
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum ReadDomError{
enum ReadDomError{
Binary(rbx_binary::DecodeError),
Xml(rbx_xml::DecodeError),
Read(std::io::Error),
@ -209,33 +111,3 @@ fn read_dom<R:std::io::Read+std::io::Seek>(input:&mut R)->Result<rbx_dom_weak::W
_=>Err(ReadDomError::UnknownFormat(first_8)),
}
}
fn class_is_a(class:&str,superclass:&str)->bool{
if class==superclass{
return true
}
let class_descriptor=rbx_reflection_database::get().classes.get(class);
if let Some(descriptor)=&class_descriptor{
if let Some(class_super)=&descriptor.superclass{
return class_is_a(&class_super,superclass)
}
}
false
}
fn recursive_collect_superclass(objects:&mut std::vec::Vec<rbx_dom_weak::types::Ref>,dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,superclass:&str){
for &referent in instance.children(){
if let Some(c)=dom.get_by_ref(referent){
if class_is_a(c.class.as_str(),superclass){
objects.push(c.referent());//copy ref
}
recursive_collect_superclass(objects,dom,c,superclass);
}
}
}
fn get_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");
scripts
}

View File

@ -1,3 +0,0 @@
node_modules
build
bun.lockb

40
web/.gitignore vendored
View File

@ -1,40 +0,0 @@
bun.lockb
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@ -1,13 +0,0 @@
FROM oven/bun:latest
WORKDIR /app
COPY . .
EXPOSE 3000
ENV NEXT_TELEMETRY_DISABLED=1
RUN bun install
RUN bun run build
ENTRYPOINT ["bun", "run", "start"]

View File

@ -1,16 +0,0 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
];
export default eslintConfig;

View File

@ -1,16 +0,0 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
distDir: "build",
output: "standalone",
rewrites: async () => {
return [
{
source: "/v1/submissions/:submissionid/status/:statustype",
destination: "http://mapsservice:8082/v1/submissions/:submissionid/status/:statustype"
}
]
}
};
export default nextConfig;

View File

@ -1,30 +0,0 @@
{
"name": "map-service-web",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 3000",
"build": "next build",
"start": "next start -p 3000",
"lint": "next lint"
},
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mui/icons-material": "^6.1.10",
"@mui/material": "^6.1.10",
"next": "^15.1.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sass": "^1.82.0"
},
"devDependencies": {
"typescript": "^5.7.2",
"@types/node": "^20.17.9",
"@types/react": "^19.0.1",
"@types/react-dom": "^19.0.2",
"eslint": "^9.16.0",
"eslint-config-next": "15.1.0",
"@eslint/eslintrc": "^3.2.0"
}
}

View File

@ -1,31 +0,0 @@
import Link from "next/link"
import "./styles/header.scss"
interface HeaderButton {
name: string,
href: string
}
function HeaderButton(header: HeaderButton) {
return (
<Link href={header.href}>
<button>{header.name}</button>
</Link>
)
}
export default function Header() {
return (
<header className="header-bar">
<nav className="left">
<HeaderButton name="Maps" href=""/>
</nav>
<nav className="right">
<HeaderButton name="Home" href=""/>
<HeaderButton name="Need" href=""/>
<HeaderButton name="Menu" href=""/>
<HeaderButton name="Items" href=""/>
</nav>
</header>
)
}

View File

@ -1,38 +0,0 @@
.header-bar {
display: flex;
justify-content: space-between;
align-items: center;
width: 100vw;
height: 60px;
background: var(--header-grad-left);
background: linear-gradient(180deg, var(--header-grad-left) 0%, var(--header-grad-right) 100%);
button {
background-color: transparent;
border: 0;
color: var(--header-button-left);
}
.left {
margin-left: 15px;
button {
font-size: 1.2rem;
}
}
.right {
display: flex;
gap: 7px;
margin-right: 50px;
button {
font-size: 1rem;
color: var(--header-button-right);
&:hover {
color: var(--header-button-hover)
}
}
}
}

View File

@ -1,8 +0,0 @@
import Header from "./header";
export default function Webpage({children}: Readonly<{children?: React.ReactNode}>) {
return (<>
<Header/>
{children}
</>)
}

View File

@ -1,54 +0,0 @@
$review-border: 1px solid var(--review-border);
@mixin border-with-radius {
border: $review-border {
radius: 5px;
}
}
:root {
color-scheme: light dark;
--page: white;
--header-grad-left: #363b40;
--header-grad-right: #353a40;
--header-button-left: white;
--header-button-right: #b4b4b4;
--header-button-hover: white;
--review-border: #c8c8c8;
--text-color: #1e1e1e;
--anchor-link-review: #008fd6;
--window-header: #f5f5f5;
--comment-highlighted: #ffffd7;
--comment-area: white;
@media (prefers-color-scheme: dark) {
--page: rgb(15,15,15);
--header-grad-left: #363b40;
--header-grad-right: #353a40;
--header-button-left: white;
--header-button-right: #b4b4b4;
--header-button-hover: white;
--review-border: rgb(50,50,50);
--text-color: rgb(230,230,230);
--anchor-link-review: #008fd6;
--window-header: rgb(10,10,10);
--comment-highlighted: #ffffd7;
--comment-area: rgb(20,20,20);
}
}
body {
font-family: -apple-system, "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla";
box-sizing: border-box;
margin: 0;
background-color: var(--page);
}
button {
cursor: pointer;
}
a:active, a:link, a:hover {
text-decoration: none;
}

View File

@ -1,9 +0,0 @@
import "./globals.scss";
export default function RootLayout({children}: Readonly<{children: React.ReactNode}>) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}

View File

@ -1,7 +0,0 @@
import Webpage from "./_components/webpage";
export default function Home() {
return (
<Webpage></Webpage>
);
}

View File

@ -1,26 +0,0 @@
@forward "./page/commentWindow.scss";
@forward "./page/reviewStatus.scss";
@forward "./page/ratingWindow.scss";
@forward "./page/reviewButtons.scss";
@forward "./page/comments.scss";
@forward "./page/review.scss";
@forward "./page/map.scss";
@use "../../../globals.scss";
.map-page-main {
display: flex;
justify-content: center;
width: 100vw;
}
.spacer {
display: block;
width: 100%;
height: 1px;
background-color: var(--review-border);
}
.by-creator {
margin-top: 10px;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,27 +0,0 @@
"use client"
import { useState, useEffect } from "react"
import { SubmissionInfo } from "@/app/ts/Submission"
import { AssetImage } from "@/app/ts/Roblox"
import Image from "next/image"
interface AssetID {
id: SubmissionInfo["AssetID"]
}
function MapImage(asset: AssetID) {
const [assetImage, setAssetImage] = useState("");
useEffect(() => {
AssetImage(asset.id, "420x420").then(image => setAssetImage(image))
}, [asset.id]);
if (!assetImage) {
return <p>Fetching map image...</p>;
}
return <Image src={assetImage} alt="Map Image"/>
}
export {
type AssetID,
MapImage
}

View File

@ -1,35 +0,0 @@
import { Button, ButtonOwnProps } from "@mui/material";
type Review = "Completed" | "Submit" | "Reject" | "Revoke" | "Accept" | "Publish"
type Action = "completed" | "submit" | "reject" | "revoke" | "trigger-validate" | "trigger-publish"
interface ReviewButton {
name: Review,
action: Action,
color: ButtonOwnProps["color"]
}
function ReviewButtonClicked(action: Action) {
const post = fetch(`http://localhost:3000/v1/submissions/1/status/${action}`, {
method: "POST",
headers: {
"Content-type": "application/json",
}
})
}
function ReviewButton(props: ReviewButton) {
return <Button color={props.color} variant="contained" onClick={() => { ReviewButtonClicked(props.action) }}>{props.name}</Button>
}
export default function ReviewButtons() {
return (
<section className="review-set">
<ReviewButton color="info" name="Submit" action="submit"/>
<ReviewButton color="info" name="Revoke" action="revoke"/>
<ReviewButton color="info" name="Accept" action="trigger-validate"/>
<ReviewButton color="error" name="Reject" action="reject"/>
<ReviewButton color="info" name="Publish" action="trigger-publish"/>
<ReviewButton color="info" name="Completed" action="completed"/>
</section>
)
}

View File

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

View File

@ -1,80 +0,0 @@
"use client"
import { SubmissionStatus, SubmissionStatusToString } from "@/app/ts/Submission";
import type { CreatorAndReviewStatus } from "./_comments";
import { MapImage, type AssetID } from "./_map";
import { useParams } from "next/navigation";
import ReviewButtons from "./_reviewButtons";
import { Rating } from "@mui/material";
import Comments from "./_comments";
import Webpage from "@/app/_components/webpage";
import Window from "./_window";
import Link from "next/link";
import "./(styles)/page.scss";
function Ratings() {
return (
<Window className="rating-window" title="Rating">
<section className="rating-type">
<aside className="rating-left">
<p>Quality</p>
<p>Difficulty</p>
<p>Fun</p>
<p>Length</p>
</aside>
<aside className="rating-right">
<Rating defaultValue={2.5} precision={0.5}/>
<Rating defaultValue={2.5} precision={0.5}/>
<Rating defaultValue={2.5} precision={0.5}/>
<Rating defaultValue={2.5} precision={0.5}/>
</aside>
</section>
</Window>
)
}
function RatingArea(asset: AssetID) {
return (
<aside className="review-area">
<section className="map-image-area">
<MapImage id={asset.id}/>
</section>
<Ratings/>
<ReviewButtons/>
</aside>
)
}
function TitleAndComments(stats: CreatorAndReviewStatus) {
const Review = SubmissionStatusToString(stats.review)
return (
<main className="review-info">
<div>
<h1>{stats.name}</h1>
<aside data-review-status={stats.review} className="review-status">
<p>{Review}</p>
</aside>
</div>
<p className="by-creator">by <Link href="" target="_blank">{stats.creator}</Link></p>
<span className="spacer"></span>
<Comments comments_data={stats}/>
</main>
)
}
export default function SubmissionInfoPage() {
const dynamicId = useParams<{submissionId: string}>()
return (
<Webpage>
<main className="map-page-main">
<section className="review-section">
<RatingArea id={432}/>
<TitleAndComments name={dynamicId.submissionId} creator="Quaternions" review={SubmissionStatus.Accepted} comments={[]}/>
</section>
</main>
</Webpage>
)
}

View File

@ -1,48 +0,0 @@
const FALLBACK_IMAGE = ""
type thumbsizes = "420" | "720"
type thumbsize<S extends thumbsizes> = `${S}x${S}`
type ParsedJson<A> = {
errors: A,
data: {
[0]: {
state: string,
imageUrl: string,
}
}
}
function Parse<A>(json: ParsedJson<A>): string {
if (json.errors) {
console.warn(json.errors)
return FALLBACK_IMAGE
}
if (json.data) {
const data = json.data[0]
if (!data) { //For whatever reason roblox will sometimes return an empty array instead of an error message
console.warn("Roblox gave us no data,", data)
return FALLBACK_IMAGE
}
if (data.state === "Completed") {
return data.imageUrl
}
console.warn(data)
return FALLBACK_IMAGE
}
return FALLBACK_IMAGE
}
async function AvatarHeadshot<S extends thumbsizes>(userid: number, size: thumbsize<S>): Promise<string> {
const avatarthumb_api = fetch(`https://thumbnails.roproxy.com/v1/users/avatar-headshot?userIds=${userid}&size=${size}&format=Png&isCircular=false`)
return avatarthumb_api.then(res => res.json()).then(json => Parse(json))
}
async function AssetImage<S extends thumbsizes>(assetid: number, size: thumbsize<S>): Promise<string> {
const avatarthumb_api = fetch(`https://thumbnails.roblox.com/v1/assets?assetIds=${assetid}&returnPolicy=PlaceHolder&size=${size}&format=Png&isCircular=false`)
return avatarthumb_api.then(res => res.json()).then(json => Parse(json))
}
export {
AvatarHeadshot,
AssetImage
}

View File

@ -1,56 +0,0 @@
const enum SubmissionStatus {
Published,
Rejected,
Publishing,
Validated,
Validating,
Accepted,
ChangesRequested,
Submitted,
UnderConstruction
}
interface SubmissionInfo {
readonly ID: number,
readonly DisplayName: string,
readonly Creator: string,
readonly GameID: number,
readonly Date: number,
readonly Submitter: number,
readonly AssetID: number,
readonly AssetVersion: number,
readonly Completed: boolean,
readonly TargetAssetID: number,
readonly StatusID: SubmissionStatus
}
function SubmissionStatusToString(submission_status: SubmissionStatus): string {
switch (submission_status) {
case SubmissionStatus.Published:
return "PUBLISHED"
case SubmissionStatus.Rejected:
return "REJECTED"
case SubmissionStatus.Publishing:
return "PUBLISHING"
case SubmissionStatus.Validated:
return "VALIDATED"
case SubmissionStatus.Validating:
return "VALIDATING"
case SubmissionStatus.Accepted:
return "ACCEPTED"
case SubmissionStatus.ChangesRequested:
return "CHANGES REQUESTED"
case SubmissionStatus.Submitted:
return "SUBMITTED"
case SubmissionStatus.UnderConstruction:
return "UNDER CONSTRUCTION"
default:
return "UNKNOWN"
}
}
export {
SubmissionStatus,
SubmissionStatusToString,
type SubmissionInfo
}

View File

@ -1,42 +0,0 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"noImplicitAny": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": [
"./src/*"
]
}
},
"include": [
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"next-env.d.ts",
"build/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}