Compare commits

...

130 Commits
nats ... master

Author SHA1 Message Date
dff37906c6 web: reorder status buttons 2024-12-13 21:22:23 -08:00
50911af656 web: fix proxy 2024-12-13 21:22:23 -08:00
1243203085 web: do not overwrite existing cookie 2024-12-13 21:22:23 -08:00
7ae2775c3f docker: pass through environment variable 2024-12-13 21:05:33 -08:00
fcd74b54cc validation: fix writer 2024-12-13 21:05:33 -08:00
b090a80f03 validation: update deps 2024-12-13 21:05:33 -08:00
3a6a62fb7c nats: periods and underscores are forbidden and cause silent failure 2024-12-13 21:05:33 -08:00
3739ff011e docker: silence authrpc container logging 2024-12-13 21:05:33 -08:00
046d95c5b3 web: change hardcoded dev address 2024-12-13 21:05:33 -08:00
090c794c24 docker: use images instead of build 2024-12-13 21:05:33 -08:00
4373ca4ba9 validation: print payload 2024-12-12 19:27:39 -08:00
f77dd14ac9 validation: parallel request processing 2024-12-12 19:27:39 -08:00
727e358cf9 validation: connect to nats and grpc concurrently 2024-12-12 17:52:43 -08:00
8250686477 validation: refactor to use a single consumer 2024-12-12 17:52:43 -08:00
1f96b5facb validation: incorrect nats assumption 2024-12-12 16:58:16 -08:00
22086e772c validation: listen for sigkill 2024-12-12 16:58:16 -08:00
c99608aaff docker: add all 8 services to single compose file 2024-12-12 16:50:48 -08:00
fdc1f1aadf Unquatification 2024-12-12 17:29:20 -05:00
3c0028f308 web: working Container file 2024-12-12 14:24:07 -08:00
a2c6981d36 web: proxy the dev port 3000 to 8081 for api calls 2024-12-12 14:24:07 -08:00
d37cc9cdf2 CSS variables for coloring 2024-12-12 14:24:07 -08:00
0eaf9aa164 docker: fix validation 2024-12-12 14:24:07 -08:00
d37964764e docker: fix maps-service 2024-12-12 14:24:07 -08:00
55b5516782 docker: secrets! 2024-12-12 13:52:25 -08:00
4c5d9e420d validation: fix cargo chef 2024-12-12 12:51:44 -08:00
dee563e12a update docker stuff 2024-12-12 12:29:15 -08:00
5ef2de6f50 validation: add strafesnet registry 2024-12-12 12:29:12 -08:00
95e9b4d8b3 nats: work queue policy 2024-12-11 23:02:59 -08:00
eea57786ff nats: use subjects 2024-12-11 21:08:04 -08:00
64d6fe6059 validation: consumers are not durable 2024-12-11 21:07:57 -08:00
7f1054d509 maps-service: nats jetstream 2024-12-11 19:56:09 -08:00
ecaff95ca7 validation: nats jetstream 2024-12-11 19:55:16 -08:00
7c2e8f00ad web: custom action per review button 2024-12-11 18:11:36 -08:00
1a747bee80 openapi: rename unclear endpoints 2024-12-11 18:11:36 -08:00
a9a4eaa20d validation: implement publish_fix 2024-12-11 18:11:36 -08:00
796935da86 validation: implement publish_new 2024-12-11 17:45:38 -08:00
6677a698be plumb game id 2024-12-11 17:45:38 -08:00
aea340fe97 Submission buttons working? 2024-12-11 19:07:54 -05:00
2a04efb10b change error message to make more sense 2024-12-11 15:39:10 -08:00
119bb79a88 Update readme for web 2024-12-11 18:36:47 -05:00
c630b44ad3 Containerfile for web 2024-12-11 17:07:15 -05:00
bc5b84d447 buttons exist for making api requests 2024-12-11 03:30:55 -05:00
89fda96e7a implicitly assume validator endpoints are authorized 2024-12-10 22:32:56 -08:00
803223d331 validation: fix url 2024-12-10 22:29:15 -08:00
dad5a40a09 validation: fix method 2024-12-10 22:28:52 -08:00
88056344f4 openapi: generate 2024-12-10 22:23:08 -08:00
0bc8eeffb3 openapi: set http return code according to standard 2024-12-10 22:22:37 -08:00
afa78a1260 openapi: implement cookie auth only on select requests 2024-12-10 22:22:12 -08:00
590627870a implement some basic status codes for errors 2024-12-10 21:49:03 -08:00
ca5c67cb4b check rows affected to see if anything happened 2024-12-10 21:38:12 -08:00
e3360a6c6b the gorm way 2024-12-10 21:31:18 -08:00
1975f3ea4a validation: mtls is handled at a higher level 2024-12-10 21:25:53 -08:00
03e71f09fc validation: golang public fields must be upper case gg 2024-12-10 20:25:40 -08:00
1d545f4413 explicitly specify the port for bun run start 2024-12-10 23:17:58 -05:00
2e379f7077 successfully builds for production 2024-12-10 23:01:22 -05:00
6ce2cd799f update README for web 2024-12-10 22:59:13 -05:00
305f18adb3 send nats
dunk on golang
2024-12-10 19:27:15 -08:00
e185712da5 nasty 2024-12-10 19:25:21 -08:00
3e58d75b87 userids and asset ids are uint64 2024-12-10 19:25:21 -08:00
b49ae10a10 fix roles 2024-12-10 18:23:23 -08:00
24e88c5a0a validation: change error enum names to be more descriptive 2024-12-10 17:47:17 -08:00
c029939ee2 validation: use environment variables 2024-12-10 17:39:08 -08:00
dedbdb2899 validation: silence "unused" code lint 2024-12-10 17:38:47 -08:00
1324fa106f validation: use incremental model version id 2024-12-10 17:38:21 -08:00
f110d82402 docker compose 2024-12-10 17:03:18 -08:00
cf488101a0 add eslint + update nextjs to remove a library deprecation warning on dev start
* requires running `bun install` again, or `bun update`
2024-12-10 17:55:16 -05:00
fbc183f408 change the routing from DOMAIN/map/[map_name] to DOMAIN/submissions/[submission_id] 2024-12-10 17:53:57 -05:00
1a5f58a3b1 map page now builds and heavily refactored
+ Handle no commenters by displaying the message "There are no commenters."
+ Text fallback: "Fetching map image..."
+ Handle the validation status message (still has no css coloring for 80% of the statuses)
- Disable the roblox api for fetching the asset image and avatar headshot images, but keep the module
2024-12-10 03:54:19 -05:00
37e4e29f04 use gorm model 2024-12-09 22:27:52 -08:00
69047eacb0 update golang side 2024-12-09 22:09:52 -08:00
ccb9c17e7f openapi: generate 2024-12-09 22:07:43 -08:00
7b343fc686 openapi: deduplicate parameter ids 2024-12-09 22:05:47 -08:00
7aaa89b69b openapi: change patch to post 2024-12-09 21:57:35 -08:00
3cc1c8a5d9 openapi: limit string lengths for user facing objects 2024-12-09 21:48:52 -08:00
04520edca7 fix IfStatusThenUpdate 2024-12-09 21:38:27 -08:00
fe3562325f fix user info 2024-12-09 21:38:27 -08:00
b3ba9c2301 openapi: unused 2024-12-09 21:38:27 -08:00
1c0ed6574f openapi: remove redundant SubmissionType 2024-12-09 20:51:00 -08:00
576c3a187a produce less garbage 2024-12-09 20:10:36 -08:00
e495a8423f openapi: try session_id 2024-12-09 20:10:36 -08:00
fb6b0ad7c7 cli flag for auth rpc host 2024-12-09 19:33:01 -08:00
8c3e9cf9ea remove empty readme and tweak .gitignore, next.config.ts, and package.json 2024-12-09 22:24:16 -05:00
76792fd144 README: write instructions for each component 2024-12-09 18:35:49 -08:00
9097c551c4 not using openapi-generator (it's java) 2024-12-09 18:28:44 -08:00
0b46ee28a4 open source 2024-12-09 18:16:07 -08:00
6adf9a8977 web folder (dont use this code) 2024-12-09 21:14:54 -05:00
fe6a88a479 tweak submission comment 2024-12-09 17:34:55 -08:00
c3b559f6a6 tweak openapi description 2024-12-09 17:34:46 -08:00
dac135f624 partially implement publish_new 2024-12-06 21:00:19 -08:00
2aacb4c87c split publishing for new maps and map fixes 2024-12-06 20:31:28 -08:00
f46df3a8eb apparently you can just do it, thanks rust 2024-12-06 20:07:15 -08:00
bf3698c1db tweak comment 2024-12-06 19:47:06 -08:00
a019e61556 remove unnecessary data structure 2024-12-06 19:45:44 -08:00
572e51961c validation: mark submission as validated 2024-12-06 19:43:06 -08:00
3289376f8e rust api: action_submission_validate 2024-12-06 19:43:06 -08:00
f99dbb1b9d fallible functions cannot exist in the error suppressor 2024-12-06 19:43:06 -08:00
1a5e6e90c6 presumptuously rename error 2024-12-06 19:36:10 -08:00
c77a34804d i64 😭 2024-12-06 19:27:20 -08:00
b7d04da9e0 rust api: implement update_submission_model 2024-12-06 19:19:01 -08:00
1133e00ccc fix api 2024-12-06 19:02:57 -08:00
56fefbe52d fat concurrent script fetching 2024-12-06 18:59:33 -08:00
0977943834 fix hash 2024-12-06 18:57:11 -08:00
bc6613f235 implement script policy 2024-12-06 18:19:35 -08:00
33ea38d35f unused 2024-12-06 18:19:35 -08:00
86121bbfc1 implement siphash (confirmed identical hash) 2024-12-06 18:19:35 -08:00
a97738b378 implement scripts crud 2024-12-05 20:26:51 -08:00
2dcae799da script always has a SubmissionID, it's just 0 2024-12-05 20:26:27 -08:00
ee7e5371a8 tweak roles 2024-12-05 19:10:09 -08:00
0c2ee16bde openapi: not optional 2024-12-05 18:56:44 -08:00
005f2ddaee rust api: patch 2024-12-05 18:49:09 -08:00
329eb1663c new api skeleton 2024-12-05 16:50:35 -08:00
80425813c3 fix non-optional values 2024-12-05 16:50:35 -08:00
5899efd759 openapi: generate 2024-12-05 16:50:35 -08:00
3b85c98e10 openapi: massive crud 2024-12-05 16:50:35 -08:00
1a48a4fb5a tweak policy 2024-12-05 16:50:35 -08:00
8f74a3f783 common service errors 2024-12-05 16:50:35 -08:00
8c254aef6e scripts & script policy 2024-12-05 16:50:35 -08:00
e7375cde11 openapi: script stuff 2024-12-04 19:07:40 -08:00
4ed70dbcad custom rust api client for maps-service 2024-12-04 19:07:40 -08:00
a457faef56 delete openapi-generator rust code - it's awful and doesn't even compile 2024-12-04 19:04:14 -08:00
0d2c412fd4 add reflection database back, not making a strafesnet roblox map lib 2024-12-04 19:00:31 -08:00
d0e6233d68 scripts can be rejected lol 2024-12-03 20:23:06 -08:00
ce5d84a646 openapi: add scripts operations 2024-12-03 19:29:59 -08:00
799346aee6 openapi: remove junk 2024-12-03 19:29:50 -08:00
6a6e68e8df add api dependency 2024-12-03 19:18:40 -08:00
42935ce990 generate api for rust 2024-12-03 19:13:29 -08:00
dd193e6e8f wip: write some script validation code 2024-12-03 18:54:33 -08:00
dc7645319e remove sqlx 2024-12-03 17:43:37 -08:00
fad27b6792 do not reply with nats 2024-12-03 17:37:33 -08:00
6c3942e37d cloud only needed to upload places 2024-12-02 23:34:25 -08:00
83 changed files with 11635 additions and 2608 deletions

36
Containerfile Normal file
View File

@ -0,0 +1,36 @@
# 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 Normal file
View File

@ -0,0 +1,23 @@
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.

8
Makefile Normal file
View File

@ -0,0 +1,8 @@
.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,6 +1,15 @@
# STRAFES.NET API # STRAFES.NET Map Submission System
## How to Begin Development ## Components
- Submissions API (golang) `pkg/` `cmd/`
- Website `/web/`
- Script Validation (rust) `validation/`
## How to Begin Development on Each Component
### Submissions API
Prerequisite: golang installed
1. Run `go generate` to ensure the generated API is up-to-date. This project uses [ogen](https://github.com/ogen-go/ogen). 1. Run `go generate` to ensure the generated API is up-to-date. This project uses [ogen](https://github.com/ogen-go/ogen).
```bash ```bash
@ -12,3 +21,38 @@
``` ```
By default, the project opens at `localhost:8080`. 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>

119
compose.yaml Normal file
View File

@ -0,0 +1,119 @@
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:

5
go.mod
View File

@ -7,8 +7,10 @@ toolchain go1.23.3
require ( require (
git.itzana.me/strafesnet/go-grpc v0.0.0-20241129081229-9e166b3d11f7 git.itzana.me/strafesnet/go-grpc v0.0.0-20241129081229-9e166b3d11f7
git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9 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/errors v0.7.1
github.com/go-faster/jx v1.1.0 github.com/go-faster/jx v1.1.0
github.com/nats-io/nats.go v1.37.0
github.com/ogen-go/ogen v1.2.1 github.com/ogen-go/ogen v1.2.1
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/urfave/cli/v2 v2.27.5 github.com/urfave/cli/v2 v2.27.5
@ -29,6 +31,9 @@ require (
github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/klauspost/compress v1.17.6 // indirect
github.com/nats-io/nkeys v0.4.7 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
golang.org/x/crypto v0.23.0 // indirect golang.org/x/crypto v0.23.0 // indirect

10
go.sum
View File

@ -20,6 +20,8 @@ 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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 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= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -86,6 +88,8 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -95,6 +99,12 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/ogen-go/ogen v1.2.1 h1:C5A0lvUMu2wl+eWIxnpXMWnuOJ26a2FyzR1CIC2qG0M= github.com/ogen-go/ogen v1.2.1 h1:C5A0lvUMu2wl+eWIxnpXMWnuOJ26a2FyzR1CIC2qG0M=
github.com/ogen-go/ogen v1.2.1/go.mod h1:P2zQdEu8UqaVRfD5GEFvl+9q63VjMLvDquq1wVbyInM= github.com/ogen-go/ogen v1.2.1/go.mod h1:P2zQdEu8UqaVRfD5GEFvl+9q63VjMLvDquq1wVbyInM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

View File

@ -1,21 +1,18 @@
openapi: 3.1.0 openapi: 3.1.0
info: info:
title: StrafesNET Submissions - OpenAPI 3.1 title: StrafesNET Submissions - OpenAPI 3.1
description: Browse and manage map submissions in the staging pipeline. description: Browse and manage map submissions.
version: 0.1.0 version: 0.1.0
servers: servers:
- url: https://submissions.strafes.net/v1 - url: https://submissions.strafes.net/v1
tags: tags:
- name: Submissions - name: Submissions
description: Submission operations description: Submission operations
security: - name: Scripts
- cookieAuth: [] description: Script operations
- name: ScriptPolicy
description: Script policy operations
paths: paths:
# status
# submit
# accept
# publish
# complete
/submissions: /submissions:
get: get:
summary: Get list of submissions summary: Get list of submissions
@ -33,6 +30,8 @@ paths:
required: false required: false
schema: schema:
$ref: "#/components/schemas/SubmissionFilter" $ref: "#/components/schemas/SubmissionFilter"
security:
- cookieAuth: []
responses: responses:
"200": "200":
description: Successful response description: Successful response
@ -54,12 +53,15 @@ paths:
tags: tags:
- Submissions - Submissions
requestBody: requestBody:
required: true
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/SubmissionCreate' $ref: '#/components/schemas/SubmissionCreate'
security:
- cookieAuth: []
responses: responses:
"200": "201":
description: Successful response description: Successful response
content: content:
application/json: application/json:
@ -78,12 +80,9 @@ paths:
tags: tags:
- Submissions - Submissions
parameters: parameters:
- name: SubmissionID - $ref: '#/components/parameters/SubmissionID'
in: path security:
required: true - cookieAuth: []
schema:
type: integer
format: int64
responses: responses:
"200": "200":
description: Successful response description: Successful response
@ -98,18 +97,13 @@ paths:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/model: /submissions/{SubmissionID}/model:
patch: post:
summary: Update model following role restrictions summary: Update model following role restrictions
operationId: patchSubmissionModel operationId: updateSubmissionModel
tags: tags:
- Submissions - Submissions
parameters: parameters:
- name: SubmissionID - $ref: '#/components/parameters/SubmissionID'
in: path
required: true
schema:
type: integer
format: int64
- name: ModelID - name: ModelID
in: query in: query
required: true required: true
@ -122,8 +116,10 @@ paths:
schema: schema:
type: integer type: integer
format: int64 format: int64
security:
- cookieAuth: []
responses: responses:
"200": "204":
description: Successful response description: Successful response
default: default:
description: General Error description: General Error
@ -132,20 +128,17 @@ paths:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/completed: /submissions/{SubmissionID}/completed:
patch: post:
summary: Retrieve map with ID summary: Retrieve map with ID
operationId: patchSubmissionCompleted operationId: setSubmissionCompleted
tags: tags:
- Submissions - Submissions
parameters: parameters:
- name: SubmissionID - $ref: '#/components/parameters/SubmissionID'
in: path security:
required: true - cookieAuth: []
schema:
type: integer
format: int64
responses: responses:
"200": "204":
description: Successful response description: Successful response
default: default:
description: General Error description: General Error
@ -154,20 +147,17 @@ paths:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/submit: /submissions/{SubmissionID}/status/submit:
patch: post:
summary: Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted summary: Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted
operationId: actionSubmissionSubmit operationId: actionSubmissionSubmit
tags: tags:
- Submissions - Submissions
parameters: parameters:
- name: SubmissionID - $ref: '#/components/parameters/SubmissionID'
in: path security:
required: true - cookieAuth: []
schema:
type: integer
format: int64
responses: responses:
"200": "204":
description: Successful response description: Successful response
default: default:
description: General Error description: General Error
@ -176,20 +166,17 @@ paths:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/revoke: /submissions/{SubmissionID}/status/revoke:
patch: post:
summary: Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction summary: Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction
operationId: actionSubmissionRevoke operationId: actionSubmissionRevoke
tags: tags:
- Submissions - Submissions
parameters: parameters:
- name: SubmissionID - $ref: '#/components/parameters/SubmissionID'
in: path security:
required: true - cookieAuth: []
schema:
type: integer
format: int64
responses: responses:
"200": "204":
description: Successful response description: Successful response
default: default:
description: General Error description: General Error
@ -198,20 +185,17 @@ paths:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/trigger-validate: /submissions/{SubmissionID}/status/trigger-validate:
patch: post:
summary: Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating summary: Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating
operationId: actionSubmissionTriggerValidate operationId: actionSubmissionTriggerValidate
tags: tags:
- Submissions - Submissions
parameters: parameters:
- name: SubmissionID - $ref: '#/components/parameters/SubmissionID'
in: path security:
required: true - cookieAuth: []
schema:
type: integer
format: int64
responses: responses:
"200": "204":
description: Successful response description: Successful response
default: default:
description: General Error description: General Error
@ -220,20 +204,17 @@ paths:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/reject: /submissions/{SubmissionID}/status/reject:
patch: post:
summary: Role Reviewer changes status from Submitted -> Rejected summary: Role Reviewer changes status from Submitted -> Rejected
operationId: actionSubmissionReject operationId: actionSubmissionReject
tags: tags:
- Submissions - Submissions
parameters: parameters:
- name: SubmissionID - $ref: '#/components/parameters/SubmissionID'
in: path security:
required: true - cookieAuth: []
schema:
type: integer
format: int64
responses: responses:
"200": "204":
description: Successful response description: Successful response
default: default:
description: General Error description: General Error
@ -242,20 +223,17 @@ paths:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/request-changes: /submissions/{SubmissionID}/status/request-changes:
patch: post:
summary: Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested summary: Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested
operationId: actionSubmissionRequestChanges operationId: actionSubmissionRequestChanges
tags: tags:
- Submissions - Submissions
parameters: parameters:
- name: SubmissionID - $ref: '#/components/parameters/SubmissionID'
in: path security:
required: true - cookieAuth: []
schema:
type: integer
format: int64
responses: responses:
"200": "204":
description: Successful response description: Successful response
default: default:
description: General Error description: General Error
@ -263,21 +241,16 @@ paths:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/validate: /submissions/{SubmissionID}/status/validator-validated:
patch: post:
summary: Role Validator changes status from Validating -> Validated summary: (Internal endpoint) Role Validator changes status from Validating -> Validated
operationId: actionSubmissionValidate operationId: actionSubmissionValidate
tags: tags:
- Submissions - Submissions
parameters: parameters:
- name: SubmissionID - $ref: '#/components/parameters/SubmissionID'
in: path
required: true
schema:
type: integer
format: int64
responses: responses:
"200": "204":
description: Successful response description: Successful response
default: default:
description: General Error description: General Error
@ -285,21 +258,16 @@ paths:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/publish: /submissions/{SubmissionID}/status/validator-published:
patch: post:
summary: Role Validator changes status from Publishing -> Published summary: (Internal endpoint) Role Validator changes status from Publishing -> Published
operationId: actionSubmissionPublish operationId: actionSubmissionPublish
tags: tags:
- Submissions - Submissions
parameters: parameters:
- name: SubmissionID - $ref: '#/components/parameters/SubmissionID'
in: path
required: true
schema:
type: integer
format: int64
responses: responses:
"200": "204":
description: Successful response description: Successful response
default: default:
description: General Error description: General Error
@ -308,21 +276,231 @@ paths:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/trigger-publish: /submissions/{SubmissionID}/status/trigger-publish:
patch: post:
summary: Role Admin changes status from Validated -> Publishing summary: Role Admin changes status from Validated -> Publishing
operationId: actionSubmissionTriggerPublish operationId: actionSubmissionTriggerPublish
tags: tags:
- Submissions - Submissions
parameters: parameters:
- name: SubmissionID - $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
in: path in: path
required: true required: true
schema: schema:
type: integer type: string
format: int64 minLength: 16
maxLength: 16
security:
- cookieAuth: []
responses: responses:
"200": "200":
description: Successful response 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: default:
description: General Error description: General Error
content: content:
@ -334,26 +512,56 @@ components:
cookieAuth: cookieAuth:
type: apiKey type: apiKey
in: cookie in: cookie
name: SESSIONID 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
schemas: schemas:
Id: Id:
required:
- ID
type: object type: object
properties: properties:
ID: ID:
type: integer type: integer
format: int64 format: int64
User:
type: object
properties:
ID:
type: integer
format: int64
Username:
type: string
StateID:
type: integer
format: int32
Submission: Submission:
required:
- ID
- DisplayName
- Creator
- GameID
- CreatedAt
- UpdatedAt
- Submitter
- AssetID
- AssetVersion
- Completed
- SubmissionType
# - TargetAssetID
- StatusID
type: object type: object
properties: properties:
ID: ID:
@ -361,12 +569,17 @@ components:
format: int64 format: int64
DisplayName: DisplayName:
type: string type: string
maxLength: 128
Creator: Creator:
type: string type: string
maxLength: 128
GameID: GameID:
type: integer type: integer
format: int32 format: int32
Date: CreatedAt:
type: integer
format: int64
UpdatedAt:
type: integer type: integer
format: int64 format: int64
Submitter: Submitter:
@ -390,6 +603,8 @@ components:
type: integer type: integer
format: int32 format: int32
SubmissionFilter: SubmissionFilter:
required:
- ID
type: object type: object
properties: properties:
ID: ID:
@ -397,39 +612,142 @@ components:
format: int64 format: int64
DisplayName: DisplayName:
type: string type: string
maxLength: 128
Creator: Creator:
type: string type: string
maxLength: 128
GameID: GameID:
type: integer type: integer
format: int32 format: int32
Date:
type: integer
format: int64
SubmissionCreate: SubmissionCreate:
required:
- DisplayName
- Creator
- GameID
- AssetID
- AssetVersion
# - TargetAssetID
type: object type: object
properties: properties:
DisplayName: DisplayName:
type: string type: string
maxLength: 128
Creator: Creator:
type: string type: string
maxLength: 128
GameID: GameID:
type: integer type: integer
format: int32 format: int32
Submitter:
type: integer
format: int64
AssetID: AssetID:
type: integer type: integer
format: int64 format: int64
AssetVersion: AssetVersion:
type: integer type: integer
format: int64 format: int64
SubmissionType:
type: integer
format: int32
TargetAssetID: TargetAssetID:
type: integer type: integer
format: int64 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: Pagination:
type: object type: object
required: 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,9 +14,18 @@ const (
ActionSubmissionTriggerPublishOperation OperationName = "ActionSubmissionTriggerPublish" ActionSubmissionTriggerPublishOperation OperationName = "ActionSubmissionTriggerPublish"
ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate" ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate"
ActionSubmissionValidateOperation OperationName = "ActionSubmissionValidate" ActionSubmissionValidateOperation OperationName = "ActionSubmissionValidate"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission" CreateSubmissionOperation OperationName = "CreateSubmission"
DeleteScriptOperation OperationName = "DeleteScript"
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"
GetScriptOperation OperationName = "GetScript"
GetScriptPolicyOperation OperationName = "GetScriptPolicy"
GetScriptPolicyFromHashOperation OperationName = "GetScriptPolicyFromHash"
GetSubmissionOperation OperationName = "GetSubmission" GetSubmissionOperation OperationName = "GetSubmission"
ListSubmissionsOperation OperationName = "ListSubmissions" ListSubmissionsOperation OperationName = "ListSubmissions"
PatchSubmissionCompletedOperation OperationName = "PatchSubmissionCompleted" SetSubmissionCompletedOperation OperationName = "SetSubmissionCompleted"
PatchSubmissionModelOperation OperationName = "PatchSubmissionModel" UpdateScriptOperation OperationName = "UpdateScript"
UpdateScriptPolicyOperation OperationName = "UpdateScriptPolicy"
UpdateSubmissionModelOperation OperationName = "UpdateSubmissionModel"
) )

View File

@ -17,6 +17,7 @@ import (
// ActionSubmissionPublishParams is parameters of actionSubmissionPublish operation. // ActionSubmissionPublishParams is parameters of actionSubmissionPublish operation.
type ActionSubmissionPublishParams struct { type ActionSubmissionPublishParams struct {
// The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
} }
@ -82,6 +83,7 @@ func decodeActionSubmissionPublishParams(args [1]string, argsEscaped bool, r *ht
// ActionSubmissionRejectParams is parameters of actionSubmissionReject operation. // ActionSubmissionRejectParams is parameters of actionSubmissionReject operation.
type ActionSubmissionRejectParams struct { type ActionSubmissionRejectParams struct {
// The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
} }
@ -147,6 +149,7 @@ func decodeActionSubmissionRejectParams(args [1]string, argsEscaped bool, r *htt
// ActionSubmissionRequestChangesParams is parameters of actionSubmissionRequestChanges operation. // ActionSubmissionRequestChangesParams is parameters of actionSubmissionRequestChanges operation.
type ActionSubmissionRequestChangesParams struct { type ActionSubmissionRequestChangesParams struct {
// The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
} }
@ -212,6 +215,7 @@ func decodeActionSubmissionRequestChangesParams(args [1]string, argsEscaped bool
// ActionSubmissionRevokeParams is parameters of actionSubmissionRevoke operation. // ActionSubmissionRevokeParams is parameters of actionSubmissionRevoke operation.
type ActionSubmissionRevokeParams struct { type ActionSubmissionRevokeParams struct {
// The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
} }
@ -277,6 +281,7 @@ func decodeActionSubmissionRevokeParams(args [1]string, argsEscaped bool, r *htt
// ActionSubmissionSubmitParams is parameters of actionSubmissionSubmit operation. // ActionSubmissionSubmitParams is parameters of actionSubmissionSubmit operation.
type ActionSubmissionSubmitParams struct { type ActionSubmissionSubmitParams struct {
// The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
} }
@ -342,6 +347,7 @@ func decodeActionSubmissionSubmitParams(args [1]string, argsEscaped bool, r *htt
// ActionSubmissionTriggerPublishParams is parameters of actionSubmissionTriggerPublish operation. // ActionSubmissionTriggerPublishParams is parameters of actionSubmissionTriggerPublish operation.
type ActionSubmissionTriggerPublishParams struct { type ActionSubmissionTriggerPublishParams struct {
// The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
} }
@ -407,6 +413,7 @@ func decodeActionSubmissionTriggerPublishParams(args [1]string, argsEscaped bool
// ActionSubmissionTriggerValidateParams is parameters of actionSubmissionTriggerValidate operation. // ActionSubmissionTriggerValidateParams is parameters of actionSubmissionTriggerValidate operation.
type ActionSubmissionTriggerValidateParams struct { type ActionSubmissionTriggerValidateParams struct {
// The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
} }
@ -472,6 +479,7 @@ func decodeActionSubmissionTriggerValidateParams(args [1]string, argsEscaped boo
// ActionSubmissionValidateParams is parameters of actionSubmissionValidate operation. // ActionSubmissionValidateParams is parameters of actionSubmissionValidate operation.
type ActionSubmissionValidateParams struct { type ActionSubmissionValidateParams struct {
// The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
} }
@ -535,8 +543,354 @@ func decodeActionSubmissionValidateParams(args [1]string, argsEscaped bool, r *h
return params, nil 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. // GetSubmissionParams is parameters of getSubmission operation.
type GetSubmissionParams struct { type GetSubmissionParams struct {
// The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
} }
@ -668,7 +1022,7 @@ func decodeListSubmissionsParams(args [0]string, argsEscaped bool, r *http.Reque
Name: "filter", Name: "filter",
Style: uri.QueryStyleForm, Style: uri.QueryStyleForm,
Explode: true, Explode: true,
Fields: []uri.QueryParameterObjectField{{Name: "ID", Required: false}, {Name: "DisplayName", Required: false}, {Name: "Creator", Required: false}, {Name: "GameID", Required: false}, {Name: "Date", Required: false}}, Fields: []uri.QueryParameterObjectField{{Name: "ID", Required: true}, {Name: "DisplayName", Required: false}, {Name: "Creator", Required: false}, {Name: "GameID", Required: false}},
} }
if err := q.HasParam(cfg); err == nil { if err := q.HasParam(cfg); err == nil {
@ -684,6 +1038,21 @@ func decodeListSubmissionsParams(args [0]string, argsEscaped bool, r *http.Reque
}); err != nil { }); err != nil {
return err 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 return nil
}(); err != nil { }(); err != nil {
@ -696,12 +1065,13 @@ func decodeListSubmissionsParams(args [0]string, argsEscaped bool, r *http.Reque
return params, nil return params, nil
} }
// PatchSubmissionCompletedParams is parameters of patchSubmissionCompleted operation. // SetSubmissionCompletedParams is parameters of setSubmissionCompleted operation.
type PatchSubmissionCompletedParams struct { type SetSubmissionCompletedParams struct {
// The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
} }
func unpackPatchSubmissionCompletedParams(packed middleware.Parameters) (params PatchSubmissionCompletedParams) { func unpackSetSubmissionCompletedParams(packed middleware.Parameters) (params SetSubmissionCompletedParams) {
{ {
key := middleware.ParameterKey{ key := middleware.ParameterKey{
Name: "SubmissionID", Name: "SubmissionID",
@ -712,7 +1082,7 @@ func unpackPatchSubmissionCompletedParams(packed middleware.Parameters) (params
return params return params
} }
func decodePatchSubmissionCompletedParams(args [1]string, argsEscaped bool, r *http.Request) (params PatchSubmissionCompletedParams, _ error) { func decodeSetSubmissionCompletedParams(args [1]string, argsEscaped bool, r *http.Request) (params SetSubmissionCompletedParams, _ error) {
// Decode path: SubmissionID. // Decode path: SubmissionID.
if err := func() error { if err := func() error {
param := args[0] param := args[0]
@ -761,14 +1131,147 @@ func decodePatchSubmissionCompletedParams(args [1]string, argsEscaped bool, r *h
return params, nil return params, nil
} }
// PatchSubmissionModelParams is parameters of patchSubmissionModel operation. // UpdateScriptParams is parameters of updateScript operation.
type PatchSubmissionModelParams struct { 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.
SubmissionID int64 SubmissionID int64
ModelID int64 ModelID int64
VersionID int64 VersionID int64
} }
func unpackPatchSubmissionModelParams(packed middleware.Parameters) (params PatchSubmissionModelParams) { func unpackUpdateSubmissionModelParams(packed middleware.Parameters) (params UpdateSubmissionModelParams) {
{ {
key := middleware.ParameterKey{ key := middleware.ParameterKey{
Name: "SubmissionID", Name: "SubmissionID",
@ -793,7 +1296,7 @@ func unpackPatchSubmissionModelParams(packed middleware.Parameters) (params Patc
return params return params
} }
func decodePatchSubmissionModelParams(args [1]string, argsEscaped bool, r *http.Request) (params PatchSubmissionModelParams, _ error) { func decodeUpdateSubmissionModelParams(args [1]string, argsEscaped bool, r *http.Request) (params UpdateSubmissionModelParams, _ error) {
q := uri.NewQueryDecoder(r.URL.Query()) q := uri.NewQueryDecoder(r.URL.Query())
// Decode path: SubmissionID. // Decode path: SubmissionID.
if err := func() error { if err := func() error {

View File

@ -15,8 +15,8 @@ import (
"github.com/ogen-go/ogen/validate" "github.com/ogen-go/ogen/validate"
) )
func (s *Server) decodeCreateSubmissionRequest(r *http.Request) ( func (s *Server) decodeCreateScriptRequest(r *http.Request) (
req OptSubmissionCreate, req *ScriptCreate,
close func() error, close func() error,
rerr error, rerr error,
) { ) {
@ -35,9 +35,6 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
rerr = multierr.Append(rerr, close()) 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")) ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil { if err != nil {
return req, close, errors.Wrap(err, "parse media type") return req, close, errors.Wrap(err, "parse media type")
@ -45,7 +42,7 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
switch { switch {
case ct == "application/json": case ct == "application/json":
if r.ContentLength == 0 { if r.ContentLength == 0 {
return req, close, nil return req, close, validate.ErrBodyRequired
} }
buf, err := io.ReadAll(r.Body) buf, err := io.ReadAll(r.Body)
if err != nil { if err != nil {
@ -53,14 +50,13 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
} }
if len(buf) == 0 { if len(buf) == 0 {
return req, close, nil return req, close, validate.ErrBodyRequired
} }
d := jx.DecodeBytes(buf) d := jx.DecodeBytes(buf)
var request OptSubmissionCreate var request ScriptCreate
if err := func() error { if err := func() error {
request.Reset()
if err := request.Decode(d); err != nil { if err := request.Decode(d); err != nil {
return err return err
} }
@ -76,7 +72,283 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
} }
return req, close, err return req, close, err
} }
return request, close, nil 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,
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 SubmissionCreate
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) 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
default: default:
return req, close, validate.InvalidContentType(ct) return req, close, validate.InvalidContentType(ct)
} }

View File

@ -11,21 +11,71 @@ import (
ht "github.com/ogen-go/ogen/http" ht "github.com/ogen-go/ogen/http"
) )
func encodeCreateSubmissionRequest( func encodeCreateScriptRequest(
req OptSubmissionCreate, 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,
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 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, r *http.Request,
) error { ) error {
const contentType = "application/json" const contentType = "application/json"
if !req.Set {
// Keep request with empty body if value is not set.
return nil
}
e := new(jx.Encoder) e := new(jx.Encoder)
{ {
if req.Set {
req.Encode(e) req.Encode(e)
} }
}
encoded := e.Bytes() encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType) ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil return nil

View File

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

View File

@ -13,63 +13,147 @@ import (
ht "github.com/ogen-go/ogen/http" ht "github.com/ogen-go/ogen/http"
) )
func encodeActionSubmissionPublishResponse(response *ActionSubmissionPublishOK, w http.ResponseWriter, span trace.Span) error { func encodeActionSubmissionPublishResponse(response *ActionSubmissionPublishNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200) w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(200)) span.SetStatus(codes.Ok, http.StatusText(204))
return nil return nil
} }
func encodeActionSubmissionRejectResponse(response *ActionSubmissionRejectOK, w http.ResponseWriter, span trace.Span) error { func encodeActionSubmissionRejectResponse(response *ActionSubmissionRejectNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200) w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(200)) span.SetStatus(codes.Ok, http.StatusText(204))
return nil return nil
} }
func encodeActionSubmissionRequestChangesResponse(response *ActionSubmissionRequestChangesOK, w http.ResponseWriter, span trace.Span) error { func encodeActionSubmissionRequestChangesResponse(response *ActionSubmissionRequestChangesNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200) w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(200)) span.SetStatus(codes.Ok, http.StatusText(204))
return nil return nil
} }
func encodeActionSubmissionRevokeResponse(response *ActionSubmissionRevokeOK, w http.ResponseWriter, span trace.Span) error { func encodeActionSubmissionRevokeResponse(response *ActionSubmissionRevokeNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200) w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(200)) span.SetStatus(codes.Ok, http.StatusText(204))
return nil return nil
} }
func encodeActionSubmissionSubmitResponse(response *ActionSubmissionSubmitOK, w http.ResponseWriter, span trace.Span) error { func encodeActionSubmissionSubmitResponse(response *ActionSubmissionSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200) w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(200)) span.SetStatus(codes.Ok, http.StatusText(204))
return nil return nil
} }
func encodeActionSubmissionTriggerPublishResponse(response *ActionSubmissionTriggerPublishOK, w http.ResponseWriter, span trace.Span) error { func encodeActionSubmissionTriggerPublishResponse(response *ActionSubmissionTriggerPublishNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200) w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(200)) span.SetStatus(codes.Ok, http.StatusText(204))
return nil return nil
} }
func encodeActionSubmissionTriggerValidateResponse(response *ActionSubmissionTriggerValidateOK, w http.ResponseWriter, span trace.Span) error { func encodeActionSubmissionTriggerValidateResponse(response *ActionSubmissionTriggerValidateNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200) w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(200)) span.SetStatus(codes.Ok, http.StatusText(204))
return nil return nil
} }
func encodeActionSubmissionValidateResponse(response *ActionSubmissionValidateOK, w http.ResponseWriter, span trace.Span) error { func encodeActionSubmissionValidateResponse(response *ActionSubmissionValidateNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200) w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(200)) 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")
}
return nil return nil
} }
func encodeCreateSubmissionResponse(response *ID, w http.ResponseWriter, span trace.Span) error { 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.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200) w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200)) span.SetStatus(codes.Ok, http.StatusText(200))
@ -115,16 +199,30 @@ func encodeListSubmissionsResponse(response []Submission, w http.ResponseWriter,
return nil return nil
} }
func encodePatchSubmissionCompletedResponse(response *PatchSubmissionCompletedOK, w http.ResponseWriter, span trace.Span) error { func encodeSetSubmissionCompletedResponse(response *SetSubmissionCompletedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200) w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(200)) span.SetStatus(codes.Ok, http.StatusText(204))
return nil return nil
} }
func encodePatchSubmissionModelResponse(response *PatchSubmissionModelOK, w http.ResponseWriter, span trace.Span) error { func encodeUpdateScriptResponse(response *UpdateScriptNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200) w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(200)) 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))
return nil return nil
} }

View File

@ -49,9 +49,195 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break break
} }
switch elem[0] { switch elem[0] {
case '/': // Prefix: "/submissions" case '/': // Prefix: "/s"
origElem := elem origElem := elem
if l := len("/submissions"); len(elem) >= l && elem[0:l] == "/submissions" { if l := len("/s"); len(elem) >= l && elem[0:l] == "/s" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'c': // Prefix: "cript"
origElem := elem
if l := len("cript"); len(elem) >= l && elem[0:l] == "cript" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case '-': // Prefix: "-policy"
origElem := elem
if l := len("-policy"); len(elem) >= l && elem[0:l] == "-policy" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
switch r.Method {
case "POST":
s.handleCreateScriptPolicyRequest([0]string{}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
switch elem[0] {
case '/': // Prefix: "/"
origElem := elem
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'h': // Prefix: "hash/"
origElem := elem
if l := len("hash/"); len(elem) >= l && elem[0:l] == "hash/" {
elem = elem[l:]
} else {
break
}
// Param: "FromScriptHash"
// Leaf parameter
args[0] = elem
elem = ""
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "GET":
s.handleGetScriptPolicyFromHashRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "GET")
}
return
}
elem = origElem
case 'i': // Prefix: "id/"
origElem := elem
if l := len("id/"); len(elem) >= l && elem[0:l] == "id/" {
elem = elem[l:]
} else {
break
}
// Param: "ScriptPolicyID"
// Leaf parameter
args[0] = elem
elem = ""
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "DELETE":
s.handleDeleteScriptPolicyRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
case "GET":
s.handleGetScriptPolicyRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
case "POST":
s.handleUpdateScriptPolicyRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "DELETE,GET,POST")
}
return
}
elem = origElem
}
elem = origElem
}
elem = origElem
case 's': // Prefix: "s"
origElem := elem
if l := len("s"); len(elem) >= l && elem[0:l] == "s" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
switch r.Method {
case "POST":
s.handleCreateScriptRequest([0]string{}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
switch elem[0] {
case '/': // Prefix: "/"
origElem := elem
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:]
} else {
break
}
// Param: "ScriptID"
// Leaf parameter
args[0] = elem
elem = ""
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "DELETE":
s.handleDeleteScriptRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
case "GET":
s.handleGetScriptRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
case "POST":
s.handleUpdateScriptRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "DELETE,GET,POST")
}
return
}
elem = origElem
}
elem = origElem
}
elem = origElem
case 'u': // Prefix: "ubmissions"
origElem := elem
if l := len("ubmissions"); len(elem) >= l && elem[0:l] == "ubmissions" {
elem = elem[l:] elem = elem[l:]
} else { } else {
break break
@ -123,12 +309,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch r.Method { switch r.Method {
case "PATCH": case "POST":
s.handlePatchSubmissionCompletedRequest([1]string{ s.handleSetSubmissionCompletedRequest([1]string{
args[0], args[0],
}, elemIsEscaped, w, r) }, elemIsEscaped, w, r)
default: default:
s.notAllowed(w, r, "PATCH") s.notAllowed(w, r, "POST")
} }
return return
@ -146,12 +332,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch r.Method { switch r.Method {
case "PATCH": case "POST":
s.handlePatchSubmissionModelRequest([1]string{ s.handleUpdateSubmissionModelRequest([1]string{
args[0], args[0],
}, elemIsEscaped, w, r) }, elemIsEscaped, w, r)
default: default:
s.notAllowed(w, r, "PATCH") s.notAllowed(w, r, "POST")
} }
return return
@ -170,29 +356,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
break break
} }
switch elem[0] { switch elem[0] {
case 'p': // Prefix: "publish"
origElem := elem
if l := len("publish"); len(elem) >= l && elem[0:l] == "publish" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "PATCH":
s.handleActionSubmissionPublishRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "PATCH")
}
return
}
elem = origElem
case 'r': // Prefix: "re" case 'r': // Prefix: "re"
origElem := elem origElem := elem
if l := len("re"); len(elem) >= l && elem[0:l] == "re" { if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
@ -216,12 +379,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch r.Method { switch r.Method {
case "PATCH": case "POST":
s.handleActionSubmissionRejectRequest([1]string{ s.handleActionSubmissionRejectRequest([1]string{
args[0], args[0],
}, elemIsEscaped, w, r) }, elemIsEscaped, w, r)
default: default:
s.notAllowed(w, r, "PATCH") s.notAllowed(w, r, "POST")
} }
return return
@ -239,12 +402,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch r.Method { switch r.Method {
case "PATCH": case "POST":
s.handleActionSubmissionRequestChangesRequest([1]string{ s.handleActionSubmissionRequestChangesRequest([1]string{
args[0], args[0],
}, elemIsEscaped, w, r) }, elemIsEscaped, w, r)
default: default:
s.notAllowed(w, r, "PATCH") s.notAllowed(w, r, "POST")
} }
return return
@ -262,12 +425,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch r.Method { switch r.Method {
case "PATCH": case "POST":
s.handleActionSubmissionRevokeRequest([1]string{ s.handleActionSubmissionRevokeRequest([1]string{
args[0], args[0],
}, elemIsEscaped, w, r) }, elemIsEscaped, w, r)
default: default:
s.notAllowed(w, r, "PATCH") s.notAllowed(w, r, "POST")
} }
return return
@ -288,12 +451,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch r.Method { switch r.Method {
case "PATCH": case "POST":
s.handleActionSubmissionSubmitRequest([1]string{ s.handleActionSubmissionSubmitRequest([1]string{
args[0], args[0],
}, elemIsEscaped, w, r) }, elemIsEscaped, w, r)
default: default:
s.notAllowed(w, r, "PATCH") s.notAllowed(w, r, "POST")
} }
return return
@ -323,12 +486,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch r.Method { switch r.Method {
case "PATCH": case "POST":
s.handleActionSubmissionTriggerPublishRequest([1]string{ s.handleActionSubmissionTriggerPublishRequest([1]string{
args[0], args[0],
}, elemIsEscaped, w, r) }, elemIsEscaped, w, r)
default: default:
s.notAllowed(w, r, "PATCH") s.notAllowed(w, r, "POST")
} }
return return
@ -346,12 +509,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch r.Method { switch r.Method {
case "PATCH": case "POST":
s.handleActionSubmissionTriggerValidateRequest([1]string{ s.handleActionSubmissionTriggerValidateRequest([1]string{
args[0], args[0],
}, elemIsEscaped, w, r) }, elemIsEscaped, w, r)
default: default:
s.notAllowed(w, r, "PATCH") s.notAllowed(w, r, "POST")
} }
return return
@ -361,9 +524,21 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
elem = origElem elem = origElem
case 'v': // Prefix: "validate" case 'v': // Prefix: "validator-"
origElem := elem origElem := elem
if l := len("validate"); len(elem) >= l && elem[0:l] == "validate" { if l := len("validator-"); len(elem) >= l && elem[0:l] == "validator-" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'p': // Prefix: "published"
origElem := elem
if l := len("published"); len(elem) >= l && elem[0:l] == "published" {
elem = elem[l:] elem = elem[l:]
} else { } else {
break break
@ -372,12 +547,35 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch r.Method { switch r.Method {
case "PATCH": case "POST":
s.handleActionSubmissionPublishRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
elem = origElem
case 'v': // Prefix: "validated"
origElem := elem
if l := len("validated"); len(elem) >= l && elem[0:l] == "validated" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionValidateRequest([1]string{ s.handleActionSubmissionValidateRequest([1]string{
args[0], args[0],
}, elemIsEscaped, w, r) }, elemIsEscaped, w, r)
default: default:
s.notAllowed(w, r, "PATCH") s.notAllowed(w, r, "POST")
} }
return return
@ -397,6 +595,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
elem = origElem elem = origElem
} }
elem = origElem
}
elem = origElem
}
} }
s.notFound(w, r) s.notFound(w, r)
} }
@ -476,9 +680,225 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break break
} }
switch elem[0] { switch elem[0] {
case '/': // Prefix: "/submissions" case '/': // Prefix: "/s"
origElem := elem origElem := elem
if l := len("/submissions"); len(elem) >= l && elem[0:l] == "/submissions" { if l := len("/s"); len(elem) >= l && elem[0:l] == "/s" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'c': // Prefix: "cript"
origElem := elem
if l := len("cript"); len(elem) >= l && elem[0:l] == "cript" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case '-': // Prefix: "-policy"
origElem := elem
if l := len("-policy"); len(elem) >= l && elem[0:l] == "-policy" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
switch method {
case "POST":
r.name = CreateScriptPolicyOperation
r.summary = "Create a new script policy"
r.operationID = "createScriptPolicy"
r.pathPattern = "/script-policy"
r.args = args
r.count = 0
return r, true
default:
return
}
}
switch elem[0] {
case '/': // Prefix: "/"
origElem := elem
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'h': // Prefix: "hash/"
origElem := elem
if l := len("hash/"); len(elem) >= l && elem[0:l] == "hash/" {
elem = elem[l:]
} else {
break
}
// Param: "FromScriptHash"
// Leaf parameter
args[0] = elem
elem = ""
if len(elem) == 0 {
// Leaf node.
switch method {
case "GET":
r.name = GetScriptPolicyFromHashOperation
r.summary = "Get the policy for the given hash of script source code"
r.operationID = "getScriptPolicyFromHash"
r.pathPattern = "/script-policy/hash/{FromScriptHash}"
r.args = args
r.count = 1
return r, true
default:
return
}
}
elem = origElem
case 'i': // Prefix: "id/"
origElem := elem
if l := len("id/"); len(elem) >= l && elem[0:l] == "id/" {
elem = elem[l:]
} else {
break
}
// Param: "ScriptPolicyID"
// Leaf parameter
args[0] = elem
elem = ""
if len(elem) == 0 {
// Leaf node.
switch method {
case "DELETE":
r.name = DeleteScriptPolicyOperation
r.summary = "Delete the specified script policy by ID"
r.operationID = "deleteScriptPolicy"
r.pathPattern = "/script-policy/id/{ScriptPolicyID}"
r.args = args
r.count = 1
return r, true
case "GET":
r.name = GetScriptPolicyOperation
r.summary = "Get the specified script policy by ID"
r.operationID = "getScriptPolicy"
r.pathPattern = "/script-policy/id/{ScriptPolicyID}"
r.args = args
r.count = 1
return r, true
case "POST":
r.name = UpdateScriptPolicyOperation
r.summary = "Update the specified script policy by ID"
r.operationID = "updateScriptPolicy"
r.pathPattern = "/script-policy/id/{ScriptPolicyID}"
r.args = args
r.count = 1
return r, true
default:
return
}
}
elem = origElem
}
elem = origElem
}
elem = origElem
case 's': // Prefix: "s"
origElem := elem
if l := len("s"); len(elem) >= l && elem[0:l] == "s" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
switch method {
case "POST":
r.name = CreateScriptOperation
r.summary = "Create a new script"
r.operationID = "createScript"
r.pathPattern = "/scripts"
r.args = args
r.count = 0
return r, true
default:
return
}
}
switch elem[0] {
case '/': // Prefix: "/"
origElem := elem
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:]
} else {
break
}
// Param: "ScriptID"
// Leaf parameter
args[0] = elem
elem = ""
if len(elem) == 0 {
// Leaf node.
switch method {
case "DELETE":
r.name = DeleteScriptOperation
r.summary = "Delete the specified script by ID"
r.operationID = "deleteScript"
r.pathPattern = "/scripts/{ScriptID}"
r.args = args
r.count = 1
return r, true
case "GET":
r.name = GetScriptOperation
r.summary = "Get the specified script by ID"
r.operationID = "getScript"
r.pathPattern = "/scripts/{ScriptID}"
r.args = args
r.count = 1
return r, true
case "POST":
r.name = UpdateScriptOperation
r.summary = "Update the specified script by ID"
r.operationID = "updateScript"
r.pathPattern = "/scripts/{ScriptID}"
r.args = args
r.count = 1
return r, true
default:
return
}
}
elem = origElem
}
elem = origElem
}
elem = origElem
case 'u': // Prefix: "ubmissions"
origElem := elem
if l := len("ubmissions"); len(elem) >= l && elem[0:l] == "ubmissions" {
elem = elem[l:] elem = elem[l:]
} else { } else {
break break
@ -562,10 +982,10 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch method { switch method {
case "PATCH": case "POST":
r.name = PatchSubmissionCompletedOperation r.name = SetSubmissionCompletedOperation
r.summary = "Retrieve map with ID" r.summary = "Retrieve map with ID"
r.operationID = "patchSubmissionCompleted" r.operationID = "setSubmissionCompleted"
r.pathPattern = "/submissions/{SubmissionID}/completed" r.pathPattern = "/submissions/{SubmissionID}/completed"
r.args = args r.args = args
r.count = 1 r.count = 1
@ -587,10 +1007,10 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch method { switch method {
case "PATCH": case "POST":
r.name = PatchSubmissionModelOperation r.name = UpdateSubmissionModelOperation
r.summary = "Update model following role restrictions" r.summary = "Update model following role restrictions"
r.operationID = "patchSubmissionModel" r.operationID = "updateSubmissionModel"
r.pathPattern = "/submissions/{SubmissionID}/model" r.pathPattern = "/submissions/{SubmissionID}/model"
r.args = args r.args = args
r.count = 1 r.count = 1
@ -613,31 +1033,6 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
break break
} }
switch elem[0] { switch elem[0] {
case 'p': // Prefix: "publish"
origElem := elem
if l := len("publish"); len(elem) >= l && elem[0:l] == "publish" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "PATCH":
r.name = ActionSubmissionPublishOperation
r.summary = "Role Validator changes status from Publishing -> Published"
r.operationID = "actionSubmissionPublish"
r.pathPattern = "/submissions/{SubmissionID}/status/publish"
r.args = args
r.count = 1
return r, true
default:
return
}
}
elem = origElem
case 'r': // Prefix: "re" case 'r': // Prefix: "re"
origElem := elem origElem := elem
if l := len("re"); len(elem) >= l && elem[0:l] == "re" { if l := len("re"); len(elem) >= l && elem[0:l] == "re" {
@ -661,7 +1056,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch method { switch method {
case "PATCH": case "POST":
r.name = ActionSubmissionRejectOperation r.name = ActionSubmissionRejectOperation
r.summary = "Role Reviewer changes status from Submitted -> Rejected" r.summary = "Role Reviewer changes status from Submitted -> Rejected"
r.operationID = "actionSubmissionReject" r.operationID = "actionSubmissionReject"
@ -686,7 +1081,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch method { switch method {
case "PATCH": case "POST":
r.name = ActionSubmissionRequestChangesOperation r.name = ActionSubmissionRequestChangesOperation
r.summary = "Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested" r.summary = "Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested"
r.operationID = "actionSubmissionRequestChanges" r.operationID = "actionSubmissionRequestChanges"
@ -711,7 +1106,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch method { switch method {
case "PATCH": case "POST":
r.name = ActionSubmissionRevokeOperation r.name = ActionSubmissionRevokeOperation
r.summary = "Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction" r.summary = "Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction"
r.operationID = "actionSubmissionRevoke" r.operationID = "actionSubmissionRevoke"
@ -739,7 +1134,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch method { switch method {
case "PATCH": case "POST":
r.name = ActionSubmissionSubmitOperation r.name = ActionSubmissionSubmitOperation
r.summary = "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted" r.summary = "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted"
r.operationID = "actionSubmissionSubmit" r.operationID = "actionSubmissionSubmit"
@ -776,7 +1171,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch method { switch method {
case "PATCH": case "POST":
r.name = ActionSubmissionTriggerPublishOperation r.name = ActionSubmissionTriggerPublishOperation
r.summary = "Role Admin changes status from Validated -> Publishing" r.summary = "Role Admin changes status from Validated -> Publishing"
r.operationID = "actionSubmissionTriggerPublish" r.operationID = "actionSubmissionTriggerPublish"
@ -801,7 +1196,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch method { switch method {
case "PATCH": case "POST":
r.name = ActionSubmissionTriggerValidateOperation r.name = ActionSubmissionTriggerValidateOperation
r.summary = "Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating" r.summary = "Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating"
r.operationID = "actionSubmissionTriggerValidate" r.operationID = "actionSubmissionTriggerValidate"
@ -818,9 +1213,21 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
} }
elem = origElem elem = origElem
case 'v': // Prefix: "validate" case 'v': // Prefix: "validator-"
origElem := elem origElem := elem
if l := len("validate"); len(elem) >= l && elem[0:l] == "validate" { if l := len("validator-"); len(elem) >= l && elem[0:l] == "validator-" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
break
}
switch elem[0] {
case 'p': // Prefix: "published"
origElem := elem
if l := len("published"); len(elem) >= l && elem[0:l] == "published" {
elem = elem[l:] elem = elem[l:]
} else { } else {
break break
@ -829,11 +1236,11 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 { if len(elem) == 0 {
// Leaf node. // Leaf node.
switch method { switch method {
case "PATCH": case "POST":
r.name = ActionSubmissionValidateOperation r.name = ActionSubmissionPublishOperation
r.summary = "Role Validator changes status from Validating -> Validated" r.summary = "(Internal endpoint) Role Validator changes status from Publishing -> Published"
r.operationID = "actionSubmissionValidate" r.operationID = "actionSubmissionPublish"
r.pathPattern = "/submissions/{SubmissionID}/status/validate" r.pathPattern = "/submissions/{SubmissionID}/status/validator-published"
r.args = args r.args = args
r.count = 1 r.count = 1
return r, true return r, true
@ -842,6 +1249,37 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
} }
} }
elem = origElem
case 'v': // Prefix: "validated"
origElem := elem
if l := len("validated"); len(elem) >= l && elem[0:l] == "validated" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionValidateOperation
r.summary = "(Internal endpoint) Role Validator changes status from Validating -> Validated"
r.operationID = "actionSubmissionValidate"
r.pathPattern = "/submissions/{SubmissionID}/status/validator-validated"
r.args = args
r.count = 1
return r, true
default:
return
}
}
elem = origElem
}
elem = origElem
}
elem = origElem elem = origElem
} }

View File

@ -10,29 +10,29 @@ func (s *ErrorStatusCode) Error() string {
return fmt.Sprintf("code %d: %+v", s.StatusCode, s.Response) return fmt.Sprintf("code %d: %+v", s.StatusCode, s.Response)
} }
// ActionSubmissionPublishOK is response for ActionSubmissionPublish operation. // ActionSubmissionPublishNoContent is response for ActionSubmissionPublish operation.
type ActionSubmissionPublishOK struct{} type ActionSubmissionPublishNoContent struct{}
// ActionSubmissionRejectOK is response for ActionSubmissionReject operation. // ActionSubmissionRejectNoContent is response for ActionSubmissionReject operation.
type ActionSubmissionRejectOK struct{} type ActionSubmissionRejectNoContent struct{}
// ActionSubmissionRequestChangesOK is response for ActionSubmissionRequestChanges operation. // ActionSubmissionRequestChangesNoContent is response for ActionSubmissionRequestChanges operation.
type ActionSubmissionRequestChangesOK struct{} type ActionSubmissionRequestChangesNoContent struct{}
// ActionSubmissionRevokeOK is response for ActionSubmissionRevoke operation. // ActionSubmissionRevokeNoContent is response for ActionSubmissionRevoke operation.
type ActionSubmissionRevokeOK struct{} type ActionSubmissionRevokeNoContent struct{}
// ActionSubmissionSubmitOK is response for ActionSubmissionSubmit operation. // ActionSubmissionSubmitNoContent is response for ActionSubmissionSubmit operation.
type ActionSubmissionSubmitOK struct{} type ActionSubmissionSubmitNoContent struct{}
// ActionSubmissionTriggerPublishOK is response for ActionSubmissionTriggerPublish operation. // ActionSubmissionTriggerPublishNoContent is response for ActionSubmissionTriggerPublish operation.
type ActionSubmissionTriggerPublishOK struct{} type ActionSubmissionTriggerPublishNoContent struct{}
// ActionSubmissionTriggerValidateOK is response for ActionSubmissionTriggerValidate operation. // ActionSubmissionTriggerValidateNoContent is response for ActionSubmissionTriggerValidate operation.
type ActionSubmissionTriggerValidateOK struct{} type ActionSubmissionTriggerValidateNoContent struct{}
// ActionSubmissionValidateOK is response for ActionSubmissionValidate operation. // ActionSubmissionValidateNoContent is response for ActionSubmissionValidate operation.
type ActionSubmissionValidateOK struct{} type ActionSubmissionValidateNoContent struct{}
type CookieAuth struct { type CookieAuth struct {
APIKey string APIKey string
@ -48,6 +48,12 @@ func (s *CookieAuth) SetAPIKey(val string) {
s.APIKey = val s.APIKey = val
} }
// DeleteScriptNoContent is response for DeleteScript operation.
type DeleteScriptNoContent struct{}
// DeleteScriptPolicyNoContent is response for DeleteScriptPolicy operation.
type DeleteScriptPolicyNoContent struct{}
// Represents error object. // Represents error object.
// Ref: #/components/schemas/Error // Ref: #/components/schemas/Error
type Error struct { type Error struct {
@ -103,65 +109,19 @@ func (s *ErrorStatusCode) SetResponse(val Error) {
// Ref: #/components/schemas/Id // Ref: #/components/schemas/Id
type ID struct { type ID struct {
ID OptInt64 `json:"ID"` ID int64 `json:"ID"`
} }
// GetID returns the value of ID. // GetID returns the value of ID.
func (s *ID) GetID() OptInt64 { func (s *ID) GetID() int64 {
return s.ID return s.ID
} }
// SetID sets the value of ID. // SetID sets the value of ID.
func (s *ID) SetID(val OptInt64) { func (s *ID) SetID(val int64) {
s.ID = val 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. // NewOptInt32 returns new OptInt32 with value set to v.
func NewOptInt32(v int32) OptInt32 { func NewOptInt32(v int32) OptInt32 {
return OptInt32{ return OptInt32{
@ -300,52 +260,6 @@ func (o OptString) Or(d string) string {
return d 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. // NewOptSubmissionFilter returns new OptSubmissionFilter with value set to v.
func NewOptSubmissionFilter(v SubmissionFilter) OptSubmissionFilter { func NewOptSubmissionFilter(v SubmissionFilter) OptSubmissionFilter {
return OptSubmissionFilter{ return OptSubmissionFilter{
@ -418,75 +332,322 @@ func (s *Pagination) SetLimit(val int32) {
s.Limit = val s.Limit = val
} }
// PatchSubmissionCompletedOK is response for PatchSubmissionCompleted operation. // Ref: #/components/schemas/Script
type PatchSubmissionCompletedOK struct{} type Script struct {
ID int64 `json:"ID"`
// PatchSubmissionModelOK is response for PatchSubmissionModel operation. Hash string `json:"Hash"`
type PatchSubmissionModelOK struct{} Source string `json:"Source"`
SubmissionID int64 `json:"SubmissionID"`
// Ref: #/components/schemas/Submission
type Submission struct {
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. // GetID returns the value of ID.
func (s *Submission) GetID() OptInt64 { 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{}
// 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"`
}
// GetID returns the value of ID.
func (s *Submission) GetID() int64 {
return s.ID return s.ID
} }
// GetDisplayName returns the value of DisplayName. // GetDisplayName returns the value of DisplayName.
func (s *Submission) GetDisplayName() OptString { func (s *Submission) GetDisplayName() string {
return s.DisplayName return s.DisplayName
} }
// GetCreator returns the value of Creator. // GetCreator returns the value of Creator.
func (s *Submission) GetCreator() OptString { func (s *Submission) GetCreator() string {
return s.Creator return s.Creator
} }
// GetGameID returns the value of GameID. // GetGameID returns the value of GameID.
func (s *Submission) GetGameID() OptInt32 { func (s *Submission) GetGameID() int32 {
return s.GameID return s.GameID
} }
// GetDate returns the value of Date. // GetCreatedAt returns the value of CreatedAt.
func (s *Submission) GetDate() OptInt64 { func (s *Submission) GetCreatedAt() int64 {
return s.Date return s.CreatedAt
}
// GetUpdatedAt returns the value of UpdatedAt.
func (s *Submission) GetUpdatedAt() int64 {
return s.UpdatedAt
} }
// GetSubmitter returns the value of Submitter. // GetSubmitter returns the value of Submitter.
func (s *Submission) GetSubmitter() OptInt64 { func (s *Submission) GetSubmitter() int64 {
return s.Submitter return s.Submitter
} }
// GetAssetID returns the value of AssetID. // GetAssetID returns the value of AssetID.
func (s *Submission) GetAssetID() OptInt64 { func (s *Submission) GetAssetID() int64 {
return s.AssetID return s.AssetID
} }
// GetAssetVersion returns the value of AssetVersion. // GetAssetVersion returns the value of AssetVersion.
func (s *Submission) GetAssetVersion() OptInt64 { func (s *Submission) GetAssetVersion() int64 {
return s.AssetVersion return s.AssetVersion
} }
// GetCompleted returns the value of Completed. // GetCompleted returns the value of Completed.
func (s *Submission) GetCompleted() OptBool { func (s *Submission) GetCompleted() bool {
return s.Completed return s.Completed
} }
// GetSubmissionType returns the value of SubmissionType. // GetSubmissionType returns the value of SubmissionType.
func (s *Submission) GetSubmissionType() OptInt32 { func (s *Submission) GetSubmissionType() int32 {
return s.SubmissionType return s.SubmissionType
} }
@ -496,57 +657,62 @@ func (s *Submission) GetTargetAssetID() OptInt64 {
} }
// GetStatusID returns the value of StatusID. // GetStatusID returns the value of StatusID.
func (s *Submission) GetStatusID() OptInt32 { func (s *Submission) GetStatusID() int32 {
return s.StatusID return s.StatusID
} }
// SetID sets the value of ID. // SetID sets the value of ID.
func (s *Submission) SetID(val OptInt64) { func (s *Submission) SetID(val int64) {
s.ID = val s.ID = val
} }
// SetDisplayName sets the value of DisplayName. // SetDisplayName sets the value of DisplayName.
func (s *Submission) SetDisplayName(val OptString) { func (s *Submission) SetDisplayName(val string) {
s.DisplayName = val s.DisplayName = val
} }
// SetCreator sets the value of Creator. // SetCreator sets the value of Creator.
func (s *Submission) SetCreator(val OptString) { func (s *Submission) SetCreator(val string) {
s.Creator = val s.Creator = val
} }
// SetGameID sets the value of GameID. // SetGameID sets the value of GameID.
func (s *Submission) SetGameID(val OptInt32) { func (s *Submission) SetGameID(val int32) {
s.GameID = val s.GameID = val
} }
// SetDate sets the value of Date. // SetCreatedAt sets the value of CreatedAt.
func (s *Submission) SetDate(val OptInt64) { func (s *Submission) SetCreatedAt(val int64) {
s.Date = val s.CreatedAt = val
}
// SetUpdatedAt sets the value of UpdatedAt.
func (s *Submission) SetUpdatedAt(val int64) {
s.UpdatedAt = val
} }
// SetSubmitter sets the value of Submitter. // SetSubmitter sets the value of Submitter.
func (s *Submission) SetSubmitter(val OptInt64) { func (s *Submission) SetSubmitter(val int64) {
s.Submitter = val s.Submitter = val
} }
// SetAssetID sets the value of AssetID. // SetAssetID sets the value of AssetID.
func (s *Submission) SetAssetID(val OptInt64) { func (s *Submission) SetAssetID(val int64) {
s.AssetID = val s.AssetID = val
} }
// SetAssetVersion sets the value of AssetVersion. // SetAssetVersion sets the value of AssetVersion.
func (s *Submission) SetAssetVersion(val OptInt64) { func (s *Submission) SetAssetVersion(val int64) {
s.AssetVersion = val s.AssetVersion = val
} }
// SetCompleted sets the value of Completed. // SetCompleted sets the value of Completed.
func (s *Submission) SetCompleted(val OptBool) { func (s *Submission) SetCompleted(val bool) {
s.Completed = val s.Completed = val
} }
// SetSubmissionType sets the value of SubmissionType. // SetSubmissionType sets the value of SubmissionType.
func (s *Submission) SetSubmissionType(val OptInt32) { func (s *Submission) SetSubmissionType(val int32) {
s.SubmissionType = val s.SubmissionType = val
} }
@ -556,97 +722,75 @@ func (s *Submission) SetTargetAssetID(val OptInt64) {
} }
// SetStatusID sets the value of StatusID. // SetStatusID sets the value of StatusID.
func (s *Submission) SetStatusID(val OptInt32) { func (s *Submission) SetStatusID(val int32) {
s.StatusID = val s.StatusID = val
} }
// Ref: #/components/schemas/SubmissionCreate // Ref: #/components/schemas/SubmissionCreate
type SubmissionCreate struct { type SubmissionCreate struct {
DisplayName OptString `json:"DisplayName"` DisplayName string `json:"DisplayName"`
Creator OptString `json:"Creator"` Creator string `json:"Creator"`
GameID OptInt32 `json:"GameID"` GameID int32 `json:"GameID"`
Submitter OptInt64 `json:"Submitter"` AssetID int64 `json:"AssetID"`
AssetID OptInt64 `json:"AssetID"` AssetVersion int64 `json:"AssetVersion"`
AssetVersion OptInt64 `json:"AssetVersion"`
SubmissionType OptInt32 `json:"SubmissionType"`
TargetAssetID OptInt64 `json:"TargetAssetID"` TargetAssetID OptInt64 `json:"TargetAssetID"`
} }
// GetDisplayName returns the value of DisplayName. // GetDisplayName returns the value of DisplayName.
func (s *SubmissionCreate) GetDisplayName() OptString { func (s *SubmissionCreate) GetDisplayName() string {
return s.DisplayName return s.DisplayName
} }
// GetCreator returns the value of Creator. // GetCreator returns the value of Creator.
func (s *SubmissionCreate) GetCreator() OptString { func (s *SubmissionCreate) GetCreator() string {
return s.Creator return s.Creator
} }
// GetGameID returns the value of GameID. // GetGameID returns the value of GameID.
func (s *SubmissionCreate) GetGameID() OptInt32 { func (s *SubmissionCreate) GetGameID() int32 {
return s.GameID return s.GameID
} }
// GetSubmitter returns the value of Submitter.
func (s *SubmissionCreate) GetSubmitter() OptInt64 {
return s.Submitter
}
// GetAssetID returns the value of AssetID. // GetAssetID returns the value of AssetID.
func (s *SubmissionCreate) GetAssetID() OptInt64 { func (s *SubmissionCreate) GetAssetID() int64 {
return s.AssetID return s.AssetID
} }
// GetAssetVersion returns the value of AssetVersion. // GetAssetVersion returns the value of AssetVersion.
func (s *SubmissionCreate) GetAssetVersion() OptInt64 { func (s *SubmissionCreate) GetAssetVersion() int64 {
return s.AssetVersion return s.AssetVersion
} }
// GetSubmissionType returns the value of SubmissionType.
func (s *SubmissionCreate) GetSubmissionType() OptInt32 {
return s.SubmissionType
}
// GetTargetAssetID returns the value of TargetAssetID. // GetTargetAssetID returns the value of TargetAssetID.
func (s *SubmissionCreate) GetTargetAssetID() OptInt64 { func (s *SubmissionCreate) GetTargetAssetID() OptInt64 {
return s.TargetAssetID return s.TargetAssetID
} }
// SetDisplayName sets the value of DisplayName. // SetDisplayName sets the value of DisplayName.
func (s *SubmissionCreate) SetDisplayName(val OptString) { func (s *SubmissionCreate) SetDisplayName(val string) {
s.DisplayName = val s.DisplayName = val
} }
// SetCreator sets the value of Creator. // SetCreator sets the value of Creator.
func (s *SubmissionCreate) SetCreator(val OptString) { func (s *SubmissionCreate) SetCreator(val string) {
s.Creator = val s.Creator = val
} }
// SetGameID sets the value of GameID. // SetGameID sets the value of GameID.
func (s *SubmissionCreate) SetGameID(val OptInt32) { func (s *SubmissionCreate) SetGameID(val int32) {
s.GameID = val s.GameID = val
} }
// SetSubmitter sets the value of Submitter.
func (s *SubmissionCreate) SetSubmitter(val OptInt64) {
s.Submitter = val
}
// SetAssetID sets the value of AssetID. // SetAssetID sets the value of AssetID.
func (s *SubmissionCreate) SetAssetID(val OptInt64) { func (s *SubmissionCreate) SetAssetID(val int64) {
s.AssetID = val s.AssetID = val
} }
// SetAssetVersion sets the value of AssetVersion. // SetAssetVersion sets the value of AssetVersion.
func (s *SubmissionCreate) SetAssetVersion(val OptInt64) { func (s *SubmissionCreate) SetAssetVersion(val int64) {
s.AssetVersion = val s.AssetVersion = val
} }
// SetSubmissionType sets the value of SubmissionType.
func (s *SubmissionCreate) SetSubmissionType(val OptInt32) {
s.SubmissionType = val
}
// SetTargetAssetID sets the value of TargetAssetID. // SetTargetAssetID sets the value of TargetAssetID.
func (s *SubmissionCreate) SetTargetAssetID(val OptInt64) { func (s *SubmissionCreate) SetTargetAssetID(val OptInt64) {
s.TargetAssetID = val s.TargetAssetID = val
@ -654,15 +798,14 @@ func (s *SubmissionCreate) SetTargetAssetID(val OptInt64) {
// Ref: #/components/schemas/SubmissionFilter // Ref: #/components/schemas/SubmissionFilter
type SubmissionFilter struct { type SubmissionFilter struct {
ID OptInt64 `json:"ID"` ID int64 `json:"ID"`
DisplayName OptString `json:"DisplayName"` DisplayName OptString `json:"DisplayName"`
Creator OptString `json:"Creator"` Creator OptString `json:"Creator"`
GameID OptInt32 `json:"GameID"` GameID OptInt32 `json:"GameID"`
Date OptInt64 `json:"Date"`
} }
// GetID returns the value of ID. // GetID returns the value of ID.
func (s *SubmissionFilter) GetID() OptInt64 { func (s *SubmissionFilter) GetID() int64 {
return s.ID return s.ID
} }
@ -681,13 +824,8 @@ func (s *SubmissionFilter) GetGameID() OptInt32 {
return s.GameID return s.GameID
} }
// GetDate returns the value of Date.
func (s *SubmissionFilter) GetDate() OptInt64 {
return s.Date
}
// SetID sets the value of ID. // SetID sets the value of ID.
func (s *SubmissionFilter) SetID(val OptInt64) { func (s *SubmissionFilter) SetID(val int64) {
s.ID = val s.ID = val
} }
@ -706,7 +844,11 @@ func (s *SubmissionFilter) SetGameID(val OptInt32) {
s.GameID = val s.GameID = val
} }
// SetDate sets the value of Date. // UpdateScriptNoContent is response for UpdateScript operation.
func (s *SubmissionFilter) SetDate(val OptInt64) { type UpdateScriptNoContent struct{}
s.Date = val
} // UpdateScriptPolicyNoContent is response for UpdateScriptPolicy operation.
type UpdateScriptPolicyNoContent struct{}
// UpdateSubmissionModelNoContent is response for UpdateSubmissionModel operation.
type UpdateSubmissionModelNoContent struct{}

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) { func (s *Server) securityCookieAuth(ctx context.Context, operationName OperationName, req *http.Request) (context.Context, bool, error) {
var t CookieAuth var t CookieAuth
const parameterName = "SESSIONID" const parameterName = "session_id"
var value string var value string
switch cookie, err := req.Cookie(parameterName); { switch cookie, err := req.Cookie(parameterName); {
case err == nil: // if NO error 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\"") return errors.Wrap(err, "security source \"CookieAuth\"")
} }
req.AddCookie(&http.Cookie{ req.AddCookie(&http.Cookie{
Name: "SESSIONID", Name: "session_id",
Value: t.APIKey, Value: t.APIKey,
}) })
return nil return nil

View File

@ -10,58 +10,100 @@ import (
type Handler interface { type Handler interface {
// ActionSubmissionPublish implements actionSubmissionPublish operation. // ActionSubmissionPublish implements actionSubmissionPublish operation.
// //
// Role Validator changes status from Publishing -> Published. // (Internal endpoint) Role Validator changes status from Publishing -> Published.
// //
// PATCH /submissions/{SubmissionID}/status/publish // POST /submissions/{SubmissionID}/status/validator-published
ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error
// ActionSubmissionReject implements actionSubmissionReject operation. // ActionSubmissionReject implements actionSubmissionReject operation.
// //
// Role Reviewer changes status from Submitted -> Rejected. // Role Reviewer changes status from Submitted -> Rejected.
// //
// PATCH /submissions/{SubmissionID}/status/reject // POST /submissions/{SubmissionID}/status/reject
ActionSubmissionReject(ctx context.Context, params ActionSubmissionRejectParams) error ActionSubmissionReject(ctx context.Context, params ActionSubmissionRejectParams) error
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation. // ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
// //
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested. // Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
// //
// PATCH /submissions/{SubmissionID}/status/request-changes // POST /submissions/{SubmissionID}/status/request-changes
ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error
// ActionSubmissionRevoke implements actionSubmissionRevoke operation. // ActionSubmissionRevoke implements actionSubmissionRevoke operation.
// //
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction. // Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
// //
// PATCH /submissions/{SubmissionID}/status/revoke // POST /submissions/{SubmissionID}/status/revoke
ActionSubmissionRevoke(ctx context.Context, params ActionSubmissionRevokeParams) error ActionSubmissionRevoke(ctx context.Context, params ActionSubmissionRevokeParams) error
// ActionSubmissionSubmit implements actionSubmissionSubmit operation. // ActionSubmissionSubmit implements actionSubmissionSubmit operation.
// //
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. // Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
// //
// PATCH /submissions/{SubmissionID}/status/submit // POST /submissions/{SubmissionID}/status/submit
ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error
// ActionSubmissionTriggerPublish implements actionSubmissionTriggerPublish operation. // ActionSubmissionTriggerPublish implements actionSubmissionTriggerPublish operation.
// //
// Role Admin changes status from Validated -> Publishing. // Role Admin changes status from Validated -> Publishing.
// //
// PATCH /submissions/{SubmissionID}/status/trigger-publish // POST /submissions/{SubmissionID}/status/trigger-publish
ActionSubmissionTriggerPublish(ctx context.Context, params ActionSubmissionTriggerPublishParams) error ActionSubmissionTriggerPublish(ctx context.Context, params ActionSubmissionTriggerPublishParams) error
// ActionSubmissionTriggerValidate implements actionSubmissionTriggerValidate operation. // ActionSubmissionTriggerValidate implements actionSubmissionTriggerValidate operation.
// //
// Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating. // Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
// //
// PATCH /submissions/{SubmissionID}/status/trigger-validate // POST /submissions/{SubmissionID}/status/trigger-validate
ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error
// ActionSubmissionValidate implements actionSubmissionValidate operation. // ActionSubmissionValidate implements actionSubmissionValidate operation.
// //
// Role Validator changes status from Validating -> Validated. // (Internal endpoint) Role Validator changes status from Validating -> Validated.
// //
// PATCH /submissions/{SubmissionID}/status/validate // POST /submissions/{SubmissionID}/status/validator-validated
ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error 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. // CreateSubmission implements createSubmission operation.
// //
// Create new submission. // Create new submission.
// //
// POST /submissions // POST /submissions
CreateSubmission(ctx context.Context, req OptSubmissionCreate) (*ID, error) 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)
// GetSubmission implements getSubmission operation. // GetSubmission implements getSubmission operation.
// //
// Retrieve map with ID. // Retrieve map with ID.
@ -74,18 +116,30 @@ type Handler interface {
// //
// GET /submissions // GET /submissions
ListSubmissions(ctx context.Context, params ListSubmissionsParams) ([]Submission, error) ListSubmissions(ctx context.Context, params ListSubmissionsParams) ([]Submission, error)
// PatchSubmissionCompleted implements patchSubmissionCompleted operation. // SetSubmissionCompleted implements setSubmissionCompleted operation.
// //
// Retrieve map with ID. // Retrieve map with ID.
// //
// PATCH /submissions/{SubmissionID}/completed // POST /submissions/{SubmissionID}/completed
PatchSubmissionCompleted(ctx context.Context, params PatchSubmissionCompletedParams) error SetSubmissionCompleted(ctx context.Context, params SetSubmissionCompletedParams) error
// PatchSubmissionModel implements patchSubmissionModel operation. // 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.
// //
// Update model following role restrictions. // Update model following role restrictions.
// //
// PATCH /submissions/{SubmissionID}/model // POST /submissions/{SubmissionID}/model
PatchSubmissionModel(ctx context.Context, params PatchSubmissionModelParams) error UpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) error
// NewError creates *ErrorStatusCode from error returned by handler. // NewError creates *ErrorStatusCode from error returned by handler.
// //
// Used for common default response. // Used for common default response.

View File

@ -15,9 +15,9 @@ var _ Handler = UnimplementedHandler{}
// ActionSubmissionPublish implements actionSubmissionPublish operation. // ActionSubmissionPublish implements actionSubmissionPublish operation.
// //
// Role Validator changes status from Publishing -> Published. // (Internal endpoint) Role Validator changes status from Publishing -> Published.
// //
// PATCH /submissions/{SubmissionID}/status/publish // POST /submissions/{SubmissionID}/status/validator-published
func (UnimplementedHandler) ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error { func (UnimplementedHandler) ActionSubmissionPublish(ctx context.Context, params ActionSubmissionPublishParams) error {
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }
@ -26,7 +26,7 @@ func (UnimplementedHandler) ActionSubmissionPublish(ctx context.Context, params
// //
// Role Reviewer changes status from Submitted -> Rejected. // Role Reviewer changes status from Submitted -> Rejected.
// //
// PATCH /submissions/{SubmissionID}/status/reject // POST /submissions/{SubmissionID}/status/reject
func (UnimplementedHandler) ActionSubmissionReject(ctx context.Context, params ActionSubmissionRejectParams) error { func (UnimplementedHandler) ActionSubmissionReject(ctx context.Context, params ActionSubmissionRejectParams) error {
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }
@ -35,7 +35,7 @@ func (UnimplementedHandler) ActionSubmissionReject(ctx context.Context, params A
// //
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested. // Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
// //
// PATCH /submissions/{SubmissionID}/status/request-changes // POST /submissions/{SubmissionID}/status/request-changes
func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error { func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error {
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }
@ -44,7 +44,7 @@ func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context,
// //
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction. // Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
// //
// PATCH /submissions/{SubmissionID}/status/revoke // POST /submissions/{SubmissionID}/status/revoke
func (UnimplementedHandler) ActionSubmissionRevoke(ctx context.Context, params ActionSubmissionRevokeParams) error { func (UnimplementedHandler) ActionSubmissionRevoke(ctx context.Context, params ActionSubmissionRevokeParams) error {
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }
@ -53,7 +53,7 @@ func (UnimplementedHandler) ActionSubmissionRevoke(ctx context.Context, params A
// //
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. // Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
// //
// PATCH /submissions/{SubmissionID}/status/submit // POST /submissions/{SubmissionID}/status/submit
func (UnimplementedHandler) ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error { func (UnimplementedHandler) ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error {
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }
@ -62,7 +62,7 @@ func (UnimplementedHandler) ActionSubmissionSubmit(ctx context.Context, params A
// //
// Role Admin changes status from Validated -> Publishing. // Role Admin changes status from Validated -> Publishing.
// //
// PATCH /submissions/{SubmissionID}/status/trigger-publish // POST /submissions/{SubmissionID}/status/trigger-publish
func (UnimplementedHandler) ActionSubmissionTriggerPublish(ctx context.Context, params ActionSubmissionTriggerPublishParams) error { func (UnimplementedHandler) ActionSubmissionTriggerPublish(ctx context.Context, params ActionSubmissionTriggerPublishParams) error {
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }
@ -71,26 +71,89 @@ func (UnimplementedHandler) ActionSubmissionTriggerPublish(ctx context.Context,
// //
// Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating. // Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
// //
// PATCH /submissions/{SubmissionID}/status/trigger-validate // POST /submissions/{SubmissionID}/status/trigger-validate
func (UnimplementedHandler) ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error { func (UnimplementedHandler) ActionSubmissionTriggerValidate(ctx context.Context, params ActionSubmissionTriggerValidateParams) error {
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }
// ActionSubmissionValidate implements actionSubmissionValidate operation. // ActionSubmissionValidate implements actionSubmissionValidate operation.
// //
// Role Validator changes status from Validating -> Validated. // (Internal endpoint) Role Validator changes status from Validating -> Validated.
// //
// PATCH /submissions/{SubmissionID}/status/validate // POST /submissions/{SubmissionID}/status/validator-validated
func (UnimplementedHandler) ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error { func (UnimplementedHandler) ActionSubmissionValidate(ctx context.Context, params ActionSubmissionValidateParams) error {
return ht.ErrNotImplemented 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. // CreateSubmission implements createSubmission operation.
// //
// Create new submission. // Create new submission.
// //
// POST /submissions // POST /submissions
func (UnimplementedHandler) CreateSubmission(ctx context.Context, req OptSubmissionCreate) (r *ID, _ error) { 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) {
return r, ht.ErrNotImplemented return r, ht.ErrNotImplemented
} }
@ -112,21 +175,39 @@ func (UnimplementedHandler) ListSubmissions(ctx context.Context, params ListSubm
return r, ht.ErrNotImplemented return r, ht.ErrNotImplemented
} }
// PatchSubmissionCompleted implements patchSubmissionCompleted operation. // SetSubmissionCompleted implements setSubmissionCompleted operation.
// //
// Retrieve map with ID. // Retrieve map with ID.
// //
// PATCH /submissions/{SubmissionID}/completed // POST /submissions/{SubmissionID}/completed
func (UnimplementedHandler) PatchSubmissionCompleted(ctx context.Context, params PatchSubmissionCompletedParams) error { func (UnimplementedHandler) SetSubmissionCompleted(ctx context.Context, params SetSubmissionCompletedParams) error {
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }
// PatchSubmissionModel implements patchSubmissionModel operation. // 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.
// //
// Update model following role restrictions. // Update model following role restrictions.
// //
// PATCH /submissions/{SubmissionID}/model // POST /submissions/{SubmissionID}/model
func (UnimplementedHandler) PatchSubmissionModel(ctx context.Context, params PatchSubmissionModelParams) error { func (UnimplementedHandler) UpdateSubmissionModel(ctx context.Context, params UpdateSubmissionModelParams) error {
return ht.ErrNotImplemented return ht.ErrNotImplemented
} }

View File

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

View File

@ -59,3 +59,317 @@ func (s *Pagination) Validate() error {
} }
return nil 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,14 +2,16 @@ package cmds
import ( import (
"fmt" "fmt"
"git.itzana.me/strafesnet/go-grpc/auth"
"git.itzana.me/strafesnet/maps-service/pkg/api" "git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore" "git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore"
"git.itzana.me/strafesnet/maps-service/pkg/service" "git.itzana.me/strafesnet/maps-service/pkg/service"
"github.com/nats-io/nats.go"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"net/http"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"net/http"
) )
func NewServeCommand() *cli.Command { func NewServeCommand() *cli.Command {
@ -60,6 +62,18 @@ func NewServeCommand() *cli.Command {
Value: 8080, Value: 8080,
EnvVars: []string{"PORT"}, 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",
},
}, },
} }
} }
@ -69,16 +83,35 @@ func serve(ctx *cli.Context) error {
if err != nil { if err != nil {
log.WithError(err).Fatal("failed to connect database") log.WithError(err).Fatal("failed to connect database")
} }
svc := &service.Service{ nc, err := nats.Connect(ctx.String("nats-host"))
DB: db, 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")
} }
conn, err := grpc.Dial("auth-service:8090", grpc.WithTransportCredentials(insecure.NewCredentials())) _, 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,
}
conn, err := grpc.Dial(ctx.String("auth-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
sec := service.SecurityHandler{ sec := service.SecurityHandler{
Client: conn, Client: auth.NewAuthServiceClient(conn),
} }
srv, err := api.NewServer(svc, sec, api.WithPathPrefix("/v1")) srv, err := api.NewServer(svc, sec, api.WithPathPrefix("/v1"))

View File

@ -8,10 +8,13 @@ import (
var ( var (
ErrNotExist = errors.New("resource does not exist") ErrNotExist = errors.New("resource does not exist")
ErroNoRowsAffected = errors.New("query did not affect any rows")
) )
type Datastore interface { type Datastore interface {
Submissions() Submissions Submissions() Submissions
Scripts() Scripts
ScriptPolicy() ScriptPolicy
} }
type Submissions interface { type Submissions interface {
@ -20,6 +23,22 @@ type Submissions interface {
Create(ctx context.Context, smap model.Submission) (model.Submission, error) Create(ctx context.Context, smap model.Submission) (model.Submission, error)
Update(ctx context.Context, id int64, values OptionalMap) error Update(ctx context.Context, id int64, values OptionalMap) error
IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, 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 Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page) ([]model.Submission, error) List(ctx context.Context, filters OptionalMap, page model.Page) ([]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,7 +30,11 @@ func New(ctx *cli.Context) (datastore.Datastore, error) {
} }
if ctx.Bool("migrate") { if ctx.Bool("migrate") {
if err := db.AutoMigrate(&model.Submission{}); err != nil { if err := db.AutoMigrate(
&model.Submission{},
&model.Script{},
&model.ScriptPolicy{},
); err != nil {
log.WithError(err).Errorln("database migration failed") log.WithError(err).Errorln("database migration failed")
return nil, err return nil, err
} }

View File

@ -12,3 +12,11 @@ type Gormstore struct {
func (g Gormstore) Submissions() datastore.Submissions { func (g Gormstore) Submissions() datastore.Submissions {
return &Submissions{db: g.db} 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

@ -0,0 +1,64 @@
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

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

32
pkg/model/nats.go Normal file
View File

@ -0,0 +1,32 @@
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
}

27
pkg/model/policy.go Normal file
View File

@ -0,0 +1,27 @@
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
}

12
pkg/model/script.go Normal file
View File

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

View File

@ -0,0 +1,156 @@
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)
}

111
pkg/service/scripts.go Normal file
View File

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

View File

@ -2,20 +2,40 @@ package service
import ( import (
"context" "context"
"errors"
"git.itzana.me/strafesnet/maps-service/pkg/api" "git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore" "git.itzana.me/strafesnet/maps-service/pkg/datastore"
"github.com/nats-io/nats.go"
)
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 { type Service struct {
DB datastore.Datastore DB datastore.Datastore
Nats nats.JetStreamContext
} }
// NewError creates *ErrorStatusCode from error returned by handler. // NewError creates *ErrorStatusCode from error returned by handler.
// //
// Used for common default response. // Used for common default response.
func (svc *Service) NewError(ctx context.Context, err error) *api.ErrorStatusCode { 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{ return &api.ErrorStatusCode{
StatusCode: 500, StatusCode: status,
Response: api.Error{Message: err.Error()}, Response: api.Error{
Code: int64(status),
Message: err.Error(),
},
} }
} }

View File

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

View File

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

1115
validation/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

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

24
validation/Containerfile Normal file
View File

@ -0,0 +1,24 @@
# 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"]

17
validation/api/Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[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

@ -0,0 +1,176 @@
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

@ -0,0 +1,23 @@
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.

130
validation/api/src/lib.rs Normal file
View File

@ -0,0 +1,130 @@
#[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,12 +1,20 @@
use futures::StreamExt;
mod nats_types; mod nats_types;
mod publisher;
mod validator; mod validator;
mod publish_new;
mod publish_fix;
mod message_handler;
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
enum StartupError{ pub enum StartupError{
Connect(async_nats::ConnectError), API(api::ReqwestError),
Subscribe(async_nats::SubscribeError), 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),
} }
impl std::fmt::Display for StartupError{ impl std::fmt::Display for StartupError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@ -15,26 +23,76 @@ impl std::fmt::Display for StartupError{
} }
impl std::error::Error 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] #[tokio::main]
async fn main()->Result<(),StartupError>{ async fn main()->Result<(),StartupError>{
// cookies and clouds // talk to roblox through STRAFESNET_CI2 account
let cookie_context=rbx_asset::cookie::CookieContext::new(rbx_asset::cookie::Cookie::new("".to_owned())); let cookie=std::env::var("RBXCOOKIE").expect("RBXCOOKIE env required");
let cloud_context=rbx_asset::cloud::CloudContext::new(rbx_asset::cloud::ApiKey::new("".to_owned())); 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)?;
// nats // nats
let nasty=async_nats::connect("nats").await.map_err(StartupError::Connect)?; 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)
};
// connect to nats // data-service grpc for creating map entries
let (publisher,validator)=tokio::try_join!( let data_host=std::env::var("DATA_HOST").expect("DATA_HOST env required");
publisher::Publisher::new(nasty.clone(),cookie_context.clone(),cloud_context), let message_handler_fut=async{
validator::Validator::new(nasty,cookie_context) let maps_grpc=crate::MapsServiceClient::connect(data_host).await.map_err(StartupError::GRPCConnect)?;
).map_err(StartupError::Subscribe)?; Ok(message_handler::MessageHandler::new(cookie_context,api,maps_grpc))
};
// publisher thread // Create a signal listener for SIGTERM
tokio::spawn(publisher.run()); let mut sig_term=tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()).expect("Failed to create SIGTERM signal listener");
// run validator on the main thread indefinitely // run futures
validator.run().await; 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=>(),
};
Ok(()) Ok(())
} }

View File

@ -0,0 +1,48 @@
#[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,46 +2,36 @@
// to perform the operation, not necessarily the over-the-wire format // to perform the operation, not necessarily the over-the-wire format
// Requests are sent from maps-service to validator // Requests are sent from maps-service to validator
// Responses are sent from validator to maps-service // Validation invokes the REST api to update the submissions
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct ValidateRequest{ pub struct ValidateRequest{
// submission_id is passed back in the response message // submission_id is passed back in the response message
pub submission_id:u64, pub SubmissionID:i64,
pub model_id:u64, pub ModelID:u64,
pub model_version:u64, pub ModelVersion:u64,
} pub ValidatedModelID:Option<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 // Create a new map
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct PublishNewRequest{ pub struct PublishNewRequest{
pub submission_id:u64, pub SubmissionID:i64,
pub model_id:u64, pub ModelID:u64,
pub model_version:u64, pub ModelVersion:u64,
pub creator:String, pub Creator:String,
pub display_name:String, pub DisplayName:String,
pub GameID:u32,
//games:HashSet<GameID>, //games:HashSet<GameID>,
} }
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct PublishFixRequest{ pub struct PublishFixRequest{
pub submission_id:u64, pub SubmissionID:i64,
pub model_id:u64, pub ModelID:u64,
pub model_version:u64, pub ModelVersion:u64,
pub target_asset_id:u64, pub TargetAssetID:u64,
}
pub struct PublishResponse{
pub submission_id:u64,
pub success:bool,
} }

View File

@ -0,0 +1,62 @@
use crate::nats_types::PublishFixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum PublishError{
Get(rbx_asset::cookie::GetError),
Json(serde_json::Error),
Upload(rbx_asset::cookie::UploadError),
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

@ -0,0 +1,83 @@
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

@ -1,40 +0,0 @@
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,22 +1,31 @@
use futures::StreamExt; use futures::TryStreamExt;
use crate::nats_types::ValidateRequest; use crate::nats_types::ValidateRequest;
struct ModelVersion{ const SCRIPT_CONCURRENCY:usize=16;
model_id:u64,
model_version:u64,
}
enum Valid{ enum Policy{
Untouched, Allowed,
Modified(ModelVersion), Blocked,
Delete,
Replace(String),
} }
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
enum ValidateError{ pub enum ValidateError{
Blocked,
NotAllowed,
Get(rbx_asset::cookie::GetError), Get(rbx_asset::cookie::GetError),
Json(serde_json::Error), Json(serde_json::Error),
ReadDom(ReadDomError), 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{ impl std::fmt::Display for ValidateError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@ -26,63 +35,152 @@ impl std::fmt::Display for ValidateError{
impl std::error::Error for ValidateError{} impl std::error::Error for ValidateError{}
pub struct Validator{ pub struct Validator{
nats:async_nats::Client,
subscriber:async_nats::Subscriber,
roblox_cookie:rbx_asset::cookie::CookieContext, roblox_cookie:rbx_asset::cookie::CookieContext,
api:api::Context,
} }
impl Validator{ impl Validator{
pub async fn new( pub const fn new(
nats:async_nats::Client,
roblox_cookie:rbx_asset::cookie::CookieContext, roblox_cookie:rbx_asset::cookie::CookieContext,
)->Result<Self,async_nats::SubscribeError>{ api:api::Context,
Ok(Self{ )->Self{
subscriber:nats.subscribe("validate").await?, Self{
nats,
roblox_cookie, roblox_cookie,
}) api,
}
pub async fn run(mut self){
while let Some(message)=self.subscriber.next().await{
self.validate_supress_error(message).await
} }
} }
async fn validate_supress_error(&self,message:async_nats::Message){ pub async fn validate(&self,message:async_nats::jetstream::Message)->Result<(),ValidateError>{
match self.validate(message).await{ println!("validate {:?}",message.message.payload);
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 // decode json
let validate_info:ValidateRequest=serde_json::from_slice(&message.payload).map_err(ValidateError::Json)?; let validate_info:ValidateRequest=serde_json::from_slice(&message.payload).map_err(ValidateError::Json)?;
// download map // download map
let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{ let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:validate_info.model_id, asset_id:validate_info.ModelID,
version:Some(validate_info.model_version), version:Some(validate_info.ModelVersion),
}).await.map_err(ValidateError::Get)?; }).await.map_err(ValidateError::Get)?;
// decode dom (slow!) // decode dom (slow!)
let mut dom=read_dom(&mut std::io::Cursor::new(data)).map_err(ValidateError::ReadDom)?; let mut dom=read_dom(&mut std::io::Cursor::new(data)).map_err(ValidateError::ReadDom)?;
// validate map /* VALIDATE MAP */
// validate(dom)
// reply with validity // collect unique scripts
Ok(Valid::Untouched) 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(())
} }
} }
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
enum ReadDomError{ pub enum ReadDomError{
Binary(rbx_binary::DecodeError), Binary(rbx_binary::DecodeError),
Xml(rbx_xml::DecodeError), Xml(rbx_xml::DecodeError),
Read(std::io::Error), Read(std::io::Error),
@ -111,3 +209,33 @@ fn read_dom<R:std::io::Read+std::io::Seek>(input:&mut R)->Result<rbx_dom_weak::W
_=>Err(ReadDomError::UnknownFormat(first_8)), _=>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
}

3
web/.dockerignore Normal file
View File

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

40
web/.gitignore vendored Normal file
View File

@ -0,0 +1,40 @@
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

13
web/Containerfile Normal file
View File

@ -0,0 +1,13 @@
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"]

16
web/eslint.config.mjs Normal file
View File

@ -0,0 +1,16 @@
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;

16
web/next.config.ts Normal file
View File

@ -0,0 +1,16 @@
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;

30
web/package.json Normal file
View File

@ -0,0 +1,30 @@
{
"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

@ -0,0 +1,31 @@
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

@ -0,0 +1,38 @@
.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

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

54
web/src/app/globals.scss Normal file
View File

@ -0,0 +1,54 @@
$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;
}

9
web/src/app/layout.tsx Normal file
View File

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

7
web/src/app/page.tsx Normal file
View File

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

View File

@ -0,0 +1,26 @@
@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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,73 @@
$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

@ -0,0 +1,66 @@
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

@ -0,0 +1,27 @@
"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

@ -0,0 +1,35 @@
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

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

View File

@ -0,0 +1,80 @@
"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>
)
}

48
web/src/app/ts/Roblox.ts Normal file
View File

@ -0,0 +1,48 @@
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

@ -0,0 +1,56 @@
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
}

42
web/tsconfig.json Normal file
View File

@ -0,0 +1,42 @@
{
"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"
]
}