82 Commits

Author SHA1 Message Date
49bee944d8 openapi: rethink integers
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-25 19:15:20 -07:00
f5d41ab672 openapi: extend Map with new fields 2025-07-25 19:15:12 -07:00
2578a74ddb submissions: use unsigned ints in maps struct
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-23 23:18:54 -07:00
45a58145ed Merge pull request 'Fix Null Pointer Deref' (#246) from maps into staging
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #246
2025-07-24 05:21:14 +00:00
a4cefd263d submissions: maps: guard against null pointers
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-23 22:19:42 -07:00
1b18649cb6 Merge pull request 'Implement Maps' (#241) from maps into staging
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #241
2025-07-24 04:29:01 +00:00
bdfd1a0b23 submissions: temporary migration endpoint
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-23 20:04:39 -07:00
859004f264 openapi: generate 2025-07-23 20:04:39 -07:00
89ef254b72 openapi: maps migration endpoint 2025-07-23 20:04:39 -07:00
36f9e2db5f submissions: grpc controller 2025-07-23 20:04:39 -07:00
ce4e37dc64 submissions: update go-grpc for maps_extended 2025-07-23 20:04:32 -07:00
f417111bcf submissions: switch maps to maps-service 2025-07-23 20:04:32 -07:00
1826a51ebd submissions: maps db 2025-07-23 20:03:55 -07:00
76903656c2 Merge pull request 'Change to Proxy Download' (#208) from pr1 into staging
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Reviewed-on: #208
2025-07-23 09:17:21 +00:00
3e353b2ec6 web: change to proxy download
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-23 02:10:37 -07:00
749ea7e57d submissions: change to proxy download 2025-07-23 02:10:37 -07:00
03ec0b0183 openapi: generate 2025-07-23 01:55:21 -07:00
cc93776c25 openapi: change to proxy download 2025-07-23 01:55:21 -07:00
a8ad9f7de0 ai thinking Rust while doing golang lol 2025-07-23 01:55:21 -07:00
d5d794703b submissions: missing inner service AuditEvent functions
All checks were successful
continuous-integration/drone/push Build is passing
currently unused
2025-07-23 01:54:04 -07:00
70bbba6003 submissions: fix gRPC (the code is completely wrong)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
I blame the programming language, wouldn't have happened in Rust
2025-07-22 21:42:14 -07:00
39ba12edd9 web: add missing ResetSubmitting review button
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-22 21:30:33 -07:00
1cfdb3668a validator: connect_lazy
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-22 21:01:39 -07:00
740e6368b1 Merge pull request 'Convert Internal API to gRPC' (#238) from grpc into staging
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Reviewed-on: #238
2025-07-22 02:25:12 +00:00
746ac8e8a7 validator: convert to gRPC
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-19 04:07:28 -07:00
bdfe16ed39 validator: extend grpc 2025-07-19 04:07:28 -07:00
3c21d8948a validator: grpc 2025-07-19 04:07:28 -07:00
eaa8f704ea validator: add rust-grpc dep 2025-07-19 04:07:28 -07:00
d50ae0e664 submissions-api: remove internal api 2025-07-19 04:07:28 -07:00
f95d8b1665 validator: bump edition 2025-07-19 04:07:28 -07:00
524c56b6b5 submissions: convert validator backend to gRPC 2025-07-19 04:07:28 -07:00
e05f69ef7d submissions: delete internal api 2025-07-17 23:13:22 -07:00
a45fbb370c submissions: rename services
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-17 20:51:03 -07:00
27a646c006 openapi: fix up tags
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-17 19:28:02 -07:00
dde6f3ebdb submissions: rename services
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-17 19:07:59 -07:00
3d08b144b1 submissions: move Roles to model
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-16 20:27:04 -07:00
63b701eb72 submissions: rename services 2025-07-16 20:27:04 -07:00
07e08af5ed De-monolithificate Services (#236)
All checks were successful
continuous-integration/drone/push Build is passing
Closes #204.

The branch is called dedup but the patch adds code...

- Services both use an inner service that implements the underlying operations
- Struct fields are made private, preventing code cross-contamination
- Filter & Update structures are clearly defined, gaining clarity and type safety

Reviewed-on: #236
Co-authored-by: Rhys Lloyd <krakow20@gmail.com>
Co-committed-by: Rhys Lloyd <krakow20@gmail.com>
2025-07-17 03:18:01 +00:00
391a0fe6f9 Maps Data Model (#233)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Create a data model to be used for maps.

Reviewed-on: #233
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-07-15 04:37:20 +00:00
0e06d00c21 validation: check for unanchored parts (#230)
All checks were successful
continuous-integration/drone/push Build is passing
Unanchored parts are always invalid.  Make a check to detect them.

Reviewed-on: #230
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-07-08 00:07:27 +00:00
cd0bfbaeb2 validation: check: use &Instance instead of &str
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-07 16:33:17 -07:00
70cb80ab9b submissions: order audit events by id ascending
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-07-05 06:07:50 -07:00
6d0af22485 submissions-api: v0.8.2 derive Hash for ID newtypes
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-05 02:16:09 -07:00
c08fdddf36 submissions-api: add Hash,Eq,PartialEq to ID newtypes 2025-07-05 02:15:22 -07:00
727b823fef update rbx_asset
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-01 04:57:29 -07:00
efefaa5e62 submissions: check rows affected (#220)
All checks were successful
continuous-integration/drone/push Build is passing
Closes #218.

Reviewed-on: #220
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-07-01 11:37:29 +00:00
c6ce9b8f82 submissions: Check for duplicate mapfix (#219)
All checks were successful
continuous-integration/drone/push Build is passing
Mapfixes should check to see if you already have a mapfix for the target asset upon creation.

Reviewed-on: #219
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-07-01 11:31:06 +00:00
ca008f8fcf Update Roblox Api (#216)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Same api different language

Reviewed-on: #216
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-07-01 08:40:03 +00:00
e9affea859 Update Roblox Api + Update Deps (#215)
All checks were successful
continuous-integration/drone/push Build is passing
Roblox api changed, see 9f1bdd6a1f

Reviewed-on: #215
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-07-01 08:19:50 +00:00
a31a92f131 Merge pull request 'submissions: check role for map location' (#213) from pr2 into staging
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Reviewed-on: #213
2025-06-30 09:46:50 +00:00
a5187be8a6 submissions: check role for map location
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-06-30 02:42:19 -07:00
825b2aa91a Clickable titles and show active mapfix (#211)
All checks were successful
continuous-integration/drone/push Build is passing
Closes #144

Co-authored-by: ic3w0lf <bob@ic3.space>
Reviewed-on: #211
Reviewed-by: itzaname <itzaname@noreply@itzana.me>
Co-authored-by: ic3w0lf22 <ic3w0lf22@noreply@itzana.me>
Co-committed-by: ic3w0lf22 <ic3w0lf22@noreply@itzana.me>
2025-06-29 19:06:52 +00:00
6f9cd952d4 Taking care of some issues & QOL changes (#209)
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
Co-authored-by: ic3w0lf <bob@ic3.space>
Reviewed-on: #209
Reviewed-by: Quaternions <quaternions@noreply@itzana.me>
Co-authored-by: ic3w0lf22 <ic3w0lf22@noreply@itzana.me>
Co-committed-by: ic3w0lf22 <ic3w0lf22@noreply@itzana.me>
2025-06-28 07:44:59 +00:00
abb3cf3076 web: remove cursed ai code
All checks were successful
continuous-integration/drone/push Build is passing
2025-06-24 05:07:03 -07:00
40b0af0063 Revert "Validation: Make Assets Loadable on Maptest (#198)"
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
This reverts commit abd233ce65.
2025-06-23 23:34:58 -07:00
976adf2b66 Move Download Button Below Title (#206)
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
Senior itzaname envisioned the button existing elsewhere.

Reviewed-on: #206
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-24 06:05:50 +00:00
53cc4b9e9e Map Download Button (#201)
All checks were successful
continuous-integration/drone/push Build is passing
Closes #145.

All the backend should be implemented here, ~~I just don't know how to make a download button on the frontend.~~ I made a button, we'll see if it works.

- [x] ~~Add asset download api key to infra~~ this was never required

Reviewed-on: #201
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-24 05:09:51 +00:00
51f62f039b submissions-api: report script IDs not the whole thing
All checks were successful
continuous-integration/drone/push Build is passing
2025-06-18 03:55:03 -07:00
42cc783887 validation: fixups
All checks were successful
continuous-integration/drone/push Build is passing
2025-06-13 22:04:25 -07:00
ed7109270f Audit Event CheckList (#181)
Some checks failed
continuous-integration/drone/push Build is failing
Depends on #160, #196, #197.

Closes #147.

This introduces a new type of audit event: the CheckList.  This is a list of map checks that the validator performed.  The intention is to update the web interface to display  check marks for every check passed and  for every check failed, and also include the summary of why the check failed.  ~~The `Details` field would be the complete internal structure of the check in json, but I'm thinking it's unnecessary and should just be omitted.~~ The `Details` field has been removed.

```go
type Check struct {
	Name    string `json:"name"`
	Summary string `json:"summary"`
	Passed  bool   `json:"passed"`
}

type AuditEventDataCheckList struct {
	CheckList []Check `json:"check_list"`
}
```

This is created instead of the Error audit event when the validator requests changes, but the Error audit event can still be created for other purposes.

- [x] Make a proper error instead of hijacking a CheckList

Reviewed-on: #181
Reviewed-by: itzaname <itzaname@noreply@itzana.me>
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-14 02:33:19 +00:00
abd233ce65 Validation: Make Assets Loadable on Maptest (#198)
All checks were successful
continuous-integration/drone/push Build is passing
Closes #43.

This is a very bare bones implementation, but gets us started on https://git.itzana.me/StrafesNET/maps-service/milestone/3

This will break production as written!  A proper implementation requires a separate api key since the maptest places are to be hosted on a different group.

Edit: It will actually not break, because it is using cookie access. The staging cookie has permission to edit StrafesNET Maptest asset permissions via StrafesNET_CI3, while prod also has access via StrafesNET_CI2.  Both staging and prod versions of the website will add maptest asset access to the same places on StrafesNET Maptest.
Reviewed-on: #198
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-13 03:58:01 +00:00
215c39000b Replace bypass-submit with trigger-submit-unchecked (#199)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Bypassing the submit process means that the map revision is not updated.  Change the endpoint and include a flag to skip the map checks but update the revision.

Reviewed-on: #199
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-13 00:15:16 +00:00
c4d97b6537 Change Error to Explicit Endpoint (#197)
All checks were successful
continuous-integration/drone/push Build is passing
This changes the way that the internal api works.  The backend used to implicitly create an error for specifc endpoints, but now the validator explicitly creates the error itself.

Reviewed-on: #197
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-12 00:55:09 +00:00
0834400c05 Compartmentalize Monolith (#196)
All checks were successful
continuous-integration/drone/push Build is passing
This isn't the full job, notably Operations are still sprinkled about, and having some code sharing between `service` and `service_internal` would be nice, but that is sketchy without the explicitness of Rust's traits.

Reviewed-on: #196
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-12 00:19:56 +00:00
463d14d2b5 submissions-api: type all ids (#195)
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #195
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-11 05:11:14 +00:00
6a52166901 submissions-api: Add Releaser Endpoints (#194)
All checks were successful
continuous-integration/drone/push Build is passing
Also uses enums over ints for GameID.

Reviewed-on: #194
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-11 04:11:21 +00:00
d7c2ad3dde submissions: actually fix script names (#192)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Actually closes #165.

Reviewed-on: #192
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-10 04:15:27 +00:00
f54bf1dc34 submissions: fix script names (#191)
All checks were successful
continuous-integration/drone/push Build is passing
Closes #165.

Reviewed-on: #191
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-10 04:08:32 +00:00
644c04c133 First wave of QOL impovementss (#190)
All checks were successful
continuous-integration/drone/push Build is passing
- Updated all avatars/thumbnails to just 307 to the roblox cdn
- Moved data loading for submissions and mapfixes into a common hook
- Data will now auto refresh every 5 seconds if state Validating, Submitting, Uploading (ing Statuses) #102
- A loading icon will also show when on a "ing" status
- You don't have to be logged in to see the submissions/mapfixes
- Added text if there are no comments
- Hide comment box if not logged in

Reviewed-on: #190
Reviewed-by: Quaternions <quaternions@noreply@itzana.me>
Co-authored-by: itzaname <me@sliving.io>
Co-committed-by: itzaname <me@sliving.io>
2025-06-10 02:18:55 +00:00
8006e3efbc validation: refuse to validate if model has updates (#188)
All checks were successful
continuous-integration/drone/push Build is passing
Closes #187.

Reviewed-on: #188
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-09 03:04:33 +00:00
b60d2b6186 Submissions: Fix Comments (#184)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Closes #163.

Reviewed-on: #184
Reviewed-by: itzaname <itzaname@noreply@itzana.me>
Co-authored-by: Quaternions <krakow20@gmail.com>
Co-committed-by: Quaternions <krakow20@gmail.com>
2025-06-09 01:03:55 +00:00
8f2a0b53e4 Refactor remaining frontend pages (#183)
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #183
Reviewed-by: Quaternions <quaternions@noreply@itzana.me>
Co-authored-by: itzaname <me@sliving.io>
Co-committed-by: itzaname <me@sliving.io>
2025-06-09 00:33:27 +00:00
70dd8502f4 Merge pull request 'submissions: add missing audit even when requesting changes for a mapfix' (#180) from pr2 into staging
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #180
2025-06-08 05:49:28 +00:00
5b977289e7 Merge pull request 'validation: include more details in duplicates error' (#179) from dupes into staging
Some checks are pending
continuous-integration/drone/push Build is running
Reviewed-on: #179
2025-06-08 05:49:11 +00:00
7b3af95f3d Merge pull request 'Remove class_is_a' (#178) from pr1 into staging
Some checks are pending
continuous-integration/drone/push Build is running
Reviewed-on: #178
2025-06-08 05:48:55 +00:00
4d78a9b2c5 submissions: add missing audit even when requesting changes for a mapfix
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-06-07 22:42:39 -07:00
ec59a83379 validation: include more details in duplicates error
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-06-07 22:34:57 -07:00
54bf3f55a0 Rework submission/mapfix/maps list views (#173)
All checks were successful
continuous-integration/drone/push Build is passing
Refactored maps/landing/mapfix/submission and navbar

Reviewed-on: #173
Reviewed-by: Quaternions <quaternions@noreply@itzana.me>
Co-authored-by: itzaname <me@sliving.io>
Co-committed-by: itzaname <me@sliving.io>
2025-06-08 03:41:36 +00:00
14f404ffe3 Merge pull request 'Openapi: Document Enum Fields' (#177) from openapi-doc into staging
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #177
Reviewed-by: itzaname <itzaname@noreply@itzana.me>
2025-06-08 00:21:21 +00:00
0e1d2fe50a openapi: generate
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-06-07 17:09:51 -07:00
ada8c322da openapi: add descriptions to enum fields
All checks were successful
continuous-integration/drone/push Build is passing
2025-06-07 17:07:47 -07:00
84d2bfef20 remove class_is_a
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-06-07 16:29:06 -07:00
180 changed files with 13909 additions and 26170 deletions

435
Cargo.lock generated
View File

@@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "adler2"
version = "2.0.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "ahash"
@@ -54,6 +54,12 @@ dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "arrayref"
version = "0.3.9"
@@ -68,9 +74,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "async-nats"
version = "0.41.0"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cf0ae68ffe9ef362127a2223b42f57104edb20a50429f8c6e058912212884f7"
checksum = "08f6da6d49a956424ca4e28fe93656f790d748b469eaccbc7488fec545315180"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -102,6 +108,17 @@ dependencies = [
"url",
]
[[package]]
name = "async-trait"
version = "0.1.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
@@ -110,9 +127,54 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "axum"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5"
dependencies = [
"axum-core",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"itoa",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"sync_wrapper",
"tower",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum-core"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6"
dependencies = [
"bytes",
"futures-core",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"rustversion",
"sync_wrapper",
"tower-layer",
"tower-service",
]
[[package]]
name = "backtrace"
@@ -126,7 +188,7 @@ dependencies = [
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -183,9 +245,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.18.1"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "byteorder"
@@ -204,9 +266,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.26"
version = "1.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac"
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
dependencies = [
"jobserver",
"libc",
@@ -215,9 +277,9 @@ dependencies = [
[[package]]
name = "cfg-if"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "chrono"
@@ -386,6 +448,12 @@ dependencies = [
"subtle",
]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "encoding_rs"
version = "0.8.35"
@@ -403,12 +471,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.12"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [
"libc",
"windows-sys 0.59.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -570,7 +638,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasi 0.11.1+wasi-snapshot-preview1",
]
[[package]]
@@ -593,9 +661,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "h2"
version = "0.4.10"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5"
checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785"
dependencies = [
"atomic-waker",
"bytes",
@@ -612,9 +680,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.15.3"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
[[package]]
name = "heck"
@@ -662,6 +730,12 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.6.0"
@@ -675,6 +749,7 @@ dependencies = [
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"smallvec",
@@ -698,6 +773,19 @@ dependencies = [
"tower-service",
]
[[package]]
name = "hyper-timeout"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
dependencies = [
"hyper",
"hyper-util",
"pin-project-lite",
"tokio",
"tower-service",
]
[[package]]
name = "hyper-tls"
version = "0.6.0"
@@ -873,9 +961,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.9.0"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
dependencies = [
"equivalent",
"hashbrown",
@@ -897,6 +985,15 @@ dependencies = [
"serde",
]
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.15"
@@ -954,9 +1051,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.172"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "linux-raw-sys"
@@ -1018,18 +1115,25 @@ dependencies = [
"rbx_dom_weak",
"rbx_reflection_database",
"rbx_xml",
"rust-grpc",
"serde",
"serde_json",
"siphasher",
"submissions-api",
"tokio",
"tonic",
]
[[package]]
name = "memchr"
version = "2.7.4"
name = "matchit"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "mime"
@@ -1049,9 +1153,9 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.8.8"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
@@ -1063,7 +1167,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasi 0.11.1+wasi-snapshot-preview1",
"windows-sys 0.59.0",
]
@@ -1202,7 +1306,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -1315,23 +1419,56 @@ dependencies = [
[[package]]
name = "profiling"
version = "1.0.16"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
dependencies = [
"profiling-procmacros",
]
[[package]]
name = "profiling-procmacros"
version = "1.0.16"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "prost"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
dependencies = [
"bytes",
"prost-derive",
]
[[package]]
name = "prost-derive"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "prost-types"
version = "0.13.5-serde3"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "e42128b6e3a6655aa5f72ac65a33848a512eb9b23e98986adc4bbe6559ea88ce"
dependencies = [
"prost",
"serde",
]
[[package]]
name = "quote"
version = "1.0.40"
@@ -1343,9 +1480,9 @@ dependencies = [
[[package]]
name = "r-efi"
version = "5.2.0"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
@@ -1379,9 +1516,9 @@ dependencies = [
[[package]]
name = "rbx_asset"
version = "0.4.5"
version = "0.4.8"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "b448bf22f70748215c2a937158f83790bf3f4df81e2af8521a089bc821155360"
checksum = "8c067713a3201d0d2de3445ebecc8001952d914319863a9c7bc8f9212fdb1a17"
dependencies = [
"bytes",
"chrono",
@@ -1476,9 +1613,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.12"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
dependencies = [
"bitflags 2.9.1",
]
@@ -1514,9 +1651,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "reqwest"
version = "0.12.19"
version = "0.12.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119"
checksum = "4c8cea6b35bcceb099f30173754403d2eba0a5dc18cea3630fccd88251909288"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -1531,13 +1668,11 @@ dependencies = [
"hyper-rustls",
"hyper-tls",
"hyper-util",
"ipnet",
"js-sys",
"log",
"mime",
"mime_guess",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls-pki-types",
@@ -1592,11 +1727,23 @@ dependencies = [
"serde",
]
[[package]]
name = "rust-grpc"
version = "1.3.0"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "23dc6efbef932befc29c64b9484be3686c97217f6197a91c2028c2eeee022f46"
dependencies = [
"prost",
"prost-types",
"serde",
"tonic",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
[[package]]
name = "rustc_version"
@@ -1622,9 +1769,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.27"
version = "0.23.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643"
dependencies = [
"once_cell",
"ring",
@@ -1862,12 +2009,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]]
name = "slab"
version = "0.4.9"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
[[package]]
name = "smallvec"
@@ -1903,8 +2047,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "submissions-api"
version = "0.7.2"
version = "0.8.2"
dependencies = [
"chrono",
"reqwest",
"serde",
"serde_json",
@@ -1920,9 +2065,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.101"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
@@ -2092,6 +2237,17 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-stream"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.15"
@@ -2126,6 +2282,35 @@ dependencies = [
"webpki-roots 0.26.11",
]
[[package]]
name = "tonic"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9"
dependencies = [
"async-trait",
"axum",
"base64 0.22.1",
"bytes",
"h2",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-timeout",
"hyper-util",
"percent-encoding",
"pin-project",
"prost",
"socket2",
"tokio",
"tokio-stream",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower"
version = "0.5.2"
@@ -2134,11 +2319,15 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
dependencies = [
"futures-core",
"futures-util",
"indexmap",
"pin-project-lite",
"slab",
"sync_wrapper",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
@@ -2184,9 +2373,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
version = "0.1.29"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
dependencies = [
"proc-macro2",
"quote",
@@ -2210,11 +2399,10 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "tryhard"
version = "0.5.1"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9f0a709784e86923586cff0d872dba54cd2d2e116b3bc57587d15737cfce9d"
checksum = "9fe58ebd5edd976e0fe0f8a14d2a04b7c81ef153ea9a54eebc42e67c2c23b4e5"
dependencies = [
"futures",
"pin-project-lite",
"tokio",
]
@@ -2296,9 +2484,9 @@ dependencies = [
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasi"
@@ -2396,14 +2584,14 @@ version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
"webpki-roots 1.0.0",
"webpki-roots 1.0.1",
]
[[package]]
name = "webpki-roots"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb"
checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502"
dependencies = [
"rustls-pki-types",
]
@@ -2445,15 +2633,15 @@ dependencies = [
[[package]]
name = "windows-link"
version = "0.1.1"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-registry"
version = "0.5.2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820"
checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e"
dependencies = [
"windows-link",
"windows-result",
@@ -2484,7 +2672,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -2493,7 +2681,16 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.2",
]
[[package]]
@@ -2502,14 +2699,30 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
dependencies = [
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
"windows_i686_gnullvm 0.53.0",
"windows_i686_msvc 0.53.0",
"windows_x86_64_gnu 0.53.0",
"windows_x86_64_gnullvm 0.53.0",
"windows_x86_64_msvc 0.53.0",
]
[[package]]
@@ -2518,48 +2731,96 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
@@ -2607,18 +2868,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.25"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.25"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -33,6 +33,8 @@ services:
"--auth-rpc-host","authrpc:8081",
"--data-rpc-host","dataservice:9000",
]
env_file:
- ../auth-compose/strafesnet_staging.env
depends_on:
- authrpc
- nats

View File

@@ -1,4 +1,3 @@
package main
//go:generate go run github.com/ogen-go/ogen/cmd/ogen@latest --target pkg/api --clean openapi.yaml
//go:generate go run github.com/ogen-go/ogen/cmd/ogen@latest --target pkg/internal --clean openapi-internal.yaml

4
go.mod
View File

@@ -5,7 +5,7 @@ go 1.22
toolchain go1.23.3
require (
git.itzana.me/strafesnet/go-grpc v0.0.0-20241129081229-9e166b3d11f7
git.itzana.me/strafesnet/go-grpc v0.0.0-20250724030029-845bea991815
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
@@ -53,7 +53,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/segmentio/asm v1.2.0 // indirect
go.uber.org/multierr v1.11.0
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
golang.org/x/net v0.25.0 // indirect

4
go.sum
View File

@@ -1,7 +1,7 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
git.itzana.me/strafesnet/go-grpc v0.0.0-20241129081229-9e166b3d11f7 h1:5XzWd3ZZjSw1M60IfHuILty2vRPBYiqM0FZ+E7uHCi8=
git.itzana.me/strafesnet/go-grpc v0.0.0-20241129081229-9e166b3d11f7/go.mod h1:X7XTRUScRkBWq8q8bplbeso105RPDlnY7J6Wy1IwBMs=
git.itzana.me/strafesnet/go-grpc v0.0.0-20250724030029-845bea991815 h1:hkuOnehphRXUq/2z2UYgoqTq5MJj1GsWfshyc7bXda8=
git.itzana.me/strafesnet/go-grpc v0.0.0-20250724030029-845bea991815/go.mod h1:X7XTRUScRkBWq8q8bplbeso105RPDlnY7J6Wy1IwBMs=
git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9 h1:7lU6jyR7S7Rhh1dnUp7GyIRHUTBXZagw8F4n4hOyxLw=
git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9/go.mod h1:uyYerSieEt4v0MJCdPLppG0LtJ4Yj035vuTetWGsxjY=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

View File

@@ -1,862 +0,0 @@
openapi: 3.1.0
info:
title: StrafesNET Internal - OpenAPI 3.1
description: Internal operations inaccessible from the public internet.
version: 0.1.0
tags:
- name: Mapfixes
description: Mapfix operations
- name: Submissions
description: Submission operations
paths:
/mapfixes:
post:
summary: Create a mapfix
operationId: createMapfix
tags:
- Mapfixes
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MapfixCreate'
responses:
"201":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/MapfixID"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/validated-model:
post:
summary: Update validated model
operationId: updateMapfixValidatedModel
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: ValidatedModelID
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
- name: ValidatedModelVersion
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-submitted:
post:
summary: (Internal endpoint) Role Validator changes status from Submitting -> Submitted
operationId: actionMapfixSubmitted
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: ModelVersion
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
- name: DisplayName
in: query
required: true
schema:
type: string
maxLength: 128
- name: Creator
in: query
required: true
schema:
type: string
maxLength: 128
- name: GameID
in: query
required: true
schema:
type: integer
format: int32
minimum: 0
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-request-changes:
post:
summary: (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested
operationId: actionMapfixRequestChanges
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: ErrorMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-validated:
post:
summary: (Internal endpoint) Role Validator changes status from Validating -> Validated
operationId: actionMapfixValidated
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-failed:
post:
summary: (Internal endpoint) Role Validator changes status from Validating -> Accepted
operationId: actionMapfixAccepted
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: ErrorMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-uploaded:
post:
summary: (Internal endpoint) Role Validator changes status from Uploading -> Uploaded
operationId: actionMapfixUploaded
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/operations/{OperationID}/status/operation-failed:
post:
summary: (Internal endpoint) Fail an operation and write a StatusMessage
operationId: actionOperationFailed
tags:
- Operations
parameters:
- $ref: '#/components/parameters/OperationID'
- name: StatusMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions:
post:
summary: Create a new submission
operationId: createSubmission
tags:
- Submissions
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SubmissionCreate'
responses:
"201":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/SubmissionID"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/validated-model:
post:
summary: Update validated model
operationId: updateSubmissionValidatedModel
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: ValidatedModelID
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
- name: ValidatedModelVersion
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/validator-submitted:
post:
summary: (Internal endpoint) Role Validator changes status from Submitting -> Submitted
operationId: actionSubmissionSubmitted
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: ModelVersion
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
- name: DisplayName
in: query
required: true
schema:
type: string
maxLength: 128
- name: Creator
in: query
required: true
schema:
type: string
maxLength: 128
- name: GameID
in: query
required: true
schema:
type: integer
format: int32
minimum: 0
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/validator-request-changes:
post:
summary: (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested
operationId: actionSubmissionRequestChanges
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: ErrorMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/validator-validated:
post:
summary: (Internal endpoint) Role Validator changes status from Validating -> Validated
operationId: actionSubmissionValidated
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/validator-failed:
post:
summary: (Internal endpoint) Role Validator changes status from Validating -> Accepted
operationId: actionSubmissionAccepted
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: ErrorMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/validator-uploaded:
post:
summary: (Internal endpoint) Role Validator changes status from Uploading -> Uploaded
operationId: actionSubmissionUploaded
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: UploadedAssetID
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/script-policy:
get:
summary: Get list of script policies
operationId: listScriptPolicy
tags:
- ScriptPolicy
parameters:
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/Limit"
- name: FromScriptHash
in: query
schema:
type: string
minLength: 16
maxLength: 16
- name: ToScriptID
in: query
schema:
type: integer
format: int64
minimum: 0
- name: Policy
in: query
schema:
type: integer
format: int32
minimum: 0
responses:
"200":
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ScriptPolicy"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a new script policy
operationId: createScriptPolicy
tags:
- ScriptPolicy
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ScriptPolicyCreate'
responses:
"201":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/ScriptPolicyID"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/scripts:
get:
summary: Get list of scripts
operationId: listScripts
tags:
- Script
parameters:
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/Limit"
- name: Hash
in: query
schema:
type: string
minLength: 16
maxLength: 16
- name: Name
in: query
schema:
type: string
maxLength: 128
- name: Source
in: query
schema:
type: string
maxLength: 1048576
- name: ResourceType
in: query
schema:
type: integer
format: int32
minimum: 0
- name: ResourceID
in: query
schema:
type: integer
format: int64
minimum: 0
responses:
"200":
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Script"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a new script
operationId: createScript
tags:
- Scripts
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ScriptCreate'
responses:
"201":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/ScriptID"
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'
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"
components:
parameters:
MapfixID:
name: MapfixID
in: path
required: true
description: The unique identifier for a submission.
schema:
type: integer
format: int64
minimum: 0
OperationID:
name: OperationID
in: path
required: true
description: The unique identifier for a long-running operation.
schema:
type: integer
format: int32
minimum: 0
SubmissionID:
name: SubmissionID
in: path
required: true
description: The unique identifier for a submission.
schema:
type: integer
format: int64
minimum: 0
ScriptID:
name: ScriptID
in: path
required: true
description: The unique identifier for a script.
schema:
type: integer
format: int64
minimum: 0
Page:
name: Page
in: query
required: true
schema:
type: integer
format: int32
minimum: 1
Limit:
name: Limit
in: query
required: true
schema:
type: integer
format: int32
minimum: 1
maximum: 100
schemas:
MapfixID:
required:
- MapfixID
type: object
properties:
MapfixID:
type: integer
format: int64
minimum: 0
SubmissionID:
required:
- SubmissionID
type: object
properties:
SubmissionID:
type: integer
format: int64
minimum: 0
ScriptID:
required:
- ScriptID
type: object
properties:
ScriptID:
type: integer
format: int64
minimum: 0
ScriptPolicyID:
required:
- ScriptPolicyID
type: object
properties:
ScriptPolicyID:
type: integer
format: int64
minimum: 0
MapfixCreate:
required:
- OperationID
- AssetOwner
- DisplayName
- Creator
- GameID
- AssetID
- AssetVersion
- TargetAssetID
- Description
type: object
properties:
OperationID:
type: integer
format: int32
minimum: 0
AssetOwner:
type: integer
format: int64
minimum: 0
DisplayName:
type: string
maxLength: 128
Creator:
type: string
maxLength: 128
GameID:
type: integer
format: int32
minimum: 0
AssetID:
type: integer
format: int64
minimum: 0
AssetVersion:
type: integer
format: int64
minimum: 0
TargetAssetID:
type: integer
format: int64
minimum: 0
Description:
type: string
maxLength: 256
SubmissionCreate:
required:
- OperationID
- AssetOwner
- DisplayName
- Creator
- GameID
- AssetID
- AssetVersion
- Status
- Roles
type: object
properties:
OperationID:
type: integer
format: int32
minimum: 0
AssetOwner:
type: integer
format: int64
minimum: 0
DisplayName:
type: string
maxLength: 128
Creator:
type: string
maxLength: 128
GameID:
type: integer
format: int32
minimum: 0
AssetID:
type: integer
format: int64
minimum: 0
AssetVersion:
type: integer
format: int64
minimum: 0
Status:
type: integer
format: uint32
minimum: 0
maximum: 9
Roles:
type: integer
format: uint32
Script:
required:
- ID
- Name
- Hash
- Source
- ResourceType
- ResourceID
type: object
properties:
ID:
type: integer
format: int64
minimum: 0
Name:
type: string
maxLength: 128
Hash:
type: string
minLength: 16
maxLength: 16
Source:
type: string
maxLength: 1048576
ResourceType:
type: integer
format: int32
minimum: 0
ResourceID:
type: integer
format: int64
minimum: 0
ScriptCreate:
required:
- Name
- Source
- ResourceType
# - ResourceID
type: object
properties:
Name:
type: string
maxLength: 128
Source:
type: string
maxLength: 1048576
ResourceType:
type: integer
format: int32
minimum: 0
ResourceID:
type: integer
format: int64
minimum: 0
ScriptPolicy:
required:
- ID
- FromScriptHash
- ToScriptID
- Policy
type: object
properties:
ID:
type: integer
format: int64
minimum: 0
FromScriptHash:
type: string
minLength: 16
maxLength: 16
ToScriptID:
type: integer
format: int64
minimum: 0
Policy:
type: integer
format: int32
minimum: 0
ScriptPolicyCreate:
required:
- FromScriptID
- ToScriptID
- Policy
type: object
properties:
FromScriptID:
type: integer
format: int64
minimum: 0
ToScriptID:
type: integer
format: int64
minimum: 0
Policy:
type: integer
format: int32
minimum: 0
Error:
description: Represents error object
type: object
properties:
code:
type: integer
format: int64
minimum: 0
message:
type: string
required:
- code
- message

View File

@@ -80,6 +80,21 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/migrate-maps:
post:
summary: Perform maps migration
operationId: migrateMaps
tags:
- Maps
responses:
"200":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/maps:
get:
summary: Get list of maps
@@ -104,16 +119,23 @@ paths:
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 1
maximum: 5
- name: Sort
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
maximum: 4
description: >
Sort order:
* `0` - Disabled
* `1` - DisplayNameAscending
* `2` - DisplayNameDescending
* `3` - DateAscending
* `4` - DateDescending
responses:
"200":
description: Successful response
@@ -151,6 +173,34 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/maps/{MapID}/download:
get:
summary: Download the map asset
operationId: downloadMapAsset
tags:
- Maps
parameters:
- name: MapID
in: path
required: true
schema:
type: integer
format: int64
minimum: 0
responses:
"200":
description: Successful response
content:
application/octet-stream:
schema:
type: string
format: binary
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes:
get:
summary: Get list of mapfixes
@@ -175,41 +225,71 @@ paths:
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 1
maximum: 5
description: >
Game ID:
* `1` - Bhop
* `2` - Surf
* `5` - FlyTrials
- name: Sort
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
maximum: 4
description: >
Sort order:
* `0` - Disabled
* `1` - DisplayNameAscending
* `2` - DisplayNameDescending
* `3` - DateAscending
* `4` - DateDescending
- name: Submitter
in: query
schema:
type: integer
format: int64
format: uint64
minimum: 0
- name: AssetID
in: query
schema:
type: integer
format: int64
format: uint64
minimum: 0
- name: TargetAssetID
in: query
schema:
type: integer
format: int64
format: uint64
minimum: 0
- name: StatusID
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
maximum: 9
description: >
// Phase: Creation
* `0` - UnderConstruction
* `1` - ChangesRequested
// Phase: Review
* `2` - Submitting
* `3` - Submitted
// Phase: Testing
* `4` - AcceptedUnvalidated // pending script review, can re-trigger validation
* `5` - Validating
* `6` - Validated
* `7` - Uploading
// Phase: Final MapfixStatus
* `8` - Uploaded // uploaded to the group, but pending release
* `9` - Rejected
responses:
"200":
description: Successful response
@@ -332,14 +412,14 @@ paths:
required: true
schema:
type: integer
format: int64
format: uint64
minimum: 0
- name: ModelVersion
in: query
required: true
schema:
type: integer
format: int64
format: uint64
minimum: 0
responses:
"204":
@@ -384,10 +464,10 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/bypass-submit:
/mapfixes/{MapfixID}/status/trigger-submit-unchecked:
post:
summary: Role Reviewer changes status from ChangesRequested -> Submitted
operationId: actionMapfixBypassSubmit
summary: Role Reviewer changes status from ChangesRequested -> Submitting
operationId: actionMapfixTriggerSubmitUnchecked
tags:
- Mapfixes
parameters:
@@ -599,41 +679,72 @@ paths:
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 1
maximum: 5
description: >
Game ID:
* `1` - Bhop
* `2` - Surf
* `5` - FlyTrials
- name: Sort
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
maximum: 4
description: >
Sort order:
* `0` - Disabled
* `1` - DisplayNameAscending
* `2` - DisplayNameDescending
* `3` - DateAscending
* `4` - DateDescending
- name: Submitter
in: query
schema:
type: integer
format: int64
format: uint64
minimum: 0
- name: AssetID
in: query
schema:
type: integer
format: int64
format: uint64
minimum: 0
- name: UploadedAssetID
in: query
schema:
type: integer
format: int64
format: uint64
minimum: 0
- name: StatusID
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
maximum: 10
description: >
// Phase: Creation
* `0` - UnderConstruction
* `1` - ChangesRequested
// Phase: Review
* `2` - Submitting
* `3` - Submitted
// Phase: Testing
* `4` - AcceptedUnvalidated // pending script review, can re-trigger validation
* `5` - Validating
* `6` - Validated
* `7` - Uploading
* `8` - Uploaded // uploaded to the group, but pending release
// Phase: Final SubmissionStatus
* `9` - Rejected
* `10` - Released
responses:
"200":
description: Successful response
@@ -781,14 +892,14 @@ paths:
required: true
schema:
type: integer
format: int64
format: uint64
minimum: 0
- name: ModelVersion
in: query
required: true
schema:
type: integer
format: int64
format: uint64
minimum: 0
responses:
"204":
@@ -833,10 +944,10 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/bypass-submit:
/submissions/{SubmissionID}/status/trigger-submit-unchecked:
post:
summary: Role Reviewer changes status from ChangesRequested -> Submitted
operationId: actionSubmissionBypassSubmit
summary: Role Reviewer changes status from ChangesRequested -> Submitting
operationId: actionSubmissionTriggerSubmitUnchecked
tags:
- Submissions
parameters:
@@ -1054,7 +1165,7 @@ paths:
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
responses:
"200":
@@ -1185,7 +1296,7 @@ paths:
in: query
schema:
type: integer
format: int32
format: uint32
minimum: 0
- name: ResourceID
in: query
@@ -1359,7 +1470,7 @@ components:
required: true
schema:
type: integer
format: int32
format: uint32
minimum: 1
Limit:
name: Limit
@@ -1367,7 +1478,7 @@ components:
required: true
schema:
type: integer
format: int32
format: uint32
minimum: 0
maximum: 100
schemas:
@@ -1391,20 +1502,20 @@ components:
format: int64
User:
type: integer
format: int64
format: uint64
Username:
type: string
maxLength: 64
ResourceType:
type: integer
format: int32
format: uint32
description: Is this a submission or is it a mapfix
ResourceID:
type: integer
format: int64
EventType:
type: integer
format: int32
format: uint32
EventData:
type: object
description: Arbitrary event data
@@ -1443,7 +1554,7 @@ components:
properties:
Roles:
type: integer
format: int32
format: uint32
minimum: 0
User:
required:
@@ -1454,7 +1565,7 @@ components:
properties:
UserID:
type: integer
format: int64
format: uint64
minimum: 0
Username:
type: string
@@ -1469,6 +1580,13 @@ components:
- Creator
- GameID
- Date
- CreatedAt
- UpdatedAt
- Submitter
- Thumbnail
- AssetVersion
- LoadCount
- Modes
type: object
properties:
ID:
@@ -1483,12 +1601,33 @@ components:
maxLength: 128
GameID:
type: integer
format: int32
format: uint32
minimum: 0
Date:
type: integer
format: int64
minimum: 0
CreatedAt:
type: integer
format: int64
UpdatedAt:
type: integer
format: int64
Submitter:
type: integer
format: uint64
Thumbnail:
type: integer
format: uint64
AssetVersion:
type: integer
format: uint64
LoadCount:
type: integer
format: uint32
Modes:
type: integer
format: uint32
Mapfix:
required:
- ID
@@ -1518,7 +1657,7 @@ components:
maxLength: 128
GameID:
type: integer
format: int32
format: uint32
minimum: 0
CreatedAt:
type: integer
@@ -1530,25 +1669,25 @@ components:
minimum: 0
Submitter:
type: integer
format: int64
format: uint64
minimum: 0
AssetID:
type: integer
format: int64
format: uint64
minimum: 0
AssetVersion:
type: integer
format: int64
format: uint64
minimum: 0
Completed:
type: boolean
TargetAssetID:
type: integer
format: int64
format: uint64
minimum: 0
StatusID:
type: integer
format: int32
format: uint32
minimum: 0
Description:
type: string
@@ -1561,7 +1700,7 @@ components:
properties:
Total:
type: integer
format: int64
format: uint32
minimum: 0
Mapfixes:
type: array
@@ -1576,11 +1715,11 @@ components:
properties:
AssetID:
type: integer
format: int64
format: uint64
minimum: 0
TargetAssetID:
type: integer
format: int64
format: uint64
minimum: 0
Description:
type: string
@@ -1605,11 +1744,11 @@ components:
minimum: 0
Owner:
type: integer
format: int64
format: uint64
minimum: 0
Status:
type: integer
format: int32
format: uint32
minimum: 0
StatusMessage:
type: string
@@ -1647,7 +1786,7 @@ components:
maxLength: 128
GameID:
type: integer
format: int32
format: uint32
minimum: 0
CreatedAt:
type: integer
@@ -1659,33 +1798,33 @@ components:
minimum: 0
Submitter:
type: integer
format: int64
format: uint64
minimum: 0
AssetID:
type: integer
format: int64
format: uint64
minimum: 0
AssetVersion:
type: integer
format: int64
format: uint64
minimum: 0
ValidatedAssetID:
type: integer
format: int64
format: uint64
minimum: 0
ValidatedAssetVersion:
type: integer
format: int64
format: uint64
minimum: 0
Completed:
type: boolean
UploadedAssetID:
type: integer
format: int64
format: uint64
minimum: 0
StatusID:
type: integer
format: int32
format: uint32
minimum: 0
Submissions:
required:
@@ -1695,7 +1834,7 @@ components:
properties:
Total:
type: integer
format: int64
format: uint32
minimum: 0
Submissions:
type: array
@@ -1711,7 +1850,7 @@ components:
properties:
AssetID:
type: integer
format: int64
format: uint64
minimum: 0
DisplayName:
type: string
@@ -1721,7 +1860,7 @@ components:
maxLength: 128
GameID:
type: integer
format: int32
format: uint32
minimum: 0
ReleaseInfo:
required:
@@ -1762,7 +1901,7 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: int32
format: uint32
minimum: 0
ResourceID:
type: integer
@@ -1784,7 +1923,7 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: int32
format: uint32
minimum: 0
ResourceID:
type: integer
@@ -1807,7 +1946,7 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: int32
format: uint32
minimum: 0
ResourceID:
type: integer
@@ -1835,7 +1974,7 @@ components:
minimum: 0
Policy:
type: integer
format: int32
format: uint32
minimum: 0
ScriptPolicyCreate:
required:
@@ -1854,7 +1993,7 @@ components:
minimum: 0
Policy:
type: integer
format: int32
format: uint32
minimum: 0
ScriptPolicyUpdate:
required:
@@ -1875,7 +2014,7 @@ components:
minimum: 0
Policy:
type: integer
format: int32
format: uint32
minimum: 0
Error:
description: Represents error object
@@ -1883,7 +2022,7 @@ components:
properties:
code:
type: integer
format: int64
format: uint32
minimum: 0
message:
type: string

View File

@@ -35,12 +35,6 @@ type Invoker interface {
//
// POST /mapfixes/{MapfixID}/status/reset-validating
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixBypassSubmit invokes actionMapfixBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/bypass-submit
ActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) error
// ActionMapfixReject invokes actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@@ -78,6 +72,12 @@ type Invoker interface {
//
// POST /mapfixes/{MapfixID}/status/trigger-submit
ActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) error
// ActionMapfixTriggerSubmitUnchecked invokes actionMapfixTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/trigger-submit-unchecked
ActionMapfixTriggerSubmitUnchecked(ctx context.Context, params ActionMapfixTriggerSubmitUncheckedParams) error
// ActionMapfixTriggerUpload invokes actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@@ -102,12 +102,6 @@ type Invoker interface {
//
// POST /submissions/{SubmissionID}/status/reset-validating
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionBypassSubmit invokes actionSubmissionBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/bypass-submit
ActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) error
// ActionSubmissionReject invokes actionSubmissionReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@@ -145,6 +139,12 @@ type Invoker interface {
//
// POST /submissions/{SubmissionID}/status/trigger-submit
ActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) error
// ActionSubmissionTriggerSubmitUnchecked invokes actionSubmissionTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/trigger-submit-unchecked
ActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params ActionSubmissionTriggerSubmitUncheckedParams) error
// ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@@ -217,6 +217,12 @@ type Invoker interface {
//
// DELETE /script-policy/{ScriptPolicyID}
DeleteScriptPolicy(ctx context.Context, params DeleteScriptPolicyParams) error
// DownloadMapAsset invokes downloadMapAsset operation.
//
// Download the map asset.
//
// GET /maps/{MapID}/download
DownloadMapAsset(ctx context.Context, params DownloadMapAssetParams) (DownloadMapAssetOK, error)
// GetMap invokes getMap operation.
//
// Retrieve map with ID.
@@ -295,6 +301,12 @@ type Invoker interface {
//
// GET /submissions
ListSubmissions(ctx context.Context, params ListSubmissionsParams) (*Submissions, error)
// MigrateMaps invokes migrateMaps operation.
//
// Perform maps migration.
//
// POST /migrate-maps
MigrateMaps(ctx context.Context) error
// ReleaseSubmissions invokes releaseSubmissions operation.
//
// Release a set of uploaded maps.
@@ -530,130 +542,6 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf
return result, nil
}
// ActionMapfixBypassSubmit invokes actionMapfixBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/bypass-submit
func (c *Client) ActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) error {
_, err := c.sendActionMapfixBypassSubmit(ctx, params)
return err
}
func (c *Client) sendActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) (res *ActionMapfixBypassSubmitNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixBypassSubmit"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/bypass-submit"),
}
// Run stopwatch.
startTime := time.Now()
defer func() {
// Use floating point division here for higher precision (instead of Millisecond method).
elapsedDuration := time.Since(startTime)
c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...))
}()
// Increment request counter.
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, ActionMapfixBypassSubmitOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/mapfixes/"
{
// Encode "MapfixID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "MapfixID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/status/bypass-submit"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
{
type bitset = [1]uint8
var satisfied bitset
{
stage = "Security:CookieAuth"
switch err := c.securityCookieAuth(ctx, ActionMapfixBypassSubmitOperation, r); {
case err == nil: // if NO error
satisfied[0] |= 1 << 0
case errors.Is(err, ogenerrors.ErrSkipClientSecurity):
// Skip this security.
default:
return res, errors.Wrap(err, "security \"CookieAuth\"")
}
}
if ok := func() bool {
nextRequirement:
for _, requirement := range []bitset{
{0b00000001},
} {
for i, mask := range requirement {
if satisfied[i]&mask != mask {
continue nextRequirement
}
}
return true
}
return false
}(); !ok {
return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied
}
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeActionMapfixBypassSubmitResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionMapfixReject invokes actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@@ -1399,6 +1287,130 @@ func (c *Client) sendActionMapfixTriggerSubmit(ctx context.Context, params Actio
return result, nil
}
// ActionMapfixTriggerSubmitUnchecked invokes actionMapfixTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/trigger-submit-unchecked
func (c *Client) ActionMapfixTriggerSubmitUnchecked(ctx context.Context, params ActionMapfixTriggerSubmitUncheckedParams) error {
_, err := c.sendActionMapfixTriggerSubmitUnchecked(ctx, params)
return err
}
func (c *Client) sendActionMapfixTriggerSubmitUnchecked(ctx context.Context, params ActionMapfixTriggerSubmitUncheckedParams) (res *ActionMapfixTriggerSubmitUncheckedNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixTriggerSubmitUnchecked"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/trigger-submit-unchecked"),
}
// Run stopwatch.
startTime := time.Now()
defer func() {
// Use floating point division here for higher precision (instead of Millisecond method).
elapsedDuration := time.Since(startTime)
c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...))
}()
// Increment request counter.
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, ActionMapfixTriggerSubmitUncheckedOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/mapfixes/"
{
// Encode "MapfixID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "MapfixID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/status/trigger-submit-unchecked"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
{
type bitset = [1]uint8
var satisfied bitset
{
stage = "Security:CookieAuth"
switch err := c.securityCookieAuth(ctx, ActionMapfixTriggerSubmitUncheckedOperation, r); {
case err == nil: // if NO error
satisfied[0] |= 1 << 0
case errors.Is(err, ogenerrors.ErrSkipClientSecurity):
// Skip this security.
default:
return res, errors.Wrap(err, "security \"CookieAuth\"")
}
}
if ok := func() bool {
nextRequirement:
for _, requirement := range []bitset{
{0b00000001},
} {
for i, mask := range requirement {
if satisfied[i]&mask != mask {
continue nextRequirement
}
}
return true
}
return false
}(); !ok {
return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied
}
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeActionMapfixTriggerSubmitUncheckedResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionMapfixTriggerUpload invokes actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@@ -1895,130 +1907,6 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
return result, nil
}
// ActionSubmissionBypassSubmit invokes actionSubmissionBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/bypass-submit
func (c *Client) ActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) error {
_, err := c.sendActionSubmissionBypassSubmit(ctx, params)
return err
}
func (c *Client) sendActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) (res *ActionSubmissionBypassSubmitNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionSubmissionBypassSubmit"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/bypass-submit"),
}
// Run stopwatch.
startTime := time.Now()
defer func() {
// Use floating point division here for higher precision (instead of Millisecond method).
elapsedDuration := time.Since(startTime)
c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...))
}()
// Increment request counter.
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, ActionSubmissionBypassSubmitOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/submissions/"
{
// Encode "SubmissionID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "SubmissionID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/status/bypass-submit"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
{
type bitset = [1]uint8
var satisfied bitset
{
stage = "Security:CookieAuth"
switch err := c.securityCookieAuth(ctx, ActionSubmissionBypassSubmitOperation, r); {
case err == nil: // if NO error
satisfied[0] |= 1 << 0
case errors.Is(err, ogenerrors.ErrSkipClientSecurity):
// Skip this security.
default:
return res, errors.Wrap(err, "security \"CookieAuth\"")
}
}
if ok := func() bool {
nextRequirement:
for _, requirement := range []bitset{
{0b00000001},
} {
for i, mask := range requirement {
if satisfied[i]&mask != mask {
continue nextRequirement
}
}
return true
}
return false
}(); !ok {
return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied
}
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeActionSubmissionBypassSubmitResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionSubmissionReject invokes actionSubmissionReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@@ -2764,6 +2652,130 @@ func (c *Client) sendActionSubmissionTriggerSubmit(ctx context.Context, params A
return result, nil
}
// ActionSubmissionTriggerSubmitUnchecked invokes actionSubmissionTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/trigger-submit-unchecked
func (c *Client) ActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params ActionSubmissionTriggerSubmitUncheckedParams) error {
_, err := c.sendActionSubmissionTriggerSubmitUnchecked(ctx, params)
return err
}
func (c *Client) sendActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params ActionSubmissionTriggerSubmitUncheckedParams) (res *ActionSubmissionTriggerSubmitUncheckedNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionSubmissionTriggerSubmitUnchecked"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/trigger-submit-unchecked"),
}
// Run stopwatch.
startTime := time.Now()
defer func() {
// Use floating point division here for higher precision (instead of Millisecond method).
elapsedDuration := time.Since(startTime)
c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...))
}()
// Increment request counter.
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, ActionSubmissionTriggerSubmitUncheckedOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/submissions/"
{
// Encode "SubmissionID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "SubmissionID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/status/trigger-submit-unchecked"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
{
type bitset = [1]uint8
var satisfied bitset
{
stage = "Security:CookieAuth"
switch err := c.securityCookieAuth(ctx, ActionSubmissionTriggerSubmitUncheckedOperation, r); {
case err == nil: // if NO error
satisfied[0] |= 1 << 0
case errors.Is(err, ogenerrors.ErrSkipClientSecurity):
// Skip this security.
default:
return res, errors.Wrap(err, "security \"CookieAuth\"")
}
}
if ok := func() bool {
nextRequirement:
for _, requirement := range []bitset{
{0b00000001},
} {
for i, mask := range requirement {
if satisfied[i]&mask != mask {
continue nextRequirement
}
}
return true
}
return false
}(); !ok {
return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied
}
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeActionSubmissionTriggerSubmitUncheckedResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@@ -4176,6 +4188,130 @@ func (c *Client) sendDeleteScriptPolicy(ctx context.Context, params DeleteScript
return result, nil
}
// DownloadMapAsset invokes downloadMapAsset operation.
//
// Download the map asset.
//
// GET /maps/{MapID}/download
func (c *Client) DownloadMapAsset(ctx context.Context, params DownloadMapAssetParams) (DownloadMapAssetOK, error) {
res, err := c.sendDownloadMapAsset(ctx, params)
return res, err
}
func (c *Client) sendDownloadMapAsset(ctx context.Context, params DownloadMapAssetParams) (res DownloadMapAssetOK, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("downloadMapAsset"),
semconv.HTTPRequestMethodKey.String("GET"),
semconv.HTTPRouteKey.String("/maps/{MapID}/download"),
}
// Run stopwatch.
startTime := time.Now()
defer func() {
// Use floating point division here for higher precision (instead of Millisecond method).
elapsedDuration := time.Since(startTime)
c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...))
}()
// Increment request counter.
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, DownloadMapAssetOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [3]string
pathParts[0] = "/maps/"
{
// Encode "MapID" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "MapID",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Int64ToString(params.MapID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
pathParts[2] = "/download"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "GET", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
{
type bitset = [1]uint8
var satisfied bitset
{
stage = "Security:CookieAuth"
switch err := c.securityCookieAuth(ctx, DownloadMapAssetOperation, r); {
case err == nil: // if NO error
satisfied[0] |= 1 << 0
case errors.Is(err, ogenerrors.ErrSkipClientSecurity):
// Skip this security.
default:
return res, errors.Wrap(err, "security \"CookieAuth\"")
}
}
if ok := func() bool {
nextRequirement:
for _, requirement := range []bitset{
{0b00000001},
} {
for i, mask := range requirement {
if satisfied[i]&mask != mask {
continue nextRequirement
}
}
return true
}
return false
}(); !ok {
return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied
}
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeDownloadMapAssetResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// GetMap invokes getMap operation.
//
// Retrieve map with ID.
@@ -5991,6 +6127,111 @@ func (c *Client) sendListSubmissions(ctx context.Context, params ListSubmissions
return result, nil
}
// MigrateMaps invokes migrateMaps operation.
//
// Perform maps migration.
//
// POST /migrate-maps
func (c *Client) MigrateMaps(ctx context.Context) error {
_, err := c.sendMigrateMaps(ctx)
return err
}
func (c *Client) sendMigrateMaps(ctx context.Context) (res *MigrateMapsOK, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("migrateMaps"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/migrate-maps"),
}
// Run stopwatch.
startTime := time.Now()
defer func() {
// Use floating point division here for higher precision (instead of Millisecond method).
elapsedDuration := time.Since(startTime)
c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...))
}()
// Increment request counter.
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, MigrateMapsOperation,
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [1]string
pathParts[0] = "/migrate-maps"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
{
type bitset = [1]uint8
var satisfied bitset
{
stage = "Security:CookieAuth"
switch err := c.securityCookieAuth(ctx, MigrateMapsOperation, r); {
case err == nil: // if NO error
satisfied[0] |= 1 << 0
case errors.Is(err, ogenerrors.ErrSkipClientSecurity):
// Skip this security.
default:
return res, errors.Wrap(err, "security \"CookieAuth\"")
}
}
if ok := func() bool {
nextRequirement:
for _, requirement := range []bitset{
{0b00000001},
} {
for i, mask := range requirement {
if satisfied[i]&mask != mask {
continue nextRequirement
}
}
return true
}
return false
}(); !ok {
return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied
}
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeMigrateMapsResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ReleaseSubmissions invokes releaseSubmissions operation.
//
// Release a set of uploaded maps.

File diff suppressed because it is too large Load Diff

View File

@@ -6,58 +6,60 @@ package api
type OperationName = string
const (
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
ActionMapfixBypassSubmitOperation OperationName = "ActionMapfixBypassSubmit"
ActionMapfixRejectOperation OperationName = "ActionMapfixReject"
ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges"
ActionMapfixResetSubmittingOperation OperationName = "ActionMapfixResetSubmitting"
ActionMapfixRetryValidateOperation OperationName = "ActionMapfixRetryValidate"
ActionMapfixRevokeOperation OperationName = "ActionMapfixRevoke"
ActionMapfixTriggerSubmitOperation OperationName = "ActionMapfixTriggerSubmit"
ActionMapfixTriggerUploadOperation OperationName = "ActionMapfixTriggerUpload"
ActionMapfixTriggerValidateOperation OperationName = "ActionMapfixTriggerValidate"
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionBypassSubmitOperation OperationName = "ActionSubmissionBypassSubmit"
ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject"
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
ActionSubmissionResetSubmittingOperation OperationName = "ActionSubmissionResetSubmitting"
ActionSubmissionRetryValidateOperation OperationName = "ActionSubmissionRetryValidate"
ActionSubmissionRevokeOperation OperationName = "ActionSubmissionRevoke"
ActionSubmissionTriggerSubmitOperation OperationName = "ActionSubmissionTriggerSubmit"
ActionSubmissionTriggerUploadOperation OperationName = "ActionSubmissionTriggerUpload"
ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateMapfixOperation OperationName = "CreateMapfix"
CreateMapfixAuditCommentOperation OperationName = "CreateMapfixAuditComment"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
CreateSubmissionAdminOperation OperationName = "CreateSubmissionAdmin"
CreateSubmissionAuditCommentOperation OperationName = "CreateSubmissionAuditComment"
DeleteScriptOperation OperationName = "DeleteScript"
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"
GetMapOperation OperationName = "GetMap"
GetMapfixOperation OperationName = "GetMapfix"
GetOperationOperation OperationName = "GetOperation"
GetScriptOperation OperationName = "GetScript"
GetScriptPolicyOperation OperationName = "GetScriptPolicy"
GetSubmissionOperation OperationName = "GetSubmission"
ListMapfixAuditEventsOperation OperationName = "ListMapfixAuditEvents"
ListMapfixesOperation OperationName = "ListMapfixes"
ListMapsOperation OperationName = "ListMaps"
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"
ListSubmissionAuditEventsOperation OperationName = "ListSubmissionAuditEvents"
ListSubmissionsOperation OperationName = "ListSubmissions"
ReleaseSubmissionsOperation OperationName = "ReleaseSubmissions"
SessionRolesOperation OperationName = "SessionRoles"
SessionUserOperation OperationName = "SessionUser"
SessionValidateOperation OperationName = "SessionValidate"
SetMapfixCompletedOperation OperationName = "SetMapfixCompleted"
SetSubmissionCompletedOperation OperationName = "SetSubmissionCompleted"
UpdateMapfixModelOperation OperationName = "UpdateMapfixModel"
UpdateScriptOperation OperationName = "UpdateScript"
UpdateScriptPolicyOperation OperationName = "UpdateScriptPolicy"
UpdateSubmissionModelOperation OperationName = "UpdateSubmissionModel"
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
ActionMapfixRejectOperation OperationName = "ActionMapfixReject"
ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges"
ActionMapfixResetSubmittingOperation OperationName = "ActionMapfixResetSubmitting"
ActionMapfixRetryValidateOperation OperationName = "ActionMapfixRetryValidate"
ActionMapfixRevokeOperation OperationName = "ActionMapfixRevoke"
ActionMapfixTriggerSubmitOperation OperationName = "ActionMapfixTriggerSubmit"
ActionMapfixTriggerSubmitUncheckedOperation OperationName = "ActionMapfixTriggerSubmitUnchecked"
ActionMapfixTriggerUploadOperation OperationName = "ActionMapfixTriggerUpload"
ActionMapfixTriggerValidateOperation OperationName = "ActionMapfixTriggerValidate"
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject"
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
ActionSubmissionResetSubmittingOperation OperationName = "ActionSubmissionResetSubmitting"
ActionSubmissionRetryValidateOperation OperationName = "ActionSubmissionRetryValidate"
ActionSubmissionRevokeOperation OperationName = "ActionSubmissionRevoke"
ActionSubmissionTriggerSubmitOperation OperationName = "ActionSubmissionTriggerSubmit"
ActionSubmissionTriggerSubmitUncheckedOperation OperationName = "ActionSubmissionTriggerSubmitUnchecked"
ActionSubmissionTriggerUploadOperation OperationName = "ActionSubmissionTriggerUpload"
ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateMapfixOperation OperationName = "CreateMapfix"
CreateMapfixAuditCommentOperation OperationName = "CreateMapfixAuditComment"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
CreateSubmissionAdminOperation OperationName = "CreateSubmissionAdmin"
CreateSubmissionAuditCommentOperation OperationName = "CreateSubmissionAuditComment"
DeleteScriptOperation OperationName = "DeleteScript"
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"
DownloadMapAssetOperation OperationName = "DownloadMapAsset"
GetMapOperation OperationName = "GetMap"
GetMapfixOperation OperationName = "GetMapfix"
GetOperationOperation OperationName = "GetOperation"
GetScriptOperation OperationName = "GetScript"
GetScriptPolicyOperation OperationName = "GetScriptPolicy"
GetSubmissionOperation OperationName = "GetSubmission"
ListMapfixAuditEventsOperation OperationName = "ListMapfixAuditEvents"
ListMapfixesOperation OperationName = "ListMapfixes"
ListMapsOperation OperationName = "ListMaps"
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"
ListSubmissionAuditEventsOperation OperationName = "ListSubmissionAuditEvents"
ListSubmissionsOperation OperationName = "ListSubmissions"
MigrateMapsOperation OperationName = "MigrateMaps"
ReleaseSubmissionsOperation OperationName = "ReleaseSubmissions"
SessionRolesOperation OperationName = "SessionRoles"
SessionUserOperation OperationName = "SessionUser"
SessionValidateOperation OperationName = "SessionValidate"
SetMapfixCompletedOperation OperationName = "SetMapfixCompleted"
SetSubmissionCompletedOperation OperationName = "SetSubmissionCompleted"
UpdateMapfixModelOperation OperationName = "UpdateMapfixModel"
UpdateScriptOperation OperationName = "UpdateScript"
UpdateScriptPolicyOperation OperationName = "UpdateScriptPolicy"
UpdateSubmissionModelOperation OperationName = "UpdateSubmissionModel"
)

View File

@@ -98,89 +98,6 @@ func decodeActionMapfixAcceptedParams(args [1]string, argsEscaped bool, r *http.
return params, nil
}
// ActionMapfixBypassSubmitParams is parameters of actionMapfixBypassSubmit operation.
type ActionMapfixBypassSubmitParams struct {
// The unique identifier for a mapfix.
MapfixID int64
}
func unpackActionMapfixBypassSubmitParams(packed middleware.Parameters) (params ActionMapfixBypassSubmitParams) {
{
key := middleware.ParameterKey{
Name: "MapfixID",
In: "path",
}
params.MapfixID = packed[key].(int64)
}
return params
}
func decodeActionMapfixBypassSubmitParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixBypassSubmitParams, _ error) {
// Decode path: MapfixID.
if err := func() error {
param := args[0]
if argsEscaped {
unescaped, err := url.PathUnescape(args[0])
if err != nil {
return errors.Wrap(err, "unescape path")
}
param = unescaped
}
if len(param) > 0 {
d := uri.NewPathDecoder(uri.PathDecoderConfig{
Param: "MapfixID",
Value: param,
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.MapfixID = c
return nil
}(); err != nil {
return err
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(params.MapfixID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "MapfixID",
In: "path",
Err: err,
}
}
return params, nil
}
// ActionMapfixRejectParams is parameters of actionMapfixReject operation.
type ActionMapfixRejectParams struct {
// The unique identifier for a mapfix.
@@ -679,6 +596,89 @@ func decodeActionMapfixTriggerSubmitParams(args [1]string, argsEscaped bool, r *
return params, nil
}
// ActionMapfixTriggerSubmitUncheckedParams is parameters of actionMapfixTriggerSubmitUnchecked operation.
type ActionMapfixTriggerSubmitUncheckedParams struct {
// The unique identifier for a mapfix.
MapfixID int64
}
func unpackActionMapfixTriggerSubmitUncheckedParams(packed middleware.Parameters) (params ActionMapfixTriggerSubmitUncheckedParams) {
{
key := middleware.ParameterKey{
Name: "MapfixID",
In: "path",
}
params.MapfixID = packed[key].(int64)
}
return params
}
func decodeActionMapfixTriggerSubmitUncheckedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionMapfixTriggerSubmitUncheckedParams, _ error) {
// Decode path: MapfixID.
if err := func() error {
param := args[0]
if argsEscaped {
unescaped, err := url.PathUnescape(args[0])
if err != nil {
return errors.Wrap(err, "unescape path")
}
param = unescaped
}
if len(param) > 0 {
d := uri.NewPathDecoder(uri.PathDecoderConfig{
Param: "MapfixID",
Value: param,
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.MapfixID = c
return nil
}(); err != nil {
return err
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(params.MapfixID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "MapfixID",
In: "path",
Err: err,
}
}
return params, nil
}
// ActionMapfixTriggerUploadParams is parameters of actionMapfixTriggerUpload operation.
type ActionMapfixTriggerUploadParams struct {
// The unique identifier for a mapfix.
@@ -1011,89 +1011,6 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
return params, nil
}
// ActionSubmissionBypassSubmitParams is parameters of actionSubmissionBypassSubmit operation.
type ActionSubmissionBypassSubmitParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
func unpackActionSubmissionBypassSubmitParams(packed middleware.Parameters) (params ActionSubmissionBypassSubmitParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
In: "path",
}
params.SubmissionID = packed[key].(int64)
}
return params
}
func decodeActionSubmissionBypassSubmitParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionBypassSubmitParams, _ error) {
// Decode path: SubmissionID.
if err := func() error {
param := args[0]
if argsEscaped {
unescaped, err := url.PathUnescape(args[0])
if err != nil {
return errors.Wrap(err, "unescape path")
}
param = unescaped
}
if len(param) > 0 {
d := uri.NewPathDecoder(uri.PathDecoderConfig{
Param: "SubmissionID",
Value: param,
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.SubmissionID = c
return nil
}(); err != nil {
return err
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(params.SubmissionID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "SubmissionID",
In: "path",
Err: err,
}
}
return params, nil
}
// ActionSubmissionRejectParams is parameters of actionSubmissionReject operation.
type ActionSubmissionRejectParams struct {
// The unique identifier for a submission.
@@ -1592,6 +1509,89 @@ func decodeActionSubmissionTriggerSubmitParams(args [1]string, argsEscaped bool,
return params, nil
}
// ActionSubmissionTriggerSubmitUncheckedParams is parameters of actionSubmissionTriggerSubmitUnchecked operation.
type ActionSubmissionTriggerSubmitUncheckedParams struct {
// The unique identifier for a submission.
SubmissionID int64
}
func unpackActionSubmissionTriggerSubmitUncheckedParams(packed middleware.Parameters) (params ActionSubmissionTriggerSubmitUncheckedParams) {
{
key := middleware.ParameterKey{
Name: "SubmissionID",
In: "path",
}
params.SubmissionID = packed[key].(int64)
}
return params
}
func decodeActionSubmissionTriggerSubmitUncheckedParams(args [1]string, argsEscaped bool, r *http.Request) (params ActionSubmissionTriggerSubmitUncheckedParams, _ error) {
// Decode path: SubmissionID.
if err := func() error {
param := args[0]
if argsEscaped {
unescaped, err := url.PathUnescape(args[0])
if err != nil {
return errors.Wrap(err, "unescape path")
}
param = unescaped
}
if len(param) > 0 {
d := uri.NewPathDecoder(uri.PathDecoderConfig{
Param: "SubmissionID",
Value: param,
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.SubmissionID = c
return nil
}(); err != nil {
return err
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(params.SubmissionID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "SubmissionID",
In: "path",
Err: err,
}
}
return params, nil
}
// ActionSubmissionTriggerUploadParams is parameters of actionSubmissionTriggerUpload operation.
type ActionSubmissionTriggerUploadParams struct {
// The unique identifier for a submission.
@@ -2173,6 +2173,88 @@ func decodeDeleteScriptPolicyParams(args [1]string, argsEscaped bool, r *http.Re
return params, nil
}
// DownloadMapAssetParams is parameters of downloadMapAsset operation.
type DownloadMapAssetParams struct {
MapID int64
}
func unpackDownloadMapAssetParams(packed middleware.Parameters) (params DownloadMapAssetParams) {
{
key := middleware.ParameterKey{
Name: "MapID",
In: "path",
}
params.MapID = packed[key].(int64)
}
return params
}
func decodeDownloadMapAssetParams(args [1]string, argsEscaped bool, r *http.Request) (params DownloadMapAssetParams, _ error) {
// Decode path: MapID.
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: "MapID",
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.MapID = c
return nil
}(); err != nil {
return err
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(params.MapID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "MapID",
In: "path",
Err: err,
}
}
return params, nil
}
// GetMapParams is parameters of getMap operation.
type GetMapParams struct {
// The unique identifier for a map.
@@ -2879,16 +2961,25 @@ func decodeListMapfixAuditEventsParams(args [1]string, argsEscaped bool, r *http
// ListMapfixesParams is parameters of listMapfixes operation.
type ListMapfixesParams struct {
Page int32
Limit int32
DisplayName OptString
Creator OptString
GameID OptInt32
Page int32
Limit int32
DisplayName OptString
Creator OptString
// Game ID: * `1` - Bhop * `2` - Surf * `5` - FlyTrials.
GameID OptInt32
// Sort order: * `0` - Disabled * `1` - DisplayNameAscending * `2` - DisplayNameDescending * `3` -
// DateAscending * `4` - DateDescending.
Sort OptInt32
Submitter OptInt64
AssetID OptInt64
TargetAssetID OptInt64
StatusID OptInt32
// // Phase: Creation * `0` - UnderConstruction * `1` - ChangesRequested
// // Phase: Review * `2` - Submitting * `3` - Submitted
// // Phase: Testing * `4` - AcceptedUnvalidated // pending script review, can re-trigger validation
// * `5` - Validating * `6` - Validated * `7` - Uploading
// // Phase: Final MapfixStatus * `8` - Uploaded // uploaded to the group, but pending release * `9`
// - Rejected.
StatusID OptInt32
}
func unpackListMapfixesParams(packed middleware.Parameters) (params ListMapfixesParams) {
@@ -3617,7 +3708,9 @@ type ListMapsParams struct {
DisplayName OptString
Creator OptString
GameID OptInt32
Sort OptInt32
// Sort order: * `0` - Disabled * `1` - DisplayNameAscending * `2` - DisplayNameDescending * `3` -
// DateAscending * `4` - DateDescending.
Sort OptInt32
}
func unpackListMapsParams(packed middleware.Parameters) (params ListMapsParams) {
@@ -5117,16 +5210,25 @@ func decodeListSubmissionAuditEventsParams(args [1]string, argsEscaped bool, r *
// ListSubmissionsParams is parameters of listSubmissions operation.
type ListSubmissionsParams struct {
Page int32
Limit int32
DisplayName OptString
Creator OptString
GameID OptInt32
Page int32
Limit int32
DisplayName OptString
Creator OptString
// Game ID: * `1` - Bhop * `2` - Surf * `5` - FlyTrials.
GameID OptInt32
// Sort order: * `0` - Disabled * `1` - DisplayNameAscending * `2` - DisplayNameDescending * `3` -
// DateAscending * `4` - DateDescending.
Sort OptInt32
Submitter OptInt64
AssetID OptInt64
UploadedAssetID OptInt64
StatusID OptInt32
// // Phase: Creation * `0` - UnderConstruction * `1` - ChangesRequested
// // Phase: Review * `2` - Submitting * `3` - Submitted
// // Phase: Testing * `4` - AcceptedUnvalidated // pending script review, can re-trigger validation
// * `5` - Validating * `6` - Validated * `7` - Uploading * `8` - Uploaded // uploaded to the group,
// but pending release
// // Phase: Final SubmissionStatus * `9` - Rejected * `10` - Released.
StatusID OptInt32
}
func unpackListSubmissionsParams(packed middleware.Parameters) (params ListSubmissionsParams) {

View File

@@ -3,6 +3,7 @@
package api
import (
"bytes"
"fmt"
"io"
"mime"
@@ -75,66 +76,6 @@ func decodeActionMapfixAcceptedResponse(resp *http.Response) (res *ActionMapfixA
return res, errors.Wrap(defRes, "error")
}
func decodeActionMapfixBypassSubmitResponse(resp *http.Response) (res *ActionMapfixBypassSubmitNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionMapfixBypassSubmitNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
// Validate response.
if err := func() error {
if err := response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
}
return res, errors.Wrap(defRes, "error")
}
func decodeActionMapfixRejectResponse(resp *http.Response) (res *ActionMapfixRejectNoContent, _ error) {
switch resp.StatusCode {
case 204:
@@ -495,6 +436,66 @@ func decodeActionMapfixTriggerSubmitResponse(resp *http.Response) (res *ActionMa
return res, errors.Wrap(defRes, "error")
}
func decodeActionMapfixTriggerSubmitUncheckedResponse(resp *http.Response) (res *ActionMapfixTriggerSubmitUncheckedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionMapfixTriggerSubmitUncheckedNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
// Validate response.
if err := func() error {
if err := response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
}
return res, errors.Wrap(defRes, "error")
}
func decodeActionMapfixTriggerUploadResponse(resp *http.Response) (res *ActionMapfixTriggerUploadNoContent, _ error) {
switch resp.StatusCode {
case 204:
@@ -735,66 +736,6 @@ func decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSub
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionBypassSubmitResponse(resp *http.Response) (res *ActionSubmissionBypassSubmitNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionBypassSubmitNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
// Validate response.
if err := func() error {
if err := response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
}
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionRejectResponse(resp *http.Response) (res *ActionSubmissionRejectNoContent, _ error) {
switch resp.StatusCode {
case 204:
@@ -1155,6 +1096,66 @@ func decodeActionSubmissionTriggerSubmitResponse(resp *http.Response) (res *Acti
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionTriggerSubmitUncheckedResponse(resp *http.Response) (res *ActionSubmissionTriggerSubmitUncheckedNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionTriggerSubmitUncheckedNoContent{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
// Validate response.
if err := func() error {
if err := response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
}
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionTriggerUploadResponse(resp *http.Response) (res *ActionSubmissionTriggerUploadNoContent, _ error) {
switch resp.StatusCode {
case 204:
@@ -2080,6 +2081,82 @@ func decodeDeleteScriptPolicyResponse(resp *http.Response) (res *DeleteScriptPol
return res, errors.Wrap(defRes, "error")
}
func decodeDownloadMapAssetResponse(resp *http.Response) (res DownloadMapAssetOK, _ 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/octet-stream":
reader := resp.Body
b, err := io.ReadAll(reader)
if err != nil {
return res, err
}
response := DownloadMapAssetOK{Data: bytes.NewReader(b)}
return response, nil
default:
return res, validate.InvalidContentType(ct)
}
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
// Validate response.
if err := func() error {
if err := response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
}
return res, errors.Wrap(defRes, "error")
}
func decodeGetMapResponse(resp *http.Response) (res *Map, _ error) {
switch resp.StatusCode {
case 200:
@@ -3518,6 +3595,66 @@ func decodeListSubmissionsResponse(resp *http.Response) (res *Submissions, _ err
return res, errors.Wrap(defRes, "error")
}
func decodeMigrateMapsResponse(resp *http.Response) (res *MigrateMapsOK, _ error) {
switch resp.StatusCode {
case 200:
// Code 200.
return &MigrateMapsOK{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
// Validate response.
if err := func() error {
if err := response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)
}
return res, errors.Wrap(defRes, "error")
}
func decodeReleaseSubmissionsResponse(resp *http.Response) (res *ReleaseSubmissionsCreated, _ error) {
switch resp.StatusCode {
case 201:

View File

@@ -3,6 +3,7 @@
package api
import (
"io"
"net/http"
"github.com/go-faster/errors"
@@ -20,13 +21,6 @@ func encodeActionMapfixAcceptedResponse(response *ActionMapfixAcceptedNoContent,
return nil
}
func encodeActionMapfixBypassSubmitResponse(response *ActionMapfixBypassSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixRejectResponse(response *ActionMapfixRejectNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@@ -69,6 +63,13 @@ func encodeActionMapfixTriggerSubmitResponse(response *ActionMapfixTriggerSubmit
return nil
}
func encodeActionMapfixTriggerSubmitUncheckedResponse(response *ActionMapfixTriggerSubmitUncheckedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixTriggerUploadResponse(response *ActionMapfixTriggerUploadNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@@ -97,13 +98,6 @@ func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNo
return nil
}
func encodeActionSubmissionBypassSubmitResponse(response *ActionSubmissionBypassSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionRejectResponse(response *ActionSubmissionRejectNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@@ -146,6 +140,13 @@ func encodeActionSubmissionTriggerSubmitResponse(response *ActionSubmissionTrigg
return nil
}
func encodeActionSubmissionTriggerSubmitUncheckedResponse(response *ActionSubmissionTriggerSubmitUncheckedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionTriggerUploadResponse(response *ActionSubmissionTriggerUploadNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@@ -265,6 +266,22 @@ func encodeDeleteScriptPolicyResponse(response *DeleteScriptPolicyNoContent, w h
return nil
}
func encodeDownloadMapAssetResponse(response DownloadMapAssetOK, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/octet-stream")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
writer := w
if closer, ok := response.Data.(io.Closer); ok {
defer closer.Close()
}
if _, err := io.Copy(writer, response); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeGetMapResponse(response *Map, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
@@ -467,6 +484,13 @@ func encodeListSubmissionsResponse(response *Submissions, w http.ResponseWriter,
return nil
}
func encodeMigrateMapsResponse(response *MigrateMapsOK, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
return nil
}
func encodeReleaseSubmissionsResponse(response *ReleaseSubmissionsCreated, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))

File diff suppressed because it is too large Load Diff

View File

@@ -17,9 +17,6 @@ func (s *ErrorStatusCode) Error() string {
// ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation.
type ActionMapfixAcceptedNoContent struct{}
// ActionMapfixBypassSubmitNoContent is response for ActionMapfixBypassSubmit operation.
type ActionMapfixBypassSubmitNoContent struct{}
// ActionMapfixRejectNoContent is response for ActionMapfixReject operation.
type ActionMapfixRejectNoContent struct{}
@@ -38,6 +35,9 @@ type ActionMapfixRevokeNoContent struct{}
// ActionMapfixTriggerSubmitNoContent is response for ActionMapfixTriggerSubmit operation.
type ActionMapfixTriggerSubmitNoContent struct{}
// ActionMapfixTriggerSubmitUncheckedNoContent is response for ActionMapfixTriggerSubmitUnchecked operation.
type ActionMapfixTriggerSubmitUncheckedNoContent struct{}
// ActionMapfixTriggerUploadNoContent is response for ActionMapfixTriggerUpload operation.
type ActionMapfixTriggerUploadNoContent struct{}
@@ -50,9 +50,6 @@ type ActionMapfixValidatedNoContent struct{}
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
type ActionSubmissionAcceptedNoContent struct{}
// ActionSubmissionBypassSubmitNoContent is response for ActionSubmissionBypassSubmit operation.
type ActionSubmissionBypassSubmitNoContent struct{}
// ActionSubmissionRejectNoContent is response for ActionSubmissionReject operation.
type ActionSubmissionRejectNoContent struct{}
@@ -71,6 +68,9 @@ type ActionSubmissionRevokeNoContent struct{}
// ActionSubmissionTriggerSubmitNoContent is response for ActionSubmissionTriggerSubmit operation.
type ActionSubmissionTriggerSubmitNoContent struct{}
// ActionSubmissionTriggerSubmitUncheckedNoContent is response for ActionSubmissionTriggerSubmitUnchecked operation.
type ActionSubmissionTriggerSubmitUncheckedNoContent struct{}
// ActionSubmissionTriggerUploadNoContent is response for ActionSubmissionTriggerUpload operation.
type ActionSubmissionTriggerUploadNoContent struct{}
@@ -251,6 +251,20 @@ type DeleteScriptNoContent struct{}
// DeleteScriptPolicyNoContent is response for DeleteScriptPolicy operation.
type DeleteScriptPolicyNoContent struct{}
type DownloadMapAssetOK struct {
Data io.Reader
}
// Read reads data from the Data reader.
//
// Kept to satisfy the io.Reader interface.
func (s DownloadMapAssetOK) Read(p []byte) (n int, err error) {
if s.Data == nil {
return 0, io.EOF
}
return s.Data.Read(p)
}
// Represents error object.
// Ref: #/components/schemas/Error
type Error struct {
@@ -573,6 +587,9 @@ func (s *Mapfixes) SetMapfixes(val []Mapfix) {
s.Mapfixes = val
}
// MigrateMapsOK is response for MigrateMaps operation.
type MigrateMapsOK struct{}
// Ref: #/components/schemas/Operation
type Operation struct {
OperationID int32 `json:"OperationID"`

View File

@@ -34,48 +34,50 @@ func findAuthorization(h http.Header, prefix string) (string, bool) {
}
var operationRolesCookieAuth = map[string][]string{
ActionMapfixAcceptedOperation: []string{},
ActionMapfixBypassSubmitOperation: []string{},
ActionMapfixRejectOperation: []string{},
ActionMapfixRequestChangesOperation: []string{},
ActionMapfixResetSubmittingOperation: []string{},
ActionMapfixRetryValidateOperation: []string{},
ActionMapfixRevokeOperation: []string{},
ActionMapfixTriggerSubmitOperation: []string{},
ActionMapfixTriggerUploadOperation: []string{},
ActionMapfixTriggerValidateOperation: []string{},
ActionMapfixValidatedOperation: []string{},
ActionSubmissionAcceptedOperation: []string{},
ActionSubmissionBypassSubmitOperation: []string{},
ActionSubmissionRejectOperation: []string{},
ActionSubmissionRequestChangesOperation: []string{},
ActionSubmissionResetSubmittingOperation: []string{},
ActionSubmissionRetryValidateOperation: []string{},
ActionSubmissionRevokeOperation: []string{},
ActionSubmissionTriggerSubmitOperation: []string{},
ActionSubmissionTriggerUploadOperation: []string{},
ActionSubmissionTriggerValidateOperation: []string{},
ActionSubmissionValidatedOperation: []string{},
CreateMapfixOperation: []string{},
CreateMapfixAuditCommentOperation: []string{},
CreateScriptOperation: []string{},
CreateScriptPolicyOperation: []string{},
CreateSubmissionOperation: []string{},
CreateSubmissionAdminOperation: []string{},
CreateSubmissionAuditCommentOperation: []string{},
DeleteScriptOperation: []string{},
DeleteScriptPolicyOperation: []string{},
GetOperationOperation: []string{},
ReleaseSubmissionsOperation: []string{},
SessionRolesOperation: []string{},
SessionUserOperation: []string{},
SessionValidateOperation: []string{},
SetMapfixCompletedOperation: []string{},
SetSubmissionCompletedOperation: []string{},
UpdateMapfixModelOperation: []string{},
UpdateScriptOperation: []string{},
UpdateScriptPolicyOperation: []string{},
UpdateSubmissionModelOperation: []string{},
ActionMapfixAcceptedOperation: []string{},
ActionMapfixRejectOperation: []string{},
ActionMapfixRequestChangesOperation: []string{},
ActionMapfixResetSubmittingOperation: []string{},
ActionMapfixRetryValidateOperation: []string{},
ActionMapfixRevokeOperation: []string{},
ActionMapfixTriggerSubmitOperation: []string{},
ActionMapfixTriggerSubmitUncheckedOperation: []string{},
ActionMapfixTriggerUploadOperation: []string{},
ActionMapfixTriggerValidateOperation: []string{},
ActionMapfixValidatedOperation: []string{},
ActionSubmissionAcceptedOperation: []string{},
ActionSubmissionRejectOperation: []string{},
ActionSubmissionRequestChangesOperation: []string{},
ActionSubmissionResetSubmittingOperation: []string{},
ActionSubmissionRetryValidateOperation: []string{},
ActionSubmissionRevokeOperation: []string{},
ActionSubmissionTriggerSubmitOperation: []string{},
ActionSubmissionTriggerSubmitUncheckedOperation: []string{},
ActionSubmissionTriggerUploadOperation: []string{},
ActionSubmissionTriggerValidateOperation: []string{},
ActionSubmissionValidatedOperation: []string{},
CreateMapfixOperation: []string{},
CreateMapfixAuditCommentOperation: []string{},
CreateScriptOperation: []string{},
CreateScriptPolicyOperation: []string{},
CreateSubmissionOperation: []string{},
CreateSubmissionAdminOperation: []string{},
CreateSubmissionAuditCommentOperation: []string{},
DeleteScriptOperation: []string{},
DeleteScriptPolicyOperation: []string{},
DownloadMapAssetOperation: []string{},
GetOperationOperation: []string{},
MigrateMapsOperation: []string{},
ReleaseSubmissionsOperation: []string{},
SessionRolesOperation: []string{},
SessionUserOperation: []string{},
SessionValidateOperation: []string{},
SetMapfixCompletedOperation: []string{},
SetSubmissionCompletedOperation: []string{},
UpdateMapfixModelOperation: []string{},
UpdateScriptOperation: []string{},
UpdateScriptPolicyOperation: []string{},
UpdateSubmissionModelOperation: []string{},
}
func (s *Server) securityCookieAuth(ctx context.Context, operationName OperationName, req *http.Request) (context.Context, bool, error) {

View File

@@ -14,12 +14,6 @@ type Handler interface {
//
// POST /mapfixes/{MapfixID}/status/reset-validating
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixBypassSubmit implements actionMapfixBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/bypass-submit
ActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) error
// ActionMapfixReject implements actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@@ -57,6 +51,12 @@ type Handler interface {
//
// POST /mapfixes/{MapfixID}/status/trigger-submit
ActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) error
// ActionMapfixTriggerSubmitUnchecked implements actionMapfixTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/trigger-submit-unchecked
ActionMapfixTriggerSubmitUnchecked(ctx context.Context, params ActionMapfixTriggerSubmitUncheckedParams) error
// ActionMapfixTriggerUpload implements actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@@ -81,12 +81,6 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/reset-validating
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionBypassSubmit implements actionSubmissionBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/bypass-submit
ActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) error
// ActionSubmissionReject implements actionSubmissionReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@@ -124,6 +118,12 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/trigger-submit
ActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) error
// ActionSubmissionTriggerSubmitUnchecked implements actionSubmissionTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/trigger-submit-unchecked
ActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params ActionSubmissionTriggerSubmitUncheckedParams) error
// ActionSubmissionTriggerUpload implements actionSubmissionTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@@ -196,6 +196,12 @@ type Handler interface {
//
// DELETE /script-policy/{ScriptPolicyID}
DeleteScriptPolicy(ctx context.Context, params DeleteScriptPolicyParams) error
// DownloadMapAsset implements downloadMapAsset operation.
//
// Download the map asset.
//
// GET /maps/{MapID}/download
DownloadMapAsset(ctx context.Context, params DownloadMapAssetParams) (DownloadMapAssetOK, error)
// GetMap implements getMap operation.
//
// Retrieve map with ID.
@@ -274,6 +280,12 @@ type Handler interface {
//
// GET /submissions
ListSubmissions(ctx context.Context, params ListSubmissionsParams) (*Submissions, error)
// MigrateMaps implements migrateMaps operation.
//
// Perform maps migration.
//
// POST /migrate-maps
MigrateMaps(ctx context.Context) error
// ReleaseSubmissions implements releaseSubmissions operation.
//
// Release a set of uploaded maps.

View File

@@ -22,15 +22,6 @@ func (UnimplementedHandler) ActionMapfixAccepted(ctx context.Context, params Act
return ht.ErrNotImplemented
}
// ActionMapfixBypassSubmit implements actionMapfixBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/bypass-submit
func (UnimplementedHandler) ActionMapfixBypassSubmit(ctx context.Context, params ActionMapfixBypassSubmitParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixReject implements actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@@ -86,6 +77,15 @@ func (UnimplementedHandler) ActionMapfixTriggerSubmit(ctx context.Context, param
return ht.ErrNotImplemented
}
// ActionMapfixTriggerSubmitUnchecked implements actionMapfixTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/trigger-submit-unchecked
func (UnimplementedHandler) ActionMapfixTriggerSubmitUnchecked(ctx context.Context, params ActionMapfixTriggerSubmitUncheckedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixTriggerUpload implements actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@@ -122,15 +122,6 @@ func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params
return ht.ErrNotImplemented
}
// ActionSubmissionBypassSubmit implements actionSubmissionBypassSubmit operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/bypass-submit
func (UnimplementedHandler) ActionSubmissionBypassSubmit(ctx context.Context, params ActionSubmissionBypassSubmitParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionReject implements actionSubmissionReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
@@ -186,6 +177,15 @@ func (UnimplementedHandler) ActionSubmissionTriggerSubmit(ctx context.Context, p
return ht.ErrNotImplemented
}
// ActionSubmissionTriggerSubmitUnchecked implements actionSubmissionTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/trigger-submit-unchecked
func (UnimplementedHandler) ActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params ActionSubmissionTriggerSubmitUncheckedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionTriggerUpload implements actionSubmissionTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@@ -294,6 +294,15 @@ func (UnimplementedHandler) DeleteScriptPolicy(ctx context.Context, params Delet
return ht.ErrNotImplemented
}
// DownloadMapAsset implements downloadMapAsset operation.
//
// Download the map asset.
//
// GET /maps/{MapID}/download
func (UnimplementedHandler) DownloadMapAsset(ctx context.Context, params DownloadMapAssetParams) (r DownloadMapAssetOK, _ error) {
return r, ht.ErrNotImplemented
}
// GetMap implements getMap operation.
//
// Retrieve map with ID.
@@ -411,6 +420,15 @@ func (UnimplementedHandler) ListSubmissions(ctx context.Context, params ListSubm
return r, ht.ErrNotImplemented
}
// MigrateMaps implements migrateMaps operation.
//
// Perform maps migration.
//
// POST /migrate-maps
func (UnimplementedHandler) MigrateMaps(ctx context.Context) error {
return ht.ErrNotImplemented
}
// ReleaseSubmissions implements releaseSubmissions operation.
//
// Release a set of uploaded maps.

View File

@@ -2,16 +2,21 @@ package cmds
import (
"fmt"
"net"
"net/http"
"git.itzana.me/strafesnet/go-grpc/auth"
"git.itzana.me/strafesnet/go-grpc/maps"
"git.itzana.me/strafesnet/go-grpc/maps_extended"
"git.itzana.me/strafesnet/go-grpc/users"
"git.itzana.me/strafesnet/go-grpc/validator"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/controller"
"git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/roblox"
"git.itzana.me/strafesnet/maps-service/pkg/service"
"git.itzana.me/strafesnet/maps-service/pkg/service_internal"
"git.itzana.me/strafesnet/maps-service/pkg/validator_controller"
"git.itzana.me/strafesnet/maps-service/pkg/web_api"
"github.com/nats-io/nats.go"
log "github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
@@ -91,6 +96,12 @@ func NewServeCommand() *cli.Command {
EnvVars: []string{"NATS_HOST"},
Value: "nats:4222",
},
&cli.StringFlag{
Name: "rbx-api-key",
Usage: "API Key for downloading asset locations",
EnvVars: []string{"RBX_API_KEY"},
Required: true,
},
},
}
}
@@ -123,46 +134,67 @@ func serve(ctx *cli.Context) error {
if err != nil {
log.Fatal(err)
}
svc := &service.Service{
DB: db,
Nats: js,
Maps: maps.NewMapsServiceClient(conn),
Users: users.NewUsersServiceClient(conn),
}
svc_inner := service.NewService(
db,
js,
maps.NewMapsServiceClient(conn),
users.NewUsersServiceClient(conn),
)
svc_external := web_api.NewService(
&svc_inner,
roblox.Client{
HttpClient: http.DefaultClient,
ApiKey: ctx.String("rbx-api-key"),
},
)
conn, err = grpc.Dial(ctx.String("auth-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
sec := service.SecurityHandler{
sec := web_api.SecurityHandler{
Client: auth.NewAuthServiceClient(conn),
}
srv, err := api.NewServer(svc, sec, api.WithPathPrefix("/v1"))
srv_external, err := api.NewServer(&svc_external, sec, api.WithPathPrefix("/v1"))
if err != nil {
log.WithError(err).Fatal("failed to initialize api server")
}
svc2 := &service_internal.Service{
DB: db,
Nats: js,
}
grpcServer := grpc.NewServer()
srv2, err := internal.NewServer(svc2, internal.WithPathPrefix("/v1"))
maps_controller := controller.NewMapsController(&svc_inner)
maps_extended.RegisterMapsServiceServer(grpcServer,&maps_controller)
mapfix_controller := validator_controller.NewMapfixesController(&svc_inner)
operation_controller := validator_controller.NewOperationsController(&svc_inner)
script_controller := validator_controller.NewScriptsController(&svc_inner)
script_policy_controller := validator_controller.NewScriptPolicyController(&svc_inner)
submission_controller := validator_controller.NewSubmissionsController(&svc_inner)
validator.RegisterValidatorMapfixServiceServer(grpcServer,&mapfix_controller)
validator.RegisterValidatorOperationServiceServer(grpcServer,&operation_controller)
validator.RegisterValidatorScriptServiceServer(grpcServer,&script_controller)
validator.RegisterValidatorScriptPolicyServiceServer(grpcServer,&script_policy_controller)
validator.RegisterValidatorSubmissionServiceServer(grpcServer,&submission_controller)
port := ctx.Int("port-internal")
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
log.WithError(err).Fatal("failed to initialize api server")
log.WithField("error", err).Fatalln("failed to net.Listen")
}
// Channel to collect errors
errChan := make(chan error, 2)
// First server
go func(errChan chan error) {
errChan <- http.ListenAndServe(fmt.Sprintf(":%d", ctx.Int("port-internal")), srv2)
errChan <- grpcServer.Serve(lis)
}(errChan)
// Second server
go func(errChan chan error) {
errChan <- http.ListenAndServe(fmt.Sprintf(":%d", ctx.Int("port")), srv)
errChan <- http.ListenAndServe(fmt.Sprintf(":%d", ctx.Int("port")), srv_external)
}(errChan)
// Wait for the first error or completion of both tasks

197
pkg/controller/maps.go Normal file
View File

@@ -0,0 +1,197 @@
package controller
import (
"context"
"errors"
"time"
"git.itzana.me/strafesnet/go-grpc/maps_extended"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"git.itzana.me/strafesnet/maps-service/pkg/service"
)
var (
PageError = errors.New("Pagination required")
)
type Maps struct {
*maps_extended.UnimplementedMapsServiceServer
inner *service.Service
}
func NewMapsController(
inner *service.Service,
) Maps {
return Maps{
inner: inner,
}
}
func (svc *Maps) Create(ctx context.Context, request *maps_extended.MapCreate) (*maps_extended.MapId, error) {
id, err := svc.inner.CreateMap(ctx, model.Map{
ID: request.ID,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: request.GameID,
Submitter: request.Submitter,
Date: time.Unix(request.Date, 0),
Thumbnail: request.Thumbnail,
AssetVersion: request.AssetVersion,
LoadCount: 0,
Modes: request.Modes,
})
if err != nil {
return nil, err
}
return &maps_extended.MapId{
ID: id,
}, nil
}
func (svc *Maps) Delete(ctx context.Context, request *maps_extended.MapId) (*maps_extended.NullResponse, error) {
err := svc.inner.DeleteMap(ctx, request.ID)
if err != nil {
return nil, err
}
return &maps_extended.NullResponse{}, nil
}
func (svc *Maps) Get(ctx context.Context, request *maps_extended.MapId) (*maps_extended.MapResponse, error) {
item, err := svc.inner.GetMap(ctx, request.ID)
if err != nil {
return nil, err
}
return &maps_extended.MapResponse{
ID: item.ID,
DisplayName: item.DisplayName,
Creator: item.Creator,
GameID: uint32(item.GameID),
Date: item.Date.Unix(),
CreatedAt: item.CreatedAt.Unix(),
UpdatedAt: item.UpdatedAt.Unix(),
Submitter: uint64(item.Submitter),
Thumbnail: uint64(item.Thumbnail),
AssetVersion: uint64(item.AssetVersion),
LoadCount: item.LoadCount,
Modes: item.Modes,
}, nil
}
func (svc *Maps) GetList(ctx context.Context, request *maps_extended.MapIdList) (*maps_extended.MapList, error) {
items, err := svc.inner.GetMapList(ctx, request.ID)
if err != nil {
return nil, err
}
resp := maps_extended.MapList{}
resp.Maps = make([]*maps_extended.MapResponse, len(items))
for i, item := range items {
resp.Maps[i] = &maps_extended.MapResponse{
ID: item.ID,
DisplayName: item.DisplayName,
Creator: item.Creator,
GameID: uint32(item.GameID),
Date: item.Date.Unix(),
CreatedAt: item.CreatedAt.Unix(),
UpdatedAt: item.UpdatedAt.Unix(),
Submitter: uint64(item.Submitter),
Thumbnail: uint64(item.Thumbnail),
AssetVersion: uint64(item.AssetVersion),
LoadCount: item.LoadCount,
Modes: item.Modes,
}
}
return &resp, nil
}
func (svc *Maps) List(ctx context.Context, request *maps_extended.ListRequest) (*maps_extended.MapList, error) {
if request.Page == nil {
return nil, PageError
}
filter := service.NewMapFilter()
if request.Filter != nil {
if request.Filter.DisplayName != nil {
filter.SetDisplayName(*request.Filter.DisplayName)
}
if request.Filter.Creator != nil {
filter.SetCreator(*request.Filter.Creator)
}
if request.Filter.GameID != nil {
filter.SetGameID(*request.Filter.GameID)
}
if request.Filter.Submitter != nil {
filter.SetSubmitter(*request.Filter.Submitter)
}
}
items, err := svc.inner.ListMaps(ctx, filter, model.Page{
Number: int32(request.Page.Number),
Size: int32(request.Page.Size),
})
if err != nil {
return nil, err
}
resp := maps_extended.MapList{}
resp.Maps = make([]*maps_extended.MapResponse, len(items))
for i, item := range items {
resp.Maps[i] = &maps_extended.MapResponse{
ID: item.ID,
DisplayName: item.DisplayName,
Creator: item.Creator,
GameID: uint32(item.GameID),
Date: item.Date.Unix(),
CreatedAt: item.CreatedAt.Unix(),
UpdatedAt: item.UpdatedAt.Unix(),
Submitter: uint64(item.Submitter),
Thumbnail: uint64(item.Thumbnail),
AssetVersion: uint64(item.AssetVersion),
LoadCount: item.LoadCount,
Modes: item.Modes,
}
}
return &resp, nil
}
func (svc *Maps) Update(ctx context.Context, request *maps_extended.MapUpdate) (*maps_extended.NullResponse, error) {
update := service.NewMapUpdate()
if request.DisplayName != nil {
update.SetDisplayName(*request.DisplayName)
}
if request.Creator != nil {
update.SetCreator(*request.Creator)
}
if request.GameID != nil {
update.SetGameID(*request.GameID)
}
if request.Date != nil {
update.SetDate(*request.Date)
}
if request.Submitter != nil {
update.SetSubmitter(*request.Submitter)
}
if request.Thumbnail != nil {
update.SetThumbnail(*request.Thumbnail)
}
if request.AssetVersion != nil {
update.SetAssetVersion(*request.AssetVersion)
}
if request.Modes != nil {
update.SetModes(*request.Modes)
}
err := svc.inner.UpdateMap(ctx, request.ID, update)
if err != nil {
return nil, err
}
return &maps_extended.NullResponse{}, nil
}
func (svc *Maps) IncrementLoadCount(ctx context.Context, request *maps_extended.MapId) (*maps_extended.NullResponse, error) {
err := svc.inner.IncrementMapLoadCount(ctx, request.ID)
if err != nil {
return nil, err
}
return &maps_extended.NullResponse{}, nil
}

View File

@@ -25,6 +25,7 @@ const (
type Datastore interface {
AuditEvents() AuditEvents
Maps() Maps
Mapfixes() Mapfixes
Operations() Operations
Submissions() Submissions
@@ -40,6 +41,16 @@ type AuditEvents interface {
List(ctx context.Context, filters OptionalMap, page model.Page) ([]model.AuditEvent, error)
}
type Maps interface {
Get(ctx context.Context, id int64) (model.Map, error)
GetList(ctx context.Context, id []int64) ([]model.Map, error)
Create(ctx context.Context, smap model.Map) (model.Map, error)
Update(ctx context.Context, id int64, values OptionalMap) error
Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page) ([]model.Map, error)
IncrementLoadCount(ctx context.Context, id int64) error
}
type Mapfixes interface {
Get(ctx context.Context, id int64) (model.Mapfix, error)
GetList(ctx context.Context, id []int64) ([]model.Mapfix, error)

View File

@@ -56,7 +56,7 @@ func (env *AuditEvents) Delete(ctx context.Context, id int64) error {
func (env *AuditEvents) List(ctx context.Context, filters datastore.OptionalMap, page model.Page) ([]model.AuditEvent, error) {
var events []model.AuditEvent
if err := env.db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&events).Error; err != nil {
if err := env.db.Where(filters.Map()).Order("id ASC").Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&events).Error; err != nil {
return nil, err
}

View File

@@ -32,6 +32,7 @@ func New(ctx *cli.Context) (datastore.Datastore, error) {
if ctx.Bool("migrate") {
if err := db.AutoMigrate(
&model.AuditEvent{},
&model.Map{},
&model.Mapfix{},
&model.Operation{},
&model.Submission{},

View File

@@ -13,6 +13,10 @@ func (g Gormstore) AuditEvents() datastore.AuditEvents {
return &AuditEvents{db: g.db}
}
func (g Gormstore) Maps() datastore.Maps {
return &Maps{db: g.db}
}
func (g Gormstore) Mapfixes() datastore.Mapfixes {
return &Mapfixes{db: g.db}
}

View File

@@ -55,11 +55,16 @@ func (env *Mapfixes) Update(ctx context.Context, id int64, values datastore.Opti
// the update can only occur if the status matches one of the provided values.
func (env *Mapfixes) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.MapfixStatus, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Mapfix{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
result := env.db.Model(&model.Mapfix{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map())
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
return result.Error
}
if result.RowsAffected == 0 {
return datastore.ErroNoRowsAffected
}
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 Maps struct {
db *gorm.DB
}
func (env *Maps) Get(ctx context.Context, id int64) (model.Map, error) {
var mdl model.Map
if err := env.db.First(&mdl, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return mdl, datastore.ErrNotExist
}
return mdl, err
}
return mdl, nil
}
func (env *Maps) GetList(ctx context.Context, id []int64) ([]model.Map, error) {
var mapList []model.Map
if err := env.db.Find(&mapList, "id IN ?", id).Error; err != nil {
return mapList, err
}
return mapList, nil
}
func (env *Maps) Create(ctx context.Context, smap model.Map) (model.Map, error) {
if err := env.db.Create(&smap).Error; err != nil {
return smap, err
}
return smap, nil
}
func (env *Maps) Update(ctx context.Context, id int64, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Map{}).Where("id = ?", id).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *Maps) IncrementLoadCount(ctx context.Context, id int64) error {
if err := env.db.Model(&model.Map{}).Where("id = ?", id).UpdateColumn("load_count", gorm.Expr("load_count + ?", 1)).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *Maps) Delete(ctx context.Context, id int64) error {
if err := env.db.Delete(&model.Map{}, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *Maps) List(ctx context.Context, filters datastore.OptionalMap, page model.Page) ([]model.Map, error) {
var events []model.Map
if err := env.db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&events).Error; err != nil {
return nil, err
}
return events, nil
}

View File

@@ -55,11 +55,16 @@ func (env *Submissions) Update(ctx context.Context, id int64, values datastore.O
// the update can only occur if the status matches one of the provided values.
func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.SubmissionStatus, values datastore.OptionalMap) error {
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 {
result := env.db.Model(&model.Submission{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map())
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
return result.Error
}
if result.RowsAffected == 0 {
return datastore.ErroNoRowsAffected
}
return nil

View File

@@ -1,283 +0,0 @@
// Code generated by ogen, DO NOT EDIT.
package api
import (
"net/http"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/trace"
ht "github.com/ogen-go/ogen/http"
"github.com/ogen-go/ogen/middleware"
"github.com/ogen-go/ogen/ogenerrors"
"github.com/ogen-go/ogen/otelogen"
)
var (
// Allocate option closure once.
clientSpanKind = trace.WithSpanKind(trace.SpanKindClient)
// Allocate option closure once.
serverSpanKind = trace.WithSpanKind(trace.SpanKindServer)
)
type (
optionFunc[C any] func(*C)
otelOptionFunc func(*otelConfig)
)
type otelConfig struct {
TracerProvider trace.TracerProvider
Tracer trace.Tracer
MeterProvider metric.MeterProvider
Meter metric.Meter
}
func (cfg *otelConfig) initOTEL() {
if cfg.TracerProvider == nil {
cfg.TracerProvider = otel.GetTracerProvider()
}
if cfg.MeterProvider == nil {
cfg.MeterProvider = otel.GetMeterProvider()
}
cfg.Tracer = cfg.TracerProvider.Tracer(otelogen.Name,
trace.WithInstrumentationVersion(otelogen.SemVersion()),
)
cfg.Meter = cfg.MeterProvider.Meter(otelogen.Name,
metric.WithInstrumentationVersion(otelogen.SemVersion()),
)
}
// ErrorHandler is error handler.
type ErrorHandler = ogenerrors.ErrorHandler
type serverConfig struct {
otelConfig
NotFound http.HandlerFunc
MethodNotAllowed func(w http.ResponseWriter, r *http.Request, allowed string)
ErrorHandler ErrorHandler
Prefix string
Middleware Middleware
MaxMultipartMemory int64
}
// ServerOption is server config option.
type ServerOption interface {
applyServer(*serverConfig)
}
var _ ServerOption = (optionFunc[serverConfig])(nil)
func (o optionFunc[C]) applyServer(c *C) {
o(c)
}
var _ ServerOption = (otelOptionFunc)(nil)
func (o otelOptionFunc) applyServer(c *serverConfig) {
o(&c.otelConfig)
}
func newServerConfig(opts ...ServerOption) serverConfig {
cfg := serverConfig{
NotFound: http.NotFound,
MethodNotAllowed: func(w http.ResponseWriter, r *http.Request, allowed string) {
status := http.StatusMethodNotAllowed
if r.Method == "OPTIONS" {
w.Header().Set("Access-Control-Allow-Methods", allowed)
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
status = http.StatusNoContent
} else {
w.Header().Set("Allow", allowed)
}
w.WriteHeader(status)
},
ErrorHandler: ogenerrors.DefaultErrorHandler,
Middleware: nil,
MaxMultipartMemory: 32 << 20, // 32 MB
}
for _, opt := range opts {
opt.applyServer(&cfg)
}
cfg.initOTEL()
return cfg
}
type baseServer struct {
cfg serverConfig
requests metric.Int64Counter
errors metric.Int64Counter
duration metric.Float64Histogram
}
func (s baseServer) notFound(w http.ResponseWriter, r *http.Request) {
s.cfg.NotFound(w, r)
}
func (s baseServer) notAllowed(w http.ResponseWriter, r *http.Request, allowed string) {
s.cfg.MethodNotAllowed(w, r, allowed)
}
func (cfg serverConfig) baseServer() (s baseServer, err error) {
s = baseServer{cfg: cfg}
if s.requests, err = otelogen.ServerRequestCountCounter(s.cfg.Meter); err != nil {
return s, err
}
if s.errors, err = otelogen.ServerErrorsCountCounter(s.cfg.Meter); err != nil {
return s, err
}
if s.duration, err = otelogen.ServerDurationHistogram(s.cfg.Meter); err != nil {
return s, err
}
return s, nil
}
type clientConfig struct {
otelConfig
Client ht.Client
}
// ClientOption is client config option.
type ClientOption interface {
applyClient(*clientConfig)
}
var _ ClientOption = (optionFunc[clientConfig])(nil)
func (o optionFunc[C]) applyClient(c *C) {
o(c)
}
var _ ClientOption = (otelOptionFunc)(nil)
func (o otelOptionFunc) applyClient(c *clientConfig) {
o(&c.otelConfig)
}
func newClientConfig(opts ...ClientOption) clientConfig {
cfg := clientConfig{
Client: http.DefaultClient,
}
for _, opt := range opts {
opt.applyClient(&cfg)
}
cfg.initOTEL()
return cfg
}
type baseClient struct {
cfg clientConfig
requests metric.Int64Counter
errors metric.Int64Counter
duration metric.Float64Histogram
}
func (cfg clientConfig) baseClient() (c baseClient, err error) {
c = baseClient{cfg: cfg}
if c.requests, err = otelogen.ClientRequestCountCounter(c.cfg.Meter); err != nil {
return c, err
}
if c.errors, err = otelogen.ClientErrorsCountCounter(c.cfg.Meter); err != nil {
return c, err
}
if c.duration, err = otelogen.ClientDurationHistogram(c.cfg.Meter); err != nil {
return c, err
}
return c, nil
}
// Option is config option.
type Option interface {
ServerOption
ClientOption
}
// WithTracerProvider specifies a tracer provider to use for creating a tracer.
//
// If none is specified, the global provider is used.
func WithTracerProvider(provider trace.TracerProvider) Option {
return otelOptionFunc(func(cfg *otelConfig) {
if provider != nil {
cfg.TracerProvider = provider
}
})
}
// WithMeterProvider specifies a meter provider to use for creating a meter.
//
// If none is specified, the otel.GetMeterProvider() is used.
func WithMeterProvider(provider metric.MeterProvider) Option {
return otelOptionFunc(func(cfg *otelConfig) {
if provider != nil {
cfg.MeterProvider = provider
}
})
}
// WithClient specifies http client to use.
func WithClient(client ht.Client) ClientOption {
return optionFunc[clientConfig](func(cfg *clientConfig) {
if client != nil {
cfg.Client = client
}
})
}
// WithNotFound specifies Not Found handler to use.
func WithNotFound(notFound http.HandlerFunc) ServerOption {
return optionFunc[serverConfig](func(cfg *serverConfig) {
if notFound != nil {
cfg.NotFound = notFound
}
})
}
// WithMethodNotAllowed specifies Method Not Allowed handler to use.
func WithMethodNotAllowed(methodNotAllowed func(w http.ResponseWriter, r *http.Request, allowed string)) ServerOption {
return optionFunc[serverConfig](func(cfg *serverConfig) {
if methodNotAllowed != nil {
cfg.MethodNotAllowed = methodNotAllowed
}
})
}
// WithErrorHandler specifies error handler to use.
func WithErrorHandler(h ErrorHandler) ServerOption {
return optionFunc[serverConfig](func(cfg *serverConfig) {
if h != nil {
cfg.ErrorHandler = h
}
})
}
// WithPathPrefix specifies server path prefix.
func WithPathPrefix(prefix string) ServerOption {
return optionFunc[serverConfig](func(cfg *serverConfig) {
cfg.Prefix = prefix
})
}
// WithMiddleware specifies middlewares to use.
func WithMiddleware(m ...Middleware) ServerOption {
return optionFunc[serverConfig](func(cfg *serverConfig) {
switch len(m) {
case 0:
cfg.Middleware = nil
case 1:
cfg.Middleware = m[0]
default:
cfg.Middleware = middleware.ChainMiddlewares(m...)
}
})
}
// WithMaxMultipartMemory specifies limit of memory for storing file parts.
// File parts which can't be stored in memory will be stored on disk in temporary files.
func WithMaxMultipartMemory(max int64) ServerOption {
return optionFunc[serverConfig](func(cfg *serverConfig) {
if max > 0 {
cfg.MaxMultipartMemory = max
}
})
}

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

@@ -1,42 +0,0 @@
// Code generated by ogen, DO NOT EDIT.
package api
import (
"context"
"go.opentelemetry.io/otel/attribute"
)
// Labeler is used to allow adding custom attributes to the server request metrics.
type Labeler struct {
attrs []attribute.KeyValue
}
// Add attributes to the Labeler.
func (l *Labeler) Add(attrs ...attribute.KeyValue) {
l.attrs = append(l.attrs, attrs...)
}
// AttributeSet returns the attributes added to the Labeler as an attribute.Set.
func (l *Labeler) AttributeSet() attribute.Set {
return attribute.NewSet(l.attrs...)
}
type labelerContextKey struct{}
// LabelerFromContext retrieves the Labeler from the provided context, if present.
//
// If no Labeler was found in the provided context a new, empty Labeler is returned and the second
// return value is false. In this case it is safe to use the Labeler but any attributes added to
// it will not be used.
func LabelerFromContext(ctx context.Context) (*Labeler, bool) {
if l, ok := ctx.Value(labelerContextKey{}).(*Labeler); ok {
return l, true
}
return &Labeler{}, false
}
func contextWithLabeler(ctx context.Context, l *Labeler) context.Context {
return context.WithValue(ctx, labelerContextKey{}, l)
}

View File

@@ -1,10 +0,0 @@
// Code generated by ogen, DO NOT EDIT.
package api
import (
"github.com/ogen-go/ogen/middleware"
)
// Middleware is middleware type.
type Middleware = middleware.Middleware

View File

@@ -1,29 +0,0 @@
// Code generated by ogen, DO NOT EDIT.
package api
// OperationName is the ogen operation name
type OperationName = string
const (
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges"
ActionMapfixSubmittedOperation OperationName = "ActionMapfixSubmitted"
ActionMapfixUploadedOperation OperationName = "ActionMapfixUploaded"
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
ActionOperationFailedOperation OperationName = "ActionOperationFailed"
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
ActionSubmissionSubmittedOperation OperationName = "ActionSubmissionSubmitted"
ActionSubmissionUploadedOperation OperationName = "ActionSubmissionUploaded"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateMapfixOperation OperationName = "CreateMapfix"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
GetScriptOperation OperationName = "GetScript"
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"
UpdateMapfixValidatedModelOperation OperationName = "UpdateMapfixValidatedModel"
UpdateSubmissionValidatedModelOperation OperationName = "UpdateSubmissionValidatedModel"
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,299 +0,0 @@
// Code generated by ogen, DO NOT EDIT.
package api
import (
"io"
"mime"
"net/http"
"github.com/go-faster/errors"
"github.com/go-faster/jx"
"github.com/ogen-go/ogen/ogenerrors"
"github.com/ogen-go/ogen/validate"
)
func (s *Server) decodeCreateMapfixRequest(r *http.Request) (
req *MapfixCreate,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
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 MapfixCreate
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) decodeCreateScriptRequest(r *http.Request) (
req *ScriptCreate,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
if r.ContentLength == 0 {
return req, close, validate.ErrBodyRequired
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return req, close, err
}
if len(buf) == 0 {
return req, close, validate.ErrBodyRequired
}
d := jx.DecodeBytes(buf)
var request ScriptCreate
if err := func() error {
if err := request.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
req *ScriptPolicyCreate,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
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
}
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) 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 = errors.Join(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
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)
}
}

View File

@@ -1,68 +0,0 @@
// Code generated by ogen, DO NOT EDIT.
package api
import (
"bytes"
"net/http"
"github.com/go-faster/jx"
ht "github.com/ogen-go/ogen/http"
)
func encodeCreateMapfixRequest(
req *MapfixCreate,
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 encodeCreateScriptRequest(
req *ScriptCreate,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeCreateScriptPolicyRequest(
req *ScriptPolicyCreate,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeCreateSubmissionRequest(
req *SubmissionCreate,
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
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,238 +0,0 @@
// Code generated by ogen, DO NOT EDIT.
package api
import (
"net/http"
"github.com/go-faster/errors"
"github.com/go-faster/jx"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
ht "github.com/ogen-go/ogen/http"
)
func encodeActionMapfixAcceptedResponse(response *ActionMapfixAcceptedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixRequestChangesResponse(response *ActionMapfixRequestChangesNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixSubmittedResponse(response *ActionMapfixSubmittedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixUploadedResponse(response *ActionMapfixUploadedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixValidatedResponse(response *ActionMapfixValidatedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionOperationFailedResponse(response *ActionOperationFailedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionRequestChangesResponse(response *ActionSubmissionRequestChangesNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionSubmittedResponse(response *ActionSubmissionSubmittedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionUploadedResponse(response *ActionSubmissionUploadedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionValidatedResponse(response *ActionSubmissionValidatedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeCreateMapfixResponse(response *MapfixID, 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 encodeCreateScriptResponse(response *ScriptID, 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 *ScriptPolicyID, 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 encodeCreateSubmissionResponse(response *SubmissionID, 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 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 encodeListScriptPolicyResponse(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)
e.ArrStart()
for _, elem := range response {
elem.Encode(e)
}
e.ArrEnd()
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeListScriptsResponse(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)
e.ArrStart()
for _, elem := range response {
elem.Encode(e)
}
e.ArrEnd()
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeUpdateMapfixValidatedModelResponse(response *UpdateMapfixValidatedModelNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeUpdateSubmissionValidatedModelResponse(response *UpdateSubmissionValidatedModelNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeErrorResponse(response *ErrorStatusCode, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
code := response.StatusCode
if code == 0 {
// Set default status code.
code = http.StatusOK
}
w.WriteHeader(code)
if st := http.StatusText(code); code >= http.StatusBadRequest {
span.SetStatus(codes.Error, st)
} else {
span.SetStatus(codes.Ok, st)
}
e := new(jx.Encoder)
response.Response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
if code >= http.StatusInternalServerError {
return errors.Wrapf(ht.ErrInternalServerErrorResponse, "code: %d, message: %s", code, http.StatusText(code))
}
return nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,710 +0,0 @@
// Code generated by ogen, DO NOT EDIT.
package api
import (
"fmt"
)
func (s *ErrorStatusCode) Error() string {
return fmt.Sprintf("code %d: %+v", s.StatusCode, s.Response)
}
// ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation.
type ActionMapfixAcceptedNoContent struct{}
// ActionMapfixRequestChangesNoContent is response for ActionMapfixRequestChanges operation.
type ActionMapfixRequestChangesNoContent struct{}
// ActionMapfixSubmittedNoContent is response for ActionMapfixSubmitted operation.
type ActionMapfixSubmittedNoContent struct{}
// ActionMapfixUploadedNoContent is response for ActionMapfixUploaded operation.
type ActionMapfixUploadedNoContent struct{}
// ActionMapfixValidatedNoContent is response for ActionMapfixValidated operation.
type ActionMapfixValidatedNoContent struct{}
// ActionOperationFailedNoContent is response for ActionOperationFailed operation.
type ActionOperationFailedNoContent struct{}
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
type ActionSubmissionAcceptedNoContent struct{}
// ActionSubmissionRequestChangesNoContent is response for ActionSubmissionRequestChanges operation.
type ActionSubmissionRequestChangesNoContent struct{}
// ActionSubmissionSubmittedNoContent is response for ActionSubmissionSubmitted operation.
type ActionSubmissionSubmittedNoContent struct{}
// ActionSubmissionUploadedNoContent is response for ActionSubmissionUploaded operation.
type ActionSubmissionUploadedNoContent struct{}
// ActionSubmissionValidatedNoContent is response for ActionSubmissionValidated operation.
type ActionSubmissionValidatedNoContent struct{}
// Represents error object.
// Ref: #/components/schemas/Error
type Error struct {
Code int64 `json:"code"`
Message string `json:"message"`
}
// GetCode returns the value of Code.
func (s *Error) GetCode() int64 {
return s.Code
}
// GetMessage returns the value of Message.
func (s *Error) GetMessage() string {
return s.Message
}
// SetCode sets the value of Code.
func (s *Error) SetCode(val int64) {
s.Code = val
}
// SetMessage sets the value of Message.
func (s *Error) SetMessage(val string) {
s.Message = val
}
// ErrorStatusCode wraps Error with StatusCode.
type ErrorStatusCode struct {
StatusCode int
Response Error
}
// GetStatusCode returns the value of StatusCode.
func (s *ErrorStatusCode) GetStatusCode() int {
return s.StatusCode
}
// GetResponse returns the value of Response.
func (s *ErrorStatusCode) GetResponse() Error {
return s.Response
}
// SetStatusCode sets the value of StatusCode.
func (s *ErrorStatusCode) SetStatusCode(val int) {
s.StatusCode = val
}
// SetResponse sets the value of Response.
func (s *ErrorStatusCode) SetResponse(val Error) {
s.Response = val
}
// Ref: #/components/schemas/MapfixCreate
type MapfixCreate struct {
OperationID int32 `json:"OperationID"`
AssetOwner int64 `json:"AssetOwner"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
TargetAssetID int64 `json:"TargetAssetID"`
Description string `json:"Description"`
}
// GetOperationID returns the value of OperationID.
func (s *MapfixCreate) GetOperationID() int32 {
return s.OperationID
}
// GetAssetOwner returns the value of AssetOwner.
func (s *MapfixCreate) GetAssetOwner() int64 {
return s.AssetOwner
}
// GetDisplayName returns the value of DisplayName.
func (s *MapfixCreate) GetDisplayName() string {
return s.DisplayName
}
// GetCreator returns the value of Creator.
func (s *MapfixCreate) GetCreator() string {
return s.Creator
}
// GetGameID returns the value of GameID.
func (s *MapfixCreate) GetGameID() int32 {
return s.GameID
}
// GetAssetID returns the value of AssetID.
func (s *MapfixCreate) GetAssetID() int64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *MapfixCreate) GetAssetVersion() int64 {
return s.AssetVersion
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *MapfixCreate) GetTargetAssetID() int64 {
return s.TargetAssetID
}
// GetDescription returns the value of Description.
func (s *MapfixCreate) GetDescription() string {
return s.Description
}
// SetOperationID sets the value of OperationID.
func (s *MapfixCreate) SetOperationID(val int32) {
s.OperationID = val
}
// SetAssetOwner sets the value of AssetOwner.
func (s *MapfixCreate) SetAssetOwner(val int64) {
s.AssetOwner = val
}
// SetDisplayName sets the value of DisplayName.
func (s *MapfixCreate) SetDisplayName(val string) {
s.DisplayName = val
}
// SetCreator sets the value of Creator.
func (s *MapfixCreate) SetCreator(val string) {
s.Creator = val
}
// SetGameID sets the value of GameID.
func (s *MapfixCreate) SetGameID(val int32) {
s.GameID = val
}
// SetAssetID sets the value of AssetID.
func (s *MapfixCreate) SetAssetID(val int64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *MapfixCreate) SetAssetVersion(val int64) {
s.AssetVersion = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *MapfixCreate) SetTargetAssetID(val int64) {
s.TargetAssetID = val
}
// SetDescription sets the value of Description.
func (s *MapfixCreate) SetDescription(val string) {
s.Description = val
}
// Ref: #/components/schemas/MapfixID
type MapfixID struct {
MapfixID int64 `json:"MapfixID"`
}
// GetMapfixID returns the value of MapfixID.
func (s *MapfixID) GetMapfixID() int64 {
return s.MapfixID
}
// SetMapfixID sets the value of MapfixID.
func (s *MapfixID) SetMapfixID(val int64) {
s.MapfixID = val
}
// NewOptInt32 returns new OptInt32 with value set to v.
func NewOptInt32(v int32) OptInt32 {
return OptInt32{
Value: v,
Set: true,
}
}
// OptInt32 is optional int32.
type OptInt32 struct {
Value int32
Set bool
}
// IsSet returns true if OptInt32 was set.
func (o OptInt32) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptInt32) Reset() {
var v int32
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptInt32) SetTo(v int32) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptInt32) Get() (v int32, 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 OptInt32) Or(d int32) int32 {
if v, ok := o.Get(); ok {
return v
}
return d
}
// NewOptInt64 returns new OptInt64 with value set to v.
func NewOptInt64(v int64) OptInt64 {
return OptInt64{
Value: v,
Set: true,
}
}
// OptInt64 is optional int64.
type OptInt64 struct {
Value int64
Set bool
}
// IsSet returns true if OptInt64 was set.
func (o OptInt64) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptInt64) Reset() {
var v int64
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptInt64) SetTo(v int64) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptInt64) Get() (v int64, 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 OptInt64) Or(d int64) int64 {
if v, ok := o.Get(); ok {
return v
}
return d
}
// NewOptString returns new OptString with value set to v.
func NewOptString(v string) OptString {
return OptString{
Value: v,
Set: true,
}
}
// OptString is optional string.
type OptString struct {
Value string
Set bool
}
// IsSet returns true if OptString was set.
func (o OptString) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptString) Reset() {
var v string
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptString) SetTo(v string) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptString) Get() (v string, 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 OptString) Or(d string) string {
if v, ok := o.Get(); ok {
return v
}
return d
}
// Ref: #/components/schemas/Script
type Script struct {
ID int64 `json:"ID"`
Name string `json:"Name"`
Hash string `json:"Hash"`
Source string `json:"Source"`
ResourceType int32 `json:"ResourceType"`
ResourceID int64 `json:"ResourceID"`
}
// GetID returns the value of ID.
func (s *Script) GetID() int64 {
return s.ID
}
// GetName returns the value of Name.
func (s *Script) GetName() string {
return s.Name
}
// 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
}
// GetResourceType returns the value of ResourceType.
func (s *Script) GetResourceType() int32 {
return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *Script) GetResourceID() int64 {
return s.ResourceID
}
// SetID sets the value of ID.
func (s *Script) SetID(val int64) {
s.ID = val
}
// SetName sets the value of Name.
func (s *Script) SetName(val string) {
s.Name = 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
}
// SetResourceType sets the value of ResourceType.
func (s *Script) SetResourceType(val int32) {
s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *Script) SetResourceID(val int64) {
s.ResourceID = val
}
// Ref: #/components/schemas/ScriptCreate
type ScriptCreate struct {
Name string `json:"Name"`
Source string `json:"Source"`
ResourceType int32 `json:"ResourceType"`
ResourceID OptInt64 `json:"ResourceID"`
}
// GetName returns the value of Name.
func (s *ScriptCreate) GetName() string {
return s.Name
}
// GetSource returns the value of Source.
func (s *ScriptCreate) GetSource() string {
return s.Source
}
// GetResourceType returns the value of ResourceType.
func (s *ScriptCreate) GetResourceType() int32 {
return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *ScriptCreate) GetResourceID() OptInt64 {
return s.ResourceID
}
// SetName sets the value of Name.
func (s *ScriptCreate) SetName(val string) {
s.Name = val
}
// SetSource sets the value of Source.
func (s *ScriptCreate) SetSource(val string) {
s.Source = val
}
// SetResourceType sets the value of ResourceType.
func (s *ScriptCreate) SetResourceType(val int32) {
s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *ScriptCreate) SetResourceID(val OptInt64) {
s.ResourceID = val
}
// Ref: #/components/schemas/ScriptID
type ScriptID struct {
ScriptID int64 `json:"ScriptID"`
}
// GetScriptID returns the value of ScriptID.
func (s *ScriptID) GetScriptID() int64 {
return s.ScriptID
}
// SetScriptID sets the value of ScriptID.
func (s *ScriptID) SetScriptID(val int64) {
s.ScriptID = 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/ScriptPolicyID
type ScriptPolicyID struct {
ScriptPolicyID int64 `json:"ScriptPolicyID"`
}
// GetScriptPolicyID returns the value of ScriptPolicyID.
func (s *ScriptPolicyID) GetScriptPolicyID() int64 {
return s.ScriptPolicyID
}
// SetScriptPolicyID sets the value of ScriptPolicyID.
func (s *ScriptPolicyID) SetScriptPolicyID(val int64) {
s.ScriptPolicyID = val
}
// Ref: #/components/schemas/SubmissionCreate
type SubmissionCreate struct {
OperationID int32 `json:"OperationID"`
AssetOwner int64 `json:"AssetOwner"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
Status uint32 `json:"Status"`
Roles uint32 `json:"Roles"`
}
// GetOperationID returns the value of OperationID.
func (s *SubmissionCreate) GetOperationID() int32 {
return s.OperationID
}
// GetAssetOwner returns the value of AssetOwner.
func (s *SubmissionCreate) GetAssetOwner() int64 {
return s.AssetOwner
}
// GetDisplayName returns the value of DisplayName.
func (s *SubmissionCreate) GetDisplayName() string {
return s.DisplayName
}
// GetCreator returns the value of Creator.
func (s *SubmissionCreate) GetCreator() string {
return s.Creator
}
// GetGameID returns the value of GameID.
func (s *SubmissionCreate) GetGameID() int32 {
return s.GameID
}
// GetAssetID returns the value of AssetID.
func (s *SubmissionCreate) GetAssetID() int64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *SubmissionCreate) GetAssetVersion() int64 {
return s.AssetVersion
}
// GetStatus returns the value of Status.
func (s *SubmissionCreate) GetStatus() uint32 {
return s.Status
}
// GetRoles returns the value of Roles.
func (s *SubmissionCreate) GetRoles() uint32 {
return s.Roles
}
// SetOperationID sets the value of OperationID.
func (s *SubmissionCreate) SetOperationID(val int32) {
s.OperationID = val
}
// SetAssetOwner sets the value of AssetOwner.
func (s *SubmissionCreate) SetAssetOwner(val int64) {
s.AssetOwner = val
}
// SetDisplayName sets the value of DisplayName.
func (s *SubmissionCreate) SetDisplayName(val string) {
s.DisplayName = val
}
// SetCreator sets the value of Creator.
func (s *SubmissionCreate) SetCreator(val string) {
s.Creator = val
}
// SetGameID sets the value of GameID.
func (s *SubmissionCreate) SetGameID(val int32) {
s.GameID = val
}
// SetAssetID sets the value of AssetID.
func (s *SubmissionCreate) SetAssetID(val int64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *SubmissionCreate) SetAssetVersion(val int64) {
s.AssetVersion = val
}
// SetStatus sets the value of Status.
func (s *SubmissionCreate) SetStatus(val uint32) {
s.Status = val
}
// SetRoles sets the value of Roles.
func (s *SubmissionCreate) SetRoles(val uint32) {
s.Roles = val
}
// Ref: #/components/schemas/SubmissionID
type SubmissionID struct {
SubmissionID int64 `json:"SubmissionID"`
}
// GetSubmissionID returns the value of SubmissionID.
func (s *SubmissionID) GetSubmissionID() int64 {
return s.SubmissionID
}
// SetSubmissionID sets the value of SubmissionID.
func (s *SubmissionID) SetSubmissionID(val int64) {
s.SubmissionID = val
}
// UpdateMapfixValidatedModelNoContent is response for UpdateMapfixValidatedModel operation.
type UpdateMapfixValidatedModelNoContent struct{}
// UpdateSubmissionValidatedModelNoContent is response for UpdateSubmissionValidatedModel operation.
type UpdateSubmissionValidatedModelNoContent struct{}

View File

@@ -1,154 +0,0 @@
// Code generated by ogen, DO NOT EDIT.
package api
import (
"context"
)
// Handler handles operations described by OpenAPI v3 specification.
type Handler interface {
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
//
// POST /mapfixes/{MapfixID}/status/validator-request-changes
ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error
// ActionMapfixSubmitted implements actionMapfixSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
ActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) error
// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
ActionMapfixUploaded(ctx context.Context, params ActionMapfixUploadedParams) error
// ActionMapfixValidated implements actionMapfixValidated operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
ActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) error
// ActionOperationFailed implements actionOperationFailed operation.
//
// (Internal endpoint) Fail an operation and write a StatusMessage.
//
// POST /operations/{OperationID}/status/operation-failed
ActionOperationFailed(ctx context.Context, params ActionOperationFailedParams) error
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/validator-failed
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
//
// POST /submissions/{SubmissionID}/status/validator-request-changes
ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error
// ActionSubmissionSubmitted implements actionSubmissionSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
ActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) error
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /submissions/{SubmissionID}/status/validator-uploaded
ActionSubmissionUploaded(ctx context.Context, params ActionSubmissionUploadedParams) error
// ActionSubmissionValidated implements actionSubmissionValidated operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
//
// POST /submissions/{SubmissionID}/status/validator-validated
ActionSubmissionValidated(ctx context.Context, params ActionSubmissionValidatedParams) error
// CreateMapfix implements createMapfix operation.
//
// Create a mapfix.
//
// POST /mapfixes
CreateMapfix(ctx context.Context, req *MapfixCreate) (*MapfixID, error)
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
CreateScript(ctx context.Context, req *ScriptCreate) (*ScriptID, error)
// CreateScriptPolicy implements createScriptPolicy operation.
//
// Create a new script policy.
//
// POST /script-policy
CreateScriptPolicy(ctx context.Context, req *ScriptPolicyCreate) (*ScriptPolicyID, error)
// CreateSubmission implements createSubmission operation.
//
// Create a new submission.
//
// POST /submissions
CreateSubmission(ctx context.Context, req *SubmissionCreate) (*SubmissionID, error)
// GetScript implements getScript operation.
//
// Get the specified script by ID.
//
// GET /scripts/{ScriptID}
GetScript(ctx context.Context, params GetScriptParams) (*Script, error)
// ListScriptPolicy implements listScriptPolicy operation.
//
// Get list of script policies.
//
// GET /script-policy
ListScriptPolicy(ctx context.Context, params ListScriptPolicyParams) ([]ScriptPolicy, error)
// ListScripts implements listScripts operation.
//
// Get list of scripts.
//
// GET /scripts
ListScripts(ctx context.Context, params ListScriptsParams) ([]Script, error)
// UpdateMapfixValidatedModel implements updateMapfixValidatedModel operation.
//
// Update validated model.
//
// POST /mapfixes/{MapfixID}/validated-model
UpdateMapfixValidatedModel(ctx context.Context, params UpdateMapfixValidatedModelParams) error
// UpdateSubmissionValidatedModel implements updateSubmissionValidatedModel operation.
//
// Update validated model.
//
// POST /submissions/{SubmissionID}/validated-model
UpdateSubmissionValidatedModel(ctx context.Context, params UpdateSubmissionValidatedModelParams) error
// NewError creates *ErrorStatusCode from error returned by handler.
//
// Used for common default response.
NewError(ctx context.Context, err error) *ErrorStatusCode
}
// Server implements http server based on OpenAPI v3 specification and
// calls Handler to handle requests.
type Server struct {
h Handler
baseServer
}
// NewServer creates new Server.
func NewServer(h Handler, opts ...ServerOption) (*Server, error) {
s, err := newServerConfig(opts...).baseServer()
if err != nil {
return nil, err
}
return &Server{
h: h,
baseServer: s,
}, nil
}

View File

@@ -1,202 +0,0 @@
// Code generated by ogen, DO NOT EDIT.
package api
import (
"context"
ht "github.com/ogen-go/ogen/http"
)
// UnimplementedHandler is no-op Handler which returns http.ErrNotImplemented.
type UnimplementedHandler struct{}
var _ Handler = UnimplementedHandler{}
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
func (UnimplementedHandler) ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
//
// POST /mapfixes/{MapfixID}/status/validator-request-changes
func (UnimplementedHandler) ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixSubmitted implements actionMapfixSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
func (UnimplementedHandler) ActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
func (UnimplementedHandler) ActionMapfixUploaded(ctx context.Context, params ActionMapfixUploadedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixValidated implements actionMapfixValidated operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
func (UnimplementedHandler) ActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) error {
return ht.ErrNotImplemented
}
// ActionOperationFailed implements actionOperationFailed operation.
//
// (Internal endpoint) Fail an operation and write a StatusMessage.
//
// POST /operations/{OperationID}/status/operation-failed
func (UnimplementedHandler) ActionOperationFailed(ctx context.Context, params ActionOperationFailedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/validator-failed
func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
//
// POST /submissions/{SubmissionID}/status/validator-request-changes
func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionSubmitted implements actionSubmissionSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
func (UnimplementedHandler) ActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /submissions/{SubmissionID}/status/validator-uploaded
func (UnimplementedHandler) ActionSubmissionUploaded(ctx context.Context, params ActionSubmissionUploadedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionValidated implements actionSubmissionValidated operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
//
// POST /submissions/{SubmissionID}/status/validator-validated
func (UnimplementedHandler) ActionSubmissionValidated(ctx context.Context, params ActionSubmissionValidatedParams) error {
return ht.ErrNotImplemented
}
// CreateMapfix implements createMapfix operation.
//
// Create a mapfix.
//
// POST /mapfixes
func (UnimplementedHandler) CreateMapfix(ctx context.Context, req *MapfixCreate) (r *MapfixID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
func (UnimplementedHandler) CreateScript(ctx context.Context, req *ScriptCreate) (r *ScriptID, _ 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 *ScriptPolicyID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateSubmission implements createSubmission operation.
//
// Create a new submission.
//
// POST /submissions
func (UnimplementedHandler) CreateSubmission(ctx context.Context, req *SubmissionCreate) (r *SubmissionID, _ error) {
return r, 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
}
// ListScriptPolicy implements listScriptPolicy operation.
//
// Get list of script policies.
//
// GET /script-policy
func (UnimplementedHandler) ListScriptPolicy(ctx context.Context, params ListScriptPolicyParams) (r []ScriptPolicy, _ error) {
return r, ht.ErrNotImplemented
}
// ListScripts implements listScripts operation.
//
// Get list of scripts.
//
// GET /scripts
func (UnimplementedHandler) ListScripts(ctx context.Context, params ListScriptsParams) (r []Script, _ error) {
return r, ht.ErrNotImplemented
}
// UpdateMapfixValidatedModel implements updateMapfixValidatedModel operation.
//
// Update validated model.
//
// POST /mapfixes/{MapfixID}/validated-model
func (UnimplementedHandler) UpdateMapfixValidatedModel(ctx context.Context, params UpdateMapfixValidatedModelParams) error {
return ht.ErrNotImplemented
}
// UpdateSubmissionValidatedModel implements updateSubmissionValidatedModel operation.
//
// Update validated model.
//
// POST /submissions/{SubmissionID}/validated-model
func (UnimplementedHandler) UpdateSubmissionValidatedModel(ctx context.Context, params UpdateSubmissionValidatedModelParams) error {
return ht.ErrNotImplemented
}
// NewError creates *ErrorStatusCode from error returned by handler.
//
// Used for common default response.
func (UnimplementedHandler) NewError(ctx context.Context, err error) (r *ErrorStatusCode) {
r = new(ErrorStatusCode)
return r
}

View File

@@ -1,940 +0,0 @@
// Code generated by ogen, DO NOT EDIT.
package api
import (
"github.com/go-faster/errors"
"github.com/ogen-go/ogen/validate"
)
func (s *Error) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.Code)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "code",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ErrorStatusCode) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := s.Response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Response",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *MapfixCreate) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.OperationID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "OperationID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.AssetOwner)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetOwner",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.DisplayName)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "DisplayName",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Creator)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Creator",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.GameID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "GameID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.AssetID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.AssetVersion)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetVersion",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.TargetAssetID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "TargetAssetID",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 256,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Description)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Description",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *MapfixID) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.MapfixID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "MapfixID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *Script) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ID",
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.Name)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Name",
Error: 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(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 err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ResourceType)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ResourceType",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ResourceID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ResourceID",
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: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Name)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Name",
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 err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ResourceType)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ResourceType",
Error: err,
})
}
if err := func() error {
if value, ok := s.ResourceID.Get(); ok {
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(value)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
return err
}
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ResourceID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ScriptID) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ScriptID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ScriptID",
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.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ID",
Error: 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(s.FromScriptHash)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "FromScriptHash",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ToScriptID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ToScriptID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.Policy)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Policy",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ScriptPolicyCreate) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.FromScriptID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "FromScriptID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ToScriptID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ToScriptID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.Policy)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Policy",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ScriptPolicyID) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ScriptPolicyID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ScriptPolicyID",
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.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.OperationID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "OperationID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.AssetOwner)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetOwner",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.DisplayName)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "DisplayName",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Creator)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Creator",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.GameID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "GameID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.AssetID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.AssetVersion)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetVersion",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: true,
Max: 9,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.Status)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Status",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *SubmissionID) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.SubmissionID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "SubmissionID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}

View File

@@ -2,9 +2,12 @@ package model
import (
"encoding/json"
"math"
"time"
)
const ValidatorUserID uint64 = uint64(math.MaxInt64)
type AuditEventType int32
// User clicked "Submit", "Accept" etc
@@ -48,6 +51,19 @@ type AuditEventDataError struct {
Error string `json:"error"`
}
type Check struct {
Name string `json:"name"`
Summary string `json:"summary"`
Passed bool `json:"passed"`
}
// Validator map checks details
const AuditEventTypeCheckList AuditEventType = 7
type AuditEventDataCheckList struct {
CheckList []Check `json:"check_list"`
}
type AuditEvent struct {
ID int64 `gorm:"primaryKey"`
CreatedAt time.Time

18
pkg/model/map.go Normal file
View File

@@ -0,0 +1,18 @@
package model
import "time"
type Map struct {
ID int64
DisplayName string
Creator string
GameID uint32
Date time.Time // Release date
CreatedAt time.Time
UpdatedAt time.Time
Submitter uint64 // UserID of submitter
Thumbnail uint64 // AssetID of thumbnail
AssetVersion uint64 // Version number for LoadAssetVersion
LoadCount uint32 // How many times the map has been loaded
Modes uint32 // Number of modes (always at least one)
}

View File

@@ -27,11 +27,13 @@ type CreateMapfixRequest struct {
type CheckSubmissionRequest struct{
SubmissionID int64
ModelID uint64
SkipChecks bool
}
type CheckMapfixRequest struct{
MapfixID int64
ModelID uint64
MapfixID int64
ModelID uint64
SkipChecks bool
}
type ValidateSubmissionRequest struct {

13
pkg/model/resource.go Normal file
View File

@@ -0,0 +1,13 @@
package model
type ResourceType int32
const (
ResourceUnknown ResourceType = 0
ResourceMapfix ResourceType = 1
ResourceSubmission ResourceType = 2
)
type Resource struct{
ID int64
Type ResourceType
}

33
pkg/model/roles.go Normal file
View File

@@ -0,0 +1,33 @@
package model
// Submissions roles bitflag
type Roles int32
var (
RolesSubmissionUpload Roles = 1<<6
RolesSubmissionReview Roles = 1<<5
RolesSubmissionRelease Roles = 1<<4
RolesScriptWrite Roles = 1<<3
RolesMapfixUpload Roles = 1<<2
RolesMapfixReview Roles = 1<<1
RolesMapDownload Roles = 1<<0
RolesEmpty Roles = 0
)
// StrafesNET group roles
type GroupRole int32
var (
// has ScriptWrite
RoleQuat GroupRole = 255
RoleItzaname GroupRole = 254
RoleStagingDeveloper GroupRole = 240
RolesAll Roles = ^RolesEmpty
// has SubmissionUpload
RoleMapAdmin GroupRole = 128
RolesMapAdmin Roles = RolesSubmissionRelease|RolesSubmissionUpload|RolesSubmissionReview|RolesMapCouncil
// has MapfixReview
RoleMapCouncil GroupRole = 64
RolesMapCouncil Roles = RolesMapfixReview|RolesMapfixUpload|RolesMapAccess
// access to downloading maps
RoleMapAccess GroupRole = 32
RolesMapAccess Roles = RolesMapDownload
)

View File

@@ -23,13 +23,6 @@ func HashParse(hash string) (uint64, error){
return strconv.ParseUint(hash, 16, 64)
}
type ResourceType int32
const (
ResourceUnknown ResourceType = 0
ResourceMapfix ResourceType = 1
ResourceSubmission ResourceType = 2
)
type Script struct {
ID int64 `gorm:"primaryKey"`
Name string

View File

@@ -0,0 +1,95 @@
package roblox
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
type AssetMetadata struct {
MetadataType uint32 `json:"metadataType"`
Value string `json:"value"`
}
// Struct equivalent to Rust's AssetLocationInfo
type AssetLocationInfo struct {
Location string `json:"location"`
RequestId string `json:"requestId"`
IsArchived bool `json:"isArchived"`
AssetTypeId uint32 `json:"assetTypeId"`
AssetMetadatas []AssetMetadata `json:"assetMetadatas"`
IsRecordable bool `json:"isRecordable"`
}
// Input struct for getAssetLocation
type GetAssetLatestRequest struct {
AssetID uint64
}
// Custom error type if needed
type GetError string
func (e GetError) Error() string { return string(e) }
// Example client with a Get method
type Client struct {
HttpClient *http.Client
ApiKey string
}
func (c *Client) GetAssetLocation(config GetAssetLatestRequest) (*AssetLocationInfo, error) {
rawURL := fmt.Sprintf("https://apis.roblox.com/asset-delivery-api/v1/assetId/%d", config.AssetID)
parsedURL, err := url.Parse(rawURL)
if err != nil {
return nil, GetError("ParseError: " + err.Error())
}
req, err := http.NewRequest("GET", parsedURL.String(), nil)
if err != nil {
return nil, GetError("RequestCreationError: " + err.Error())
}
req.Header.Set("x-api-key", c.ApiKey)
resp, err := c.HttpClient.Do(req)
if err != nil {
return nil, GetError("RequestError: " + err.Error())
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, GetError(fmt.Sprintf("ResponseError: status code %d", resp.StatusCode))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, GetError("ReadBodyError: " + err.Error())
}
var info AssetLocationInfo
if err := json.Unmarshal(body, &info); err != nil {
return nil, GetError("JSONError: " + err.Error())
}
return &info, nil
}
func (c *Client) DownloadAsset(info *AssetLocationInfo) (io.Reader, error) {
req, err := http.NewRequest("GET", info.Location, nil)
if err != nil {
return nil, GetError("RequestCreationError: " + err.Error())
}
resp, err := c.HttpClient.Do(req)
if err != nil {
return nil, GetError("RequestError: " + err.Error())
}
if resp.StatusCode != http.StatusOK {
return nil, GetError(fmt.Sprintf("ResponseError: status code %d", resp.StatusCode))
}
return resp.Body, nil
}

View File

@@ -10,85 +10,13 @@ import (
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
// CreateMapfixAuditComment implements createMapfixAuditComment operation.
//
// Post a comment to the audit log
//
// POST /mapfixes/{MapfixID}/comment
func (svc *Service) CreateMapfixAuditComment(ctx context.Context, req api.CreateMapfixAuditCommentReq, params api.CreateMapfixAuditCommentParams) (error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleMapfixReview()
if err != nil {
return err
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
if !has_role {
// Submitter has special permission to comment on their mapfix
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
if err != nil {
return err
}
if mapfix.Submitter != userId {
return ErrPermissionDeniedNeedRoleMapfixReview
}
}
data := []byte{}
_, err = req.Read(data)
if err != nil {
return err
}
Comment := string(data)
event_data := model.AuditEventDataComment{
Comment: Comment,
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeComment,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ListMapfixAuditEvents invokes listMapfixAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /mapfixes/{MapfixID}/audit-events
func (svc *Service) ListMapfixAuditEvents(ctx context.Context, params api.ListMapfixAuditEventsParams) ([]api.AuditEvent, error) {
func (svc *Service) ListAuditEvents(ctx context.Context, resource model.Resource, page model.Page) ([]api.AuditEvent, error){
filter := datastore.Optional()
filter.Add("resource_type", model.ResourceMapfix)
filter.Add("resource_id", params.MapfixID)
filter.Add("resource_type", resource.Type)
filter.Add("resource_id", resource.ID)
items, err := svc.DB.AuditEvents().List(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
})
items, err := svc.db.AuditEvents().List(ctx, filter, page)
if err != nil {
return nil, err
}
@@ -104,7 +32,7 @@ func (svc *Service) ListMapfixAuditEvents(ctx context.Context, params api.ListMa
idList.ID = append(idList.ID, userId)
}
userList, err := svc.Users.GetList(ctx, &idList)
userList, err := svc.users.GetList(ctx, &idList)
if err != nil {
return nil, err
}
@@ -140,60 +68,38 @@ func (svc *Service) ListMapfixAuditEvents(ctx context.Context, params api.ListMa
return resp, nil
}
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
//
// Post a comment to the audit log
//
// POST /submissions/{SubmissionID}/comment
func (svc *Service) CreateSubmissionAuditComment(ctx context.Context, req api.CreateSubmissionAuditCommentReq, params api.CreateSubmissionAuditCommentParams) (error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionReview()
if err != nil {
return err
}
userId, err := userInfo.GetUserID()
if err != nil {
return err
}
if !has_role {
// Submitter has special permission to comment on their submission
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil {
return err
}
if submission.Submitter != userId {
return ErrPermissionDeniedNeedRoleSubmissionReview
}
}
data := []byte{}
_, err = req.Read(data)
if err != nil {
return err
}
Comment := string(data)
event_data := model.AuditEventDataComment{
Comment: Comment,
}
func (svc *Service) CreateAuditEventAction(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataAction) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
_, err = svc.db.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventComment(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataComment) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.db.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeComment,
EventData: EventData,
})
@@ -204,68 +110,129 @@ func (svc *Service) CreateSubmissionAuditComment(ctx context.Context, req api.Cr
return nil
}
// ListSubmissionAuditEvents invokes listSubmissionAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /submissions/{SubmissionID}/audit-events
func (svc *Service) ListSubmissionAuditEvents(ctx context.Context, params api.ListSubmissionAuditEventsParams) ([]api.AuditEvent, error) {
filter := datastore.Optional()
func (svc *Service) CreateAuditEventChangeModel(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataChangeModel) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
filter.Add("resource_type", model.ResourceSubmission)
filter.Add("resource_id", params.SubmissionID)
items, err := svc.DB.AuditEvents().List(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
_, err = svc.db.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeChangeModel,
EventData: EventData,
})
if err != nil {
return nil, err
return err
}
idMap := make(map[int64]bool)
for _, item := range items {
idMap[int64(item.User)] = true
}
var idList users.IdList
idList.ID = make([]int64,len(idMap))
for userId := range idMap {
idList.ID = append(idList.ID, userId)
}
userList, err := svc.Users.GetList(ctx, &idList)
if err != nil {
return nil, err
}
userMap := make(map[int64]*users.UserResponse)
for _,user := range userList.Users {
userMap[user.ID] = user
}
var resp []api.AuditEvent
for _, item := range items {
EventData := api.AuditEventEventData{}
err = EventData.UnmarshalJSON(item.EventData)
if err != nil {
return nil, err
}
username := ""
if userMap[int64(item.User)] != nil {
username = userMap[int64(item.User)].Username
}
resp = append(resp, api.AuditEvent{
ID: item.ID,
Date: item.CreatedAt.Unix(),
User: int64(item.User),
Username: username,
ResourceType: int32(item.ResourceType),
ResourceID: item.ResourceID,
EventType: int32(item.EventType),
EventData: EventData,
})
}
return resp, nil
return nil
}
func (svc *Service) CreateAuditEventChangeValidatedModel(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataChangeValidatedModel) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.db.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeChangeValidatedModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventError(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataError) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.db.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeError,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventCheckList(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataCheckList) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.db.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeCheckList,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventChangeDisplayName(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataChangeName) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.db.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeChangeDisplayName,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventChangeCreator(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataChangeName) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.db.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeChangeCreator,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,72 +2,187 @@ package service
import (
"context"
"time"
"git.itzana.me/strafesnet/go-grpc/maps"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
// ListMaps implements listMaps operation.
//
// Get list of maps.
//
// GET /maps
func (svc *Service) ListMaps(ctx context.Context, params api.ListMapsParams) ([]api.Map, error) {
filter := maps.MapFilter{}
// Optional map used to update an object
type MapUpdate datastore.OptionalMap
if params.DisplayName.IsSet(){
filter.DisplayName = &params.DisplayName.Value
}
if params.Creator.IsSet(){
filter.Creator = &params.Creator.Value
}
if params.GameID.IsSet(){
filter.GameID = &params.GameID.Value
}
func NewMapUpdate() MapUpdate {
update := datastore.Optional()
return MapUpdate(update)
}
mapList, err := svc.Maps.List(ctx, &maps.ListRequest{
Filter: &filter,
Page: &maps.Pagination{
Size: params.Limit,
Number: params.Page,
},
})
func (update MapUpdate) SetDisplayName(display_name string) {
datastore.OptionalMap(update).Add("display_name", display_name)
}
func (update MapUpdate) SetCreator(creator string) {
datastore.OptionalMap(update).Add("creator", creator)
}
func (update MapUpdate) SetGameID(game_id uint32) {
datastore.OptionalMap(update).Add("game_id", game_id)
}
func (update MapUpdate) SetDate(date int64) {
datastore.OptionalMap(update).Add("date", date)
}
func (update MapUpdate) SetSubmitter(submitter uint64) {
datastore.OptionalMap(update).Add("submitter", submitter)
}
func (update MapUpdate) SetThumbnail(thumbnail uint64) {
datastore.OptionalMap(update).Add("thumbnail", thumbnail)
}
func (update MapUpdate) SetAssetVersion(asset_version uint64) {
datastore.OptionalMap(update).Add("asset_version", asset_version)
}
func (update MapUpdate) SetModes(modes uint32) {
datastore.OptionalMap(update).Add("modes", modes)
}
// getters
func (update MapUpdate) GetDisplayName() (string, bool) {
value, ok := datastore.OptionalMap(update).Map()["display_name"].(string)
return value, ok
}
func (update MapUpdate) GetCreator() (string, bool) {
value, ok := datastore.OptionalMap(update).Map()["creator"].(string)
return value, ok
}
func (update MapUpdate) GetGameID() (uint32, bool) {
value, ok := datastore.OptionalMap(update).Map()["game_id"].(uint32)
return value, ok
}
func (update MapUpdate) GetDate() (int64, bool) {
value, ok := datastore.OptionalMap(update).Map()["date"].(int64)
return value, ok
}
// Optional map used to find matching objects
type MapFilter datastore.OptionalMap
func NewMapFilter(
) MapFilter {
filter := datastore.Optional()
return MapFilter(filter)
}
func (update MapFilter) SetDisplayName(display_name string) {
datastore.OptionalMap(update).Add("display_name", display_name)
}
func (update MapFilter) SetCreator(creator string) {
datastore.OptionalMap(update).Add("creator", creator)
}
func (update MapFilter) SetGameID(game_id uint32) {
datastore.OptionalMap(update).Add("game_id", game_id)
}
func (update MapFilter) SetSubmitter(submitter uint64) {
datastore.OptionalMap(update).Add("submitter", submitter)
}
func (svc *Service) TEMP_DoMapsMigration(ctx context.Context) (error) {
// get all maps
maps, err := svc.maps.List(ctx, &maps.ListRequest{})
if err != nil {
return nil, err
return err
}
var resp []api.Map
for _, item := range mapList.Maps {
resp = append(resp, api.Map{
// create all maps
for _, item := range maps.Maps {
migrated := model.Map{
ID: item.ID,
DisplayName: item.DisplayName,
Creator: item.Creator,
GameID: item.GameID,
Date: item.Date,
})
GameID: uint32(item.GameID),
Date: time.Unix(item.Date, 0),
// CreatedAt: time.Time{},
// UpdatedAt: time.Time{},
// Submitter: 0,
// Thumbnail: 0,
// AssetVersion: 0,
// LoadCount: 0,
// Modes: 0,
}
_, err := svc.db.Maps().Create(ctx, migrated)
if err != nil {
return err
}
}
return resp, nil
return nil
}
// GetMap implements getScript operation.
//
// Get the specified script by ID.
//
// GET /maps/{MapID}
func (svc *Service) GetMap(ctx context.Context, params api.GetMapParams) (*api.Map, error) {
mapResponse, err := svc.Maps.Get(ctx, &maps.IdMessage{
ID: params.MapID,
func (svc *Service) CreateMap(ctx context.Context, item model.Map) (int64, error) {
// 2 jobs:
// create map on maps-service
map_item, err := svc.db.Maps().Create(ctx, item)
if err != nil {
return 0, err
}
// create map on data-service
date := item.Date.Unix()
game_id := int32(item.GameID)
_, err = svc.maps.Create(ctx, &maps.MapRequest{
ID: item.ID,
DisplayName: &item.DisplayName,
Creator: &item.Creator,
GameID: &game_id,
Date: &date,
})
if err != nil {
return nil, err
return 0, err
}
return &api.Map{
ID: mapResponse.ID,
DisplayName: mapResponse.DisplayName,
Creator: mapResponse.Creator,
GameID: mapResponse.GameID,
Date: mapResponse.Date,
}, nil
return map_item.ID, nil
}
func (svc *Service) ListMaps(ctx context.Context, filter MapFilter, page model.Page) ([]model.Map, error) {
return svc.db.Maps().List(ctx, datastore.OptionalMap(filter), page)
}
func (svc *Service) GetMapList(ctx context.Context, ids []int64) ([]model.Map, error) {
return svc.db.Maps().GetList(ctx, ids)
}
func (svc *Service) DeleteMap(ctx context.Context, id int64) error {
// Do not delete the "embedded" map, since it deletes times.
// _, err := svc.maps.Delete(ctx, &maps.IdMessage{ID: id})
return svc.db.Maps().Delete(ctx, id)
}
func (svc *Service) GetMap(ctx context.Context, id int64) (model.Map, error) {
return svc.db.Maps().Get(ctx, id)
}
func (svc *Service) UpdateMap(ctx context.Context, id int64, pmap MapUpdate) error {
// 2 jobs:
// update map on maps-service
err := svc.db.Maps().Update(ctx, id, datastore.OptionalMap(pmap))
if err != nil {
return err
}
// update map on data-service
update := maps.MapRequest{
ID: id,
}
if display_name, ok := pmap.GetDisplayName(); ok {
update.DisplayName = &display_name
}
if creator, ok := pmap.GetCreator(); ok {
update.Creator = &creator
}
if game_id, ok := pmap.GetGameID(); ok {
game_id_int32 := int32(game_id)
update.GameID = &game_id_int32
}
if date, ok := pmap.GetDate(); ok {
update.Date = &date
}
_, err = svc.maps.Update(ctx, &update)
if err != nil {
return err
}
return nil
}
func (svc *Service) IncrementMapLoadCount(ctx context.Context, id int64) error {
return svc.db.Maps().IncrementLoadCount(ctx, id)
}

114
pkg/service/nats_mapfix.go Normal file
View File

@@ -0,0 +1,114 @@
package service
import (
"encoding/json"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
func (svc *Service) NatsCreateMapfix(
OperationID int32,
ModelID uint64,
TargetAssetID uint64,
Description string,
) error {
create_request := model.CreateMapfixRequest{
OperationID: OperationID,
ModelID: ModelID,
TargetAssetID: TargetAssetID,
Description: Description,
}
j, err := json.Marshal(create_request)
if err != nil {
return err
}
_, err = svc.nats.Publish("maptest.mapfixes.create", []byte(j))
if err != nil {
return err
}
return nil
}
func (svc *Service) NatsCheckMapfix(
MapfixID int64,
ModelID uint64,
SkipChecks bool,
) error {
validate_request := model.CheckMapfixRequest{
MapfixID: MapfixID,
ModelID: ModelID,
SkipChecks: SkipChecks,
}
j, err := json.Marshal(validate_request)
if err != nil {
return err
}
_, err = svc.nats.Publish("maptest.mapfixes.check", []byte(j))
if err != nil {
return err
}
return nil
}
func (svc *Service) NatsUploadMapfix(
MapfixID int64,
ModelID uint64,
ModelVersion uint64,
TargetAssetID uint64,
) error {
upload_fix_request := model.UploadMapfixRequest{
MapfixID: MapfixID,
ModelID: ModelID,
ModelVersion: ModelVersion,
TargetAssetID: TargetAssetID,
}
j, err := json.Marshal(upload_fix_request)
if err != nil {
return err
}
_, err = svc.nats.Publish("maptest.mapfixes.upload", []byte(j))
if err != nil {
return err
}
return nil
}
func (svc *Service) NatsValidateMapfix(
MapfixID int64,
ModelID uint64,
ModelVersion uint64,
ValidatedAssetID uint64,
) error {
validate_request := model.ValidateMapfixRequest{
MapfixID: MapfixID,
ModelID: ModelID,
ModelVersion: ModelVersion,
ValidatedModelID: nil,
}
// sentinel values because we're not using rust
if ValidatedAssetID != 0 {
validate_request.ValidatedModelID = &ValidatedAssetID
}
j, err := json.Marshal(validate_request)
if err != nil {
return err
}
_, err = svc.nats.Publish("maptest.mapfixes.validate", []byte(j))
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,120 @@
package service
import (
"encoding/json"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
func (svc *Service) NatsCreateSubmission(
OperationID int32,
ModelID uint64,
DisplayName string,
Creator string,
GameID uint32,
Status uint32,
Roles uint32,
) error {
create_request := model.CreateSubmissionRequest{
OperationID: OperationID,
ModelID: ModelID,
DisplayName: DisplayName,
Creator: Creator,
GameID: GameID,
Status: Status,
Roles: Roles,
}
j, err := json.Marshal(create_request)
if err != nil {
return err
}
_, err = svc.nats.Publish("maptest.submissions.create", []byte(j))
if err != nil {
return err
}
return nil
}
func (svc *Service) NatsCheckSubmission(
SubmissionID int64,
ModelID uint64,
SkipChecks bool,
) error {
validate_request := model.CheckSubmissionRequest{
SubmissionID: SubmissionID,
ModelID: ModelID,
SkipChecks: SkipChecks,
}
j, err := json.Marshal(validate_request)
if err != nil {
return err
}
_, err = svc.nats.Publish("maptest.submissions.check", []byte(j))
if err != nil {
return err
}
return nil
}
func (svc *Service) NatsUploadSubmission(
SubmissionID int64,
ModelID uint64,
ModelVersion uint64,
ModelName string,
) error {
upload_new_request := model.UploadSubmissionRequest{
SubmissionID: SubmissionID,
ModelID: ModelID,
ModelVersion: ModelVersion,
ModelName: ModelName,
}
j, err := json.Marshal(upload_new_request)
if err != nil {
return err
}
_, err = svc.nats.Publish("maptest.submissions.upload", []byte(j))
if err != nil {
return err
}
return nil
}
func (svc *Service) NatsValidateSubmission(
SubmissionID int64,
ModelID uint64,
ModelVersion uint64,
ValidatedModelID uint64,
) error {
validate_request := model.ValidateSubmissionRequest{
SubmissionID: SubmissionID,
ModelID: ModelID,
ModelVersion: ModelVersion,
ValidatedModelID: nil,
}
// sentinel values because we're not using rust
if ValidatedModelID != 0 {
validate_request.ValidatedModelID = &ValidatedModelID
}
j, err := json.Marshal(validate_request)
if err != nil {
return err
}
_, err = svc.nats.Publish("maptest.submissions.validate", []byte(j))
if err != nil {
return err
}
return nil
}

View File

@@ -2,45 +2,54 @@ package service
import (
"context"
"time"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
// GetOperation implements getOperation operation.
//
// Get the specified operation by ID.
//
// GET /operations/{OperationID}
func (svc *Service) GetOperation(ctx context.Context, params api.GetOperationParams) (*api.Operation, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
type OperationFailParams datastore.OptionalMap
// You must be the operation owner to read it
operation, err := svc.DB.Operations().Get(ctx, params.OperationID)
if err != nil {
return nil, err
}
userId, err := userInfo.GetUserID()
if err != nil {
return nil, err
}
// check if caller is the submitter
has_role := userId == operation.Owner
if !has_role {
return nil, ErrPermissionDeniedNotSubmitter
}
return &api.Operation{
OperationID: operation.ID,
Date: operation.CreatedAt.Unix(),
Owner: int64(operation.Owner),
Status: int32(operation.StatusID),
StatusMessage: operation.StatusMessage,
Path: operation.Path,
}, nil
func NewOperationFailParams(
status_message string,
) OperationFailParams {
filter := datastore.Optional()
filter.Add("status_id", model.OperationStatusFailed)
filter.Add("status_message", status_message)
return OperationFailParams(filter)
}
type OperationCompleteParams datastore.OptionalMap
func NewOperationCompleteParams(
path string,
) OperationCompleteParams {
filter := datastore.Optional()
filter.Add("status_id", model.OperationStatusCompleted)
filter.Add("path", path)
return OperationCompleteParams(filter)
}
func (svc *Service) CreateOperation(ctx context.Context, operation model.Operation) (model.Operation, error) {
return svc.db.Operations().Create(ctx, operation)
}
func (svc *Service) CountOperationsSince(ctx context.Context, owner int64, since time.Time) (int64, error) {
return svc.db.Operations().CountSince(ctx, owner, since)
}
func (svc *Service) DeleteOperation(ctx context.Context, id int32) error {
return svc.db.Operations().Delete(ctx, id)
}
func (svc *Service) GetOperation(ctx context.Context, id int32) (model.Operation, error) {
return svc.db.Operations().Get(ctx, id)
}
func (svc *Service) FailOperation(ctx context.Context, id int32, params OperationFailParams) error {
return svc.db.Operations().Update(ctx, id, datastore.OptionalMap(params))
}
func (svc *Service) CompleteOperation(ctx context.Context, id int32, params OperationCompleteParams) error {
return svc.db.Operations().Update(ctx, id, datastore.OptionalMap(params))
}

View File

@@ -3,169 +3,43 @@ package service
import (
"context"
"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.ScriptPolicyID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
type ScriptPolicyFilter datastore.OptionalMap
has_role, err := userInfo.HasRoleScriptWrite()
if err != nil {
return nil, err
}
if !has_role {
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.ScriptPolicyID{
ScriptPolicyID: script.ID,
}, nil
}
// ListScriptPolicy implements listScriptPolicy operation.
//
// Get list of script policies.
//
// GET /script-policy
func (svc *Service) ListScriptPolicy(ctx context.Context, params api.ListScriptPolicyParams) ([]api.ScriptPolicy, error) {
func NewScriptPolicyFilter() ScriptPolicyFilter {
filter := datastore.Optional()
if params.FromScriptHash.IsSet(){
hash, err := model.HashParse(params.FromScriptHash.Value)
if err != nil {
return nil, err
}
filter.Add("from_script_hash", int64(hash)) // No type safety!
}
if params.ToScriptID.IsSet(){
filter.Add("to_script_id", params.ToScriptID.Value)
}
if params.Policy.IsSet(){
filter.Add("policy", params.Policy.Value)
}
items, err := svc.DB.ScriptPolicy().List(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
})
if err != nil {
return nil, err
}
var resp []api.ScriptPolicy
for _, item := range items {
resp = append(resp, api.ScriptPolicy{
ID: item.ID,
FromScriptHash: model.HashFormat(uint64(item.FromScriptHash)),
ToScriptID: item.ToScriptID,
Policy: int32(item.Policy),
})
}
return resp, nil
return ScriptPolicyFilter(filter)
}
func (filter ScriptPolicyFilter) SetFromScriptHash(from_script_hash int64) {
// Finally, type safety!
datastore.OptionalMap(filter).Add("from_script_hash", from_script_hash)
}
func (filter ScriptPolicyFilter) SetToScriptID(to_script_id int64) {
datastore.OptionalMap(filter).Add("to_script_id", to_script_id)
}
func (filter ScriptPolicyFilter) SetPolicy(policy int32) {
datastore.OptionalMap(filter).Add("policy", policy)
}
// DeleteScriptPolicy implements deleteScriptPolicy operation.
//
// Delete the specified script policy by ID.
//
// DELETE /script-policy/{ScriptPolicyID}
func (svc *Service) DeleteScriptPolicy(ctx context.Context, params api.DeleteScriptPolicyParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleScriptWrite()
if err != nil {
return err
}
if !has_role {
return ErrPermissionDenied
}
return svc.DB.ScriptPolicy().Delete(ctx, params.ScriptPolicyID)
func (svc *Service) CreateScriptPolicy(ctx context.Context, script model.ScriptPolicy) (model.ScriptPolicy, error) {
return svc.db.ScriptPolicy().Create(ctx, script)
}
// GetScriptPolicy implements getScriptPolicy operation.
//
// Get the specified script policy by ID.
//
// GET /script-policy/{ScriptPolicyID}
func (svc *Service) GetScriptPolicy(ctx context.Context, params api.GetScriptPolicyParams) (*api.ScriptPolicy, error) {
policy, err := svc.DB.ScriptPolicy().Get(ctx, params.ScriptPolicyID)
if err != nil {
return nil, err
}
return &api.ScriptPolicy{
ID: policy.ID,
FromScriptHash: model.HashFormat(uint64(policy.FromScriptHash)),
ToScriptID: policy.ToScriptID,
Policy: int32(policy.Policy),
}, nil
func (svc *Service) ListScriptPolicies(ctx context.Context, filter ScriptPolicyFilter, page model.Page) ([]model.ScriptPolicy, error) {
return svc.db.ScriptPolicy().List(ctx, datastore.OptionalMap(filter), page)
}
// UpdateScriptPolicy implements updateScriptPolicy operation.
//
// Update the specified script policy by ID.
//
// POST /script-policy/{ScriptPolicyID}
func (svc *Service) UpdateScriptPolicy(ctx context.Context, req *api.ScriptPolicyUpdate, params api.UpdateScriptPolicyParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleScriptWrite()
if err != nil {
return err
}
if !has_role {
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)
func (svc *Service) DeleteScriptPolicy(ctx context.Context, id int64) error {
return svc.db.ScriptPolicy().Delete(ctx, id)
}
func (svc *Service) GetScriptPolicy(ctx context.Context, id int64) (model.ScriptPolicy, error) {
return svc.db.ScriptPolicy().Get(ctx, id)
}
func (svc *Service) UpdateScriptPolicy(ctx context.Context, id int64, pmap ScriptPolicyFilter) error {
return svc.db.ScriptPolicy().Update(ctx, id, datastore.OptionalMap(pmap))
}

View File

@@ -3,172 +3,48 @@ package service
import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ScriptID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
type ScriptFilter datastore.OptionalMap
has_role, err := userInfo.HasRoleScriptWrite()
if err != nil {
return nil, err
}
if !has_role {
return nil, ErrPermissionDenied
}
script, err := svc.DB.Scripts().Create(ctx, model.Script{
ID: 0,
Name: req.Name,
Hash: int64(model.HashSource(req.Source)),
Source: req.Source,
ResourceType: model.ResourceType(req.ResourceType),
ResourceID: req.ResourceID.Or(0),
})
if err != nil {
return nil, err
}
return &api.ScriptID{
ScriptID: script.ID,
}, nil
}
// ListScripts implements listScripts operation.
//
// Get list of scripts.
//
// GET /scripts
func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParams) ([]api.Script, error) {
func NewScriptFilter() ScriptFilter {
filter := datastore.Optional()
if params.Hash.IsSet(){
hash, err := model.HashParse(params.Hash.Value)
if err != nil {
return nil, err
}
filter.Add("hash", int64(hash)) // No type safety!
}
if params.Name.IsSet(){
filter.Add("name", params.Name.Value)
}
if params.Source.IsSet(){
filter.Add("source", params.Source.Value)
}
if params.ResourceType.IsSet(){
filter.Add("resource_type", params.ResourceType.Value)
}
if params.ResourceID.IsSet(){
filter.Add("resource_id", params.ResourceID.Value)
}
items, err := svc.DB.Scripts().List(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
})
if err != nil {
return nil, err
}
var resp []api.Script
for _, item := range items {
resp = append(resp, api.Script{
ID: item.ID,
Hash: model.HashFormat(uint64(item.Hash)),
Source: item.Source,
ResourceType: int32(item.ResourceType),
ResourceID: item.ResourceID,
})
}
return resp, nil
return ScriptFilter(filter)
}
func (filter ScriptFilter) SetName(name string) {
datastore.OptionalMap(filter).Add("name", name)
}
func (filter ScriptFilter) SetSource(source string) {
datastore.OptionalMap(filter).Add("source", source)
}
func (filter ScriptFilter) SetHash(hash int64) {
datastore.OptionalMap(filter).Add("hash", hash)
}
func (filter ScriptFilter) SetResourceType(resource_type int32) {
datastore.OptionalMap(filter).Add("resource_type", resource_type)
}
func (filter ScriptFilter) SetResourceID(resource_id int64) {
datastore.OptionalMap(filter).Add("resource_id", resource_id)
}
// 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").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleScriptWrite()
if err != nil {
return err
}
if !has_role {
return ErrPermissionDenied
}
return svc.DB.Scripts().Delete(ctx, params.ScriptID)
func (svc *Service) CreateScript(ctx context.Context, script model.Script) (model.Script, error) {
return svc.db.Scripts().Create(ctx, script)
}
// 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) {
script, err := svc.DB.Scripts().Get(ctx, params.ScriptID)
if err != nil {
return nil, err
}
return &api.Script{
ID: script.ID,
Name: script.Name,
Hash: model.HashFormat(uint64(script.Hash)),
Source: script.Source,
ResourceType: int32(script.ResourceType),
ResourceID: script.ResourceID,
}, nil
func (svc *Service) ListScripts(ctx context.Context, filter ScriptFilter, page model.Page) ([]model.Script, error) {
return svc.db.Scripts().List(ctx, datastore.OptionalMap(filter), page)
}
// 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").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleScriptWrite()
if err != nil {
return err
}
if !has_role {
return ErrPermissionDenied
}
pmap := datastore.Optional()
if Name, ok := req.Name.Get(); ok {
pmap.Add("name", Name)
}
if source, ok := req.Source.Get(); ok {
pmap.Add("source", source)
pmap.Add("hash", int64(model.HashSource(source))) // No type safety!
}
if ResourceType, ok := req.ResourceType.Get(); ok {
pmap.Add("resource_type", ResourceType)
}
if ResourceID, ok := req.ResourceID.Get(); ok {
pmap.Add("resource_id", ResourceID)
}
return svc.DB.Scripts().Update(ctx, req.ID, pmap)
func (svc *Service) DeleteScript(ctx context.Context, id int64) error {
return svc.db.Scripts().Delete(ctx, id)
}
func (svc *Service) GetScript(ctx context.Context, id int64) (model.Script, error) {
return svc.db.Scripts().Get(ctx, id)
}
func (svc *Service) UpdateScript(ctx context.Context, id int64, pmap ScriptFilter) error {
return svc.db.Scripts().Update(ctx, id, datastore.OptionalMap(pmap))
}

View File

@@ -1,61 +1,29 @@
package service
import (
"context"
"errors"
"fmt"
"git.itzana.me/strafesnet/go-grpc/maps"
"git.itzana.me/strafesnet/go-grpc/users"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/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")
ErrDelayReset = errors.New("Please give the validator at least 10 seconds to operate before attempting to reset the status")
ErrPermissionDeniedNotSubmitter = fmt.Errorf("%w: You must be the submitter to perform this action", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionRelease = fmt.Errorf("%w: Need Role SubmissionRelease", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapfixUpload = fmt.Errorf("%w: Need Role MapfixUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapfixReview = fmt.Errorf("%w: Need Role MapfixReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionUpload = fmt.Errorf("%w: Need Role SubmissionUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionReview = fmt.Errorf("%w: Need Role SubmissionReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapDownload = fmt.Errorf("%w: Need Role MapDownload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleScriptWrite = fmt.Errorf("%w: Need Role ScriptWrite", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMaptest = fmt.Errorf("%w: Need Role Maptest", ErrPermissionDenied)
ErrNegativeID = errors.New("A negative ID was provided")
)
type Service struct {
DB datastore.Datastore
Nats nats.JetStreamContext
Maps maps.MapsServiceClient
Users users.UsersServiceClient
db datastore.Datastore
nats nats.JetStreamContext
maps maps.MapsServiceClient
users users.UsersServiceClient
}
// NewError creates *ErrorStatusCode from error returned by handler.
//
// Used for common default response.
func (svc *Service) NewError(ctx context.Context, err error) *api.ErrorStatusCode {
status := 500
if errors.Is(err, datastore.ErrNotExist) {
status = 404
}
if errors.Is(err, ErrPermissionDenied) {
status = 403
}
if errors.Is(err, ErrUserInfo) {
status = 401
}
return &api.ErrorStatusCode{
StatusCode: status,
Response: api.Error{
Code: int64(status),
Message: err.Error(),
},
func NewService(
db datastore.Datastore,
nats nats.JetStreamContext,
maps maps.MapsServiceClient,
users users.UsersServiceClient,
) Service {
return Service{
db: db,
nats: nats,
maps: maps,
users: users,
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,371 +0,0 @@
package service_internal
import (
"context"
"encoding/json"
"errors"
"fmt"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
var(
// prevent two mapfixes with same asset id
ActiveMapfixStatuses = []model.MapfixStatus{
model.MapfixStatusUploading,
model.MapfixStatusValidated,
model.MapfixStatusValidating,
model.MapfixStatusAcceptedUnvalidated,
model.MapfixStatusChangesRequested,
model.MapfixStatusSubmitted,
model.MapfixStatusUnderConstruction,
}
)
var(
ErrActiveMapfixSameAssetID = errors.New("There is an active mapfix with the same AssetID")
ErrNotAssetOwner = errors.New("You can only submit an asset you own")
)
// UpdateMapfixValidatedModel implements patchMapfixModel operation.
//
// Update model following role restrictions.
//
// POST /mapfixes/{MapfixID}/validated-model
func (svc *Service) UpdateMapfixValidatedModel(ctx context.Context, params internal.UpdateMapfixValidatedModelParams) error {
ValidatedModelID := uint64(params.ValidatedModelID)
ValidatedModelVersion := uint64(params.ValidatedModelVersion)
// check if Status is ChangesRequested|Submitted|UnderConstruction
pmap := datastore.Optional()
pmap.Add("validated_asset_id", ValidatedModelID)
pmap.Add("validated_asset_version", ValidatedModelVersion)
// DO NOT reset completed when validated model is updated
// pmap.Add("completed", false)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, pmap)
if err != nil {
return err
}
event_data := model.AuditEventDataChangeValidatedModel{
ValidatedModelID: ValidatedModelID,
ValidatedModelVersion: ValidatedModelVersion,
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeChangeValidatedModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
//
// Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
func (svc *Service) ActionMapfixSubmitted(ctx context.Context, params internal.ActionMapfixSubmittedParams) error {
// transaction
target_status := model.MapfixStatusSubmitted
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("asset_version", params.ModelVersion)
smap.Add("display_name", params.DisplayName)
smap.Add("creator", params.Creator)
smap.Add("game_id", params.GameID)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> RequestChanges.
//
// POST /mapfixes/{MapfixID}/status/validator-request-changes
func (svc *Service) ActionMapfixRequestChanges(ctx context.Context, params internal.ActionMapfixRequestChangesParams) error {
// transaction
target_status := model.MapfixStatusChangesRequested
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap)
if err != nil {
return err
}
{
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeError,
EventData: EventData,
})
if err != nil {
return err
}
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixValidate invokes actionMapfixValidate operation.
//
// Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
func (svc *Service) ActionMapfixValidated(ctx context.Context, params internal.ActionMapfixValidatedParams) error {
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusValidated)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
}
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
func (svc *Service) ActionMapfixAccepted(ctx context.Context, params internal.ActionMapfixAcceptedParams) error {
// transaction
target_status := model.MapfixStatusAcceptedUnvalidated
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
if err != nil {
return err
}
//push an error audit event
{
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeError,
EventData: EventData,
})
if err != nil {
return err
}
}
// push an action audit event
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
func (svc *Service) ActionMapfixUploaded(ctx context.Context, params internal.ActionMapfixUploadedParams) error {
// transaction
target_status := model.MapfixStatusUploaded
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUploading}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceMapfix,
ResourceID: params.MapfixID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// POST /mapfixes
func (svc *Service) CreateMapfix(ctx context.Context, request *internal.MapfixCreate) (*internal.MapfixID, error) {
// sanitization
if request.GameID<0||
request.AssetOwner<0||
request.AssetID<0||
request.AssetVersion<0||
request.TargetAssetID<0{
return nil, ErrNegativeID
}
var GameID=uint32(request.GameID);
var Submitter=uint64(request.AssetOwner);
var AssetID=uint64(request.AssetID);
var AssetVersion=uint64(request.AssetVersion);
var TargetAssetID=uint64(request.TargetAssetID);
// Check if an active mapfix with the same asset id exists
{
filter := datastore.Optional()
filter.Add("asset_id", request.AssetID)
filter.Add("asset_version", request.AssetVersion)
filter.Add("status_id", ActiveMapfixStatuses)
active_mapfixes, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
Number: 1,
Size: 1,
},datastore.ListSortDisabled)
if err != nil {
return nil, err
}
if len(active_mapfixes) != 0{
return nil, ErrActiveMapfixSameAssetID
}
}
operation, err := svc.DB.Operations().Get(ctx, request.OperationID)
if err != nil {
return nil, err
}
// check if user owns asset
// TODO: allow bypass by admin
if operation.Owner != Submitter {
return nil, ErrNotAssetOwner
}
mapfix, err := svc.DB.Mapfixes().Create(ctx, model.Mapfix{
ID: 0,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: GameID,
Submitter: Submitter,
AssetID: AssetID,
AssetVersion: AssetVersion,
Completed: false,
TargetAssetID: TargetAssetID,
StatusID: model.MapfixStatusUnderConstruction,
Description: request.Description,
})
if err != nil {
return nil, err
}
// mark the operation as completed and provide the path
pmap := datastore.Optional()
pmap.Add("status_id", model.OperationStatusCompleted)
pmap.Add("path", fmt.Sprintf("/mapfixes/%d", mapfix.ID))
err = svc.DB.Operations().Update(ctx, request.OperationID, pmap)
if err != nil {
return nil, err
}
return &internal.MapfixID{
MapfixID: mapfix.ID,
}, nil
}

View File

@@ -1,21 +0,0 @@
package service_internal
import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
// ActionOperationFailed implements actionOperationFailed operation.
//
// Fail the specified OperationID with a StatusMessage.
//
// POST /operations/{OperationID}/status/operation-failed
func (svc *Service) ActionOperationFailed(ctx context.Context, params internal.ActionOperationFailedParams) (error) {
pmap := datastore.Optional()
pmap.Add("status_id", model.OperationStatusFailed)
pmap.Add("status_message", params.StatusMessage)
return svc.DB.Operations().Update(ctx, params.OperationID, pmap)
}

View File

@@ -1,80 +0,0 @@
package service_internal
import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
api "git.itzana.me/strafesnet/maps-service/pkg/internal"
"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.ScriptPolicyID, error) {
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.ScriptPolicyID{
ScriptPolicyID: script.ID,
}, nil
}
// ListScriptPolicy implements listScriptPolicy operation.
//
// Get list of script policies.
//
// GET /script-policy
func (svc *Service) ListScriptPolicy(ctx context.Context, params api.ListScriptPolicyParams) ([]api.ScriptPolicy, error) {
filter := datastore.Optional()
if params.FromScriptHash.IsSet(){
hash, err := model.HashParse(params.FromScriptHash.Value)
if err != nil {
return nil, err
}
filter.Add("from_script_hash", int64(hash)) // No type safety!
}
if params.ToScriptID.IsSet(){
filter.Add("to_script_id", params.ToScriptID.Value)
}
if params.Policy.IsSet(){
filter.Add("policy", params.Policy.Value)
}
items, err := svc.DB.ScriptPolicy().List(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
})
if err != nil {
return nil, err
}
var resp []api.ScriptPolicy
for _, item := range items {
resp = append(resp, api.ScriptPolicy{
ID: item.ID,
FromScriptHash: model.HashFormat(uint64(item.FromScriptHash)),
ToScriptID: item.ToScriptID,
Policy: int32(item.Policy),
})
}
return resp, nil
}

View File

@@ -1,103 +0,0 @@
package service_internal
import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
api "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ScriptID, error) {
script, err := svc.DB.Scripts().Create(ctx, model.Script{
ID: 0,
Name: req.Name,
Hash: int64(model.HashSource(req.Source)),
Source: req.Source,
ResourceType: model.ResourceType(req.ResourceType),
ResourceID: req.ResourceID.Or(0),
})
if err != nil {
return nil, err
}
return &api.ScriptID{
ScriptID: script.ID,
}, nil
}
// ListScripts implements listScripts operation.
//
// Get list of scripts.
//
// GET /scripts
func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParams) ([]api.Script, error) {
filter := datastore.Optional()
if params.Hash.IsSet(){
hash, err := model.HashParse(params.Hash.Value)
if err != nil {
return nil, err
}
filter.Add("hash", int64(hash)) // No type safety!
}
if params.Name.IsSet(){
filter.Add("name", params.Name.Value)
}
if params.Source.IsSet(){
filter.Add("source", params.Source.Value)
}
if params.ResourceType.IsSet(){
filter.Add("resource_type", params.ResourceType.Value)
}
if params.ResourceID.IsSet(){
filter.Add("resource_id", params.ResourceID.Value)
}
items, err := svc.DB.Scripts().List(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
})
if err != nil {
return nil, err
}
var resp []api.Script
for _, item := range items {
resp = append(resp, api.Script{
ID: item.ID,
Hash: model.HashFormat(uint64(item.Hash)),
Source: item.Source,
ResourceType: int32(item.ResourceType),
ResourceID: item.ResourceID,
})
}
return resp, nil
}
// 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) {
script, err := svc.DB.Scripts().Get(ctx, params.ScriptID)
if err != nil {
return nil, err
}
return &api.Script{
ID: script.ID,
Name: script.Name,
Hash: model.HashFormat(uint64(script.Hash)),
Source: script.Source,
ResourceType: int32(script.ResourceType),
ResourceID: script.ResourceID,
}, nil
}

View File

@@ -1,39 +0,0 @@
package service_internal
import (
"context"
"errors"
"math"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
"github.com/nats-io/nats.go"
)
const (
ValidtorUserID uint64 = uint64(math.MaxInt64)
)
var (
ErrNegativeID = errors.New("A negative ID was provided")
)
type Service struct {
DB datastore.Datastore
Nats nats.JetStreamContext
}
// yay duplicate code
func (svc *Service) NewError(ctx context.Context, err error) *internal.ErrorStatusCode {
status := 500
if errors.Is(err, datastore.ErrNotExist) {
status = 404
}
return &internal.ErrorStatusCode{
StatusCode: status,
Response: internal.Error{
Code: int64(status),
Message: err.Error(),
},
}
}

View File

@@ -1,403 +0,0 @@
package service_internal
import (
"context"
"encoding/json"
"errors"
"fmt"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"git.itzana.me/strafesnet/maps-service/pkg/service"
)
var(
// prevent two mapfixes with same asset id
ActiveSubmissionStatuses = []model.SubmissionStatus{
model.SubmissionStatusUploading,
model.SubmissionStatusValidated,
model.SubmissionStatusValidating,
model.SubmissionStatusAcceptedUnvalidated,
model.SubmissionStatusChangesRequested,
model.SubmissionStatusSubmitted,
model.SubmissionStatusUnderConstruction,
}
)
var(
ErrActiveSubmissionSameAssetID = errors.New("There is an active submission with the same AssetID")
)
// UpdateSubmissionValidatedModel implements patchSubmissionModel operation.
//
// Update model following role restrictions.
//
// POST /submissions/{SubmissionID}/validated-model
func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params internal.UpdateSubmissionValidatedModelParams) error {
ValidatedModelID := uint64(params.ValidatedModelID)
ValidatedModelVersion := uint64(params.ValidatedModelVersion)
// check if Status is ChangesRequested|Submitted|UnderConstruction
pmap := datastore.Optional()
pmap.Add("validated_asset_id", ValidatedModelID)
pmap.Add("validated_asset_version", ValidatedModelVersion)
// DO NOT reset completed when validated model is updated
// pmap.Add("completed", false)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, pmap)
if err != nil {
return err
}
event_data := model.AuditEventDataChangeValidatedModel{
ValidatedModelID: ValidatedModelID,
ValidatedModelVersion: ValidatedModelVersion,
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeChangeValidatedModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
//
// Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
func (svc *Service) ActionSubmissionSubmitted(ctx context.Context, params internal.ActionSubmissionSubmittedParams) error {
// transaction
target_status := model.SubmissionStatusSubmitted
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("asset_version", params.ModelVersion)
smap.Add("display_name", params.DisplayName)
smap.Add("creator", params.Creator)
smap.Add("game_id", params.GameID)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> RequestChanges.
//
// POST /submissions/{SubmissionID}/status/validator-request-changes
func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params internal.ActionSubmissionRequestChangesParams) error {
// transaction
target_status := model.SubmissionStatusChangesRequested
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap)
if err != nil {
return err
}
//push an error audit event
{
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeError,
EventData: EventData,
})
if err != nil {
return err
}
}
// push an action audit event
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
//
// Role Validator changes status from Validating -> Validated.
//
// POST /submissions/{SubmissionID}/status/validator-validated
func (svc *Service) ActionSubmissionValidated(ctx context.Context, params internal.ActionSubmissionValidatedParams) error {
// transaction
target_status := model.SubmissionStatusValidated
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/validator-failed
func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params internal.ActionSubmissionAcceptedParams) error {
// transaction
target_status := model.SubmissionStatusAcceptedUnvalidated
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
if err != nil {
return err
}
//push an error audit event
{
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeError,
EventData: EventData,
})
if err != nil {
return err
}
}
// push an action audit event
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /submissions/{SubmissionID}/status/validator-uploaded
func (svc *Service) ActionSubmissionUploaded(ctx context.Context, params internal.ActionSubmissionUploadedParams) error {
// transaction
target_status := model.SubmissionStatusUploaded
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("uploaded_asset_id", params.UploadedAssetID)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: ValidtorUserID,
ResourceType: model.ResourceSubmission,
ResourceID: params.SubmissionID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
// POST /submissions
func (svc *Service) CreateSubmission(ctx context.Context, request *internal.SubmissionCreate) (*internal.SubmissionID, error) {
// sanitization
if request.GameID<0||
request.AssetOwner<0||
request.AssetID<0||
request.AssetVersion<0{
return nil, ErrNegativeID
}
var GameID=uint32(request.GameID);
var Submitter=uint64(request.AssetOwner);
var AssetID=uint64(request.AssetID);
var AssetVersion=uint64(request.AssetVersion);
var Status=model.SubmissionStatus(request.Status);
var roles=service.Roles(request.Roles);
// Check if an active submission with the same asset id exists
{
filter := datastore.Optional()
filter.Add("asset_id", request.AssetID)
filter.Add("asset_version", request.AssetVersion)
filter.Add("status_id", ActiveSubmissionStatuses)
active_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: 1,
Size: 1,
},datastore.ListSortDisabled)
if err != nil {
return nil, err
}
if len(active_submissions) != 0{
return nil, ErrActiveSubmissionSameAssetID
}
}
operation, err := svc.DB.Operations().Get(ctx, request.OperationID)
if err != nil {
return nil, err
}
// check if user owns asset
is_submitter := operation.Owner == Submitter
// check if user is map admin
has_submission_review := roles & service.RolesSubmissionReview == service.RolesSubmissionReview
// if neither, u not allowed
if !is_submitter && !has_submission_review {
return nil, ErrNotAssetOwner
}
submission, err := svc.DB.Submissions().Create(ctx, model.Submission{
ID: 0,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: GameID,
Submitter: Submitter,
AssetID: AssetID,
AssetVersion: AssetVersion,
Completed: false,
StatusID: Status,
})
if err != nil {
return nil, err
}
// mark the operation as completed and provide the path
pmap := datastore.Optional()
pmap.Add("status_id", model.OperationStatusCompleted)
pmap.Add("path", fmt.Sprintf("/submissions/%d", submission.ID))
err = svc.DB.Operations().Update(ctx, request.OperationID, pmap)
if err != nil {
return nil, err
}
return &internal.SubmissionID{
SubmissionID: submission.ID,
}, nil
}

View File

@@ -0,0 +1,380 @@
package validator_controller
import (
"context"
"errors"
"fmt"
"git.itzana.me/strafesnet/go-grpc/validator"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"git.itzana.me/strafesnet/maps-service/pkg/service"
)
type Mapfixes struct {
*validator.UnimplementedValidatorMapfixServiceServer
inner *service.Service
}
func NewMapfixesController(
inner *service.Service,
) Mapfixes {
return Mapfixes{
inner: inner,
}
}
var(
// prevent two mapfixes with same asset id
ActiveMapfixStatuses = []model.MapfixStatus{
model.MapfixStatusUploading,
model.MapfixStatusValidated,
model.MapfixStatusValidating,
model.MapfixStatusAcceptedUnvalidated,
model.MapfixStatusChangesRequested,
model.MapfixStatusSubmitted,
model.MapfixStatusUnderConstruction,
}
)
var(
ErrActiveMapfixSameAssetID = errors.New("There is an active mapfix with the same AssetID")
ErrNotAssetOwner = errors.New("You can only submit an asset you own")
)
// UpdateMapfixValidatedModel implements patchMapfixModel operation.
//
// Update model following role restrictions.
//
// POST /mapfixes/{MapfixID}/validated-model
func (svc *Mapfixes) SetValidatedModel(ctx context.Context, params *validator.ValidatedModelRequest) (*validator.NullResponse, error) {
MapfixID := int64(params.ID)
// check if Status is ChangesRequested|Submitted|UnderConstruction
update := service.NewMapfixUpdate()
update.SetValidatedAssetID(params.ValidatedModelID)
update.SetValidatedAssetVersion(params.ValidatedModelVersion)
// DO NOT reset completed when validated model is updated
// update.Add("completed", false)
allow_statuses := []model.MapfixStatus{model.MapfixStatusValidating}
err := svc.inner.UpdateMapfixIfStatus(ctx, MapfixID, allow_statuses, update)
if err != nil {
return nil, err
}
event_data := model.AuditEventDataChangeValidatedModel{
ValidatedModelID: params.ValidatedModelID,
ValidatedModelVersion: params.ValidatedModelVersion,
}
err = svc.inner.CreateAuditEventChangeValidatedModel(
ctx,
model.ValidatorUserID,
model.Resource{
ID: MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
//
// Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
func (svc *Mapfixes) SetStatusSubmitted(ctx context.Context, params *validator.SubmittedRequest) (*validator.NullResponse, error) {
MapfixID := int64(params.ID)
// transaction
target_status := model.MapfixStatusSubmitted
update := service.NewMapfixUpdate()
update.SetStatusID(target_status)
update.SetAssetVersion(uint64(params.ModelVersion))
update.SetDisplayName(params.DisplayName)
update.SetCreator(params.Creator)
update.SetGameID(uint32(params.GameID))
allow_statuses := []model.MapfixStatus{model.MapfixStatusSubmitting}
err := svc.inner.UpdateMapfixIfStatus(ctx, MapfixID, allow_statuses, update)
if err != nil {
return nil, err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
err = svc.inner.CreateAuditEventAction(
ctx,
model.ValidatorUserID,
model.Resource{
ID: MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> RequestChanges.
//
// POST /mapfixes/{MapfixID}/status/validator-request-changes
func (svc *Mapfixes) SetStatusRequestChanges(ctx context.Context, params *validator.MapfixID) (*validator.NullResponse, error) {
MapfixID := int64(params.ID)
// transaction
target_status := model.MapfixStatusChangesRequested
update := service.NewMapfixUpdate()
update.SetStatusID(target_status)
allow_statuses := []model.MapfixStatus{model.MapfixStatusSubmitting}
err := svc.inner.UpdateMapfixIfStatus(ctx, MapfixID, allow_statuses, update)
if err != nil {
return nil, err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
err = svc.inner.CreateAuditEventAction(
ctx,
model.ValidatorUserID,
model.Resource{
ID: MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// ActionMapfixValidate invokes actionMapfixValidate operation.
//
// Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
func (svc *Mapfixes) SetStatusValidated(ctx context.Context, params *validator.MapfixID) (*validator.NullResponse, error) {
MapfixID := int64(params.ID)
// transaction
update := service.NewMapfixUpdate()
update.SetStatusID(model.MapfixStatusValidated)
allow_statuses := []model.MapfixStatus{model.MapfixStatusValidating}
err := svc.inner.UpdateMapfixIfStatus(ctx, MapfixID, allow_statuses, update)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
func (svc *Mapfixes) SetStatusFailed(ctx context.Context, params *validator.MapfixID) (*validator.NullResponse, error) {
MapfixID := int64(params.ID)
// transaction
target_status := model.MapfixStatusAcceptedUnvalidated
update := service.NewMapfixUpdate()
update.SetStatusID(target_status)
allow_statuses := []model.MapfixStatus{model.MapfixStatusValidating}
err := svc.inner.UpdateMapfixIfStatus(ctx, MapfixID, allow_statuses, update)
if err != nil {
return nil, err
}
// push an action audit event
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
err = svc.inner.CreateAuditEventAction(
ctx,
model.ValidatorUserID,
model.Resource{
ID: MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
func (svc *Mapfixes) SetStatusUploaded(ctx context.Context, params *validator.MapfixID) (*validator.NullResponse, error) {
MapfixID := int64(params.ID)
// transaction
target_status := model.MapfixStatusUploaded
update := service.NewMapfixUpdate()
update.SetStatusID(target_status)
allow_statuses := []model.MapfixStatus{model.MapfixStatusUploading}
err := svc.inner.UpdateMapfixIfStatus(ctx, MapfixID, allow_statuses, update)
if err != nil {
return nil, err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
err = svc.inner.CreateAuditEventAction(
ctx,
model.ValidatorUserID,
model.Resource{
ID: MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// CreateMapfixAuditError implements createMapfixAuditError operation.
//
// Post an error to the audit log
//
// POST /mapfixes/{MapfixID}/error
func (svc *Mapfixes) CreateAuditError(ctx context.Context, params *validator.AuditErrorRequest) (*validator.NullResponse, error) {
MapfixID := int64(params.ID)
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
err := svc.inner.CreateAuditEventError(
ctx,
model.ValidatorUserID,
model.Resource{
ID: MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// CreateMapfixAuditCheckList implements createMapfixAuditCheckList operation.
//
// Post a checklist to the audit log
//
// POST /mapfixes/{MapfixID}/checklist
func (svc *Mapfixes) CreateAuditChecklist(ctx context.Context, params *validator.AuditChecklistRequest) (*validator.NullResponse, error) {
MapfixID := int64(params.ID)
check_list := make([]model.Check, len(params.CheckList))
for i, check := range params.CheckList {
check_list[i] = model.Check{
Name: check.Name,
Summary: check.Summary,
Passed: check.Passed,
}
}
event_data := model.AuditEventDataCheckList{
CheckList: check_list,
}
err := svc.inner.CreateAuditEventCheckList(
ctx,
model.ValidatorUserID,
model.Resource{
ID: MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// POST /mapfixes
func (svc *Mapfixes) Create(ctx context.Context, request *validator.MapfixCreate) (*validator.MapfixID, error) {
var Submitter=request.AssetOwner;
// Check if an active mapfix with the same asset id exists
{
filter := service.NewMapfixFilter()
filter.SetAssetID(request.AssetID)
filter.SetAssetVersion(request.AssetVersion)
filter.SetStatuses(ActiveMapfixStatuses)
active_mapfixes, err := svc.inner.ListMapfixes(ctx, filter, model.Page{
Number: 1,
Size: 1,
},datastore.ListSortDisabled)
if err != nil {
return nil, err
}
if len(active_mapfixes) != 0{
return nil, ErrActiveMapfixSameAssetID
}
}
OperationID := int32(request.OperationID)
operation, err := svc.inner.GetOperation(ctx, OperationID)
if err != nil {
return nil, err
}
// check if user owns asset
// TODO: allow bypass by admin
if operation.Owner != Submitter {
return nil, ErrNotAssetOwner
}
mapfix, err := svc.inner.CreateMapfix(ctx, model.Mapfix{
ID: 0,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: request.GameID,
Submitter: Submitter,
AssetID: request.AssetID,
AssetVersion: request.AssetVersion,
Completed: false,
TargetAssetID: request.TargetAssetID,
StatusID: model.MapfixStatusUnderConstruction,
Description: request.Description,
})
if err != nil {
return nil, err
}
// mark the operation as completed and provide the path
params := service.NewOperationCompleteParams(fmt.Sprintf("/mapfixes/%d", mapfix.ID))
err = svc.inner.CompleteOperation(ctx, OperationID, params)
if err != nil {
return nil, err
}
return &validator.MapfixID{
ID: uint64(mapfix.ID),
}, nil
}

View File

@@ -0,0 +1,37 @@
package validator_controller
import (
"context"
"git.itzana.me/strafesnet/go-grpc/validator"
"git.itzana.me/strafesnet/maps-service/pkg/service"
)
type Operations struct {
*validator.UnimplementedValidatorOperationServiceServer
inner *service.Service
}
func NewOperationsController(
inner *service.Service,
) Operations {
return Operations{
inner: inner,
}
}
// ActionOperationFailed implements actionOperationFailed operation.
//
// Fail the specified OperationID with a StatusMessage.
//
// POST /operations/{OperationID}/status/operation-failed
func (svc *Operations) Fail(ctx context.Context, params *validator.OperationFailRequest) (*validator.NullResponse, error) {
fail_params := service.NewOperationFailParams(
params.StatusMessage,
)
err := svc.inner.FailOperation(ctx, int32(params.OperationID), fail_params)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}

View File

@@ -0,0 +1,89 @@
package validator_controller
import (
"context"
"git.itzana.me/strafesnet/go-grpc/validator"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"git.itzana.me/strafesnet/maps-service/pkg/service"
)
type ScriptPolicy struct {
*validator.UnimplementedValidatorScriptPolicyServiceServer
inner *service.Service
}
func NewScriptPolicyController(
inner *service.Service,
) ScriptPolicy {
return ScriptPolicy{
inner: inner,
}
}
// CreateScriptPolicy implements createScriptPolicy operation.
//
// Create a new script policy.
//
// POST /script-policy
func (svc *ScriptPolicy) Create(ctx context.Context, req *validator.ScriptPolicyCreate) (*validator.ScriptPolicyID, error) {
from_script, err := svc.inner.GetScript(ctx, int64(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.inner.CreateScriptPolicy(ctx, model.ScriptPolicy{
ID: 0,
FromScriptHash: from_script.Hash,
ToScriptID: int64(req.ToScriptID),
Policy: model.Policy(req.Policy),
})
if err != nil {
return nil, err
}
return &validator.ScriptPolicyID{
ID: uint64(script.ID),
}, nil
}
// ListScriptPolicy implements listScriptPolicy operation.
//
// Get list of script policies.
//
// GET /script-policy
func (svc *ScriptPolicy) List(ctx context.Context, params *validator.ScriptPolicyListRequest) (*validator.ScriptPolicyListResponse, error) {
filter := service.NewScriptPolicyFilter()
if params.Filter.FromScriptHash != nil {
filter.SetFromScriptHash(int64(*params.Filter.FromScriptHash))
}
if params.Filter.ToScriptID != nil {
filter.SetToScriptID(int64(*params.Filter.ToScriptID))
}
if params.Filter.Policy != nil {
filter.SetPolicy(int32(*params.Filter.Policy))
}
items, err := svc.inner.ListScriptPolicies(ctx, filter, model.Page{
Number: int32(params.Page.Number),
Size: int32(params.Page.Size),
})
if err != nil {
return nil, err
}
resp := validator.ScriptPolicyListResponse{}
resp.ScriptPolicies = make([]*validator.ScriptPolicy, len(items))
for i, item := range items {
resp.ScriptPolicies[i] = &validator.ScriptPolicy{
ID: uint64(item.ID),
FromScriptHash: uint64(item.FromScriptHash),
ToScriptID: uint64(item.ToScriptID),
Policy: validator.Policy(int32(item.Policy)),
}
}
return &resp, nil
}

View File

@@ -0,0 +1,119 @@
package validator_controller
import (
"context"
"git.itzana.me/strafesnet/go-grpc/validator"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"git.itzana.me/strafesnet/maps-service/pkg/service"
)
type Scripts struct {
*validator.UnimplementedValidatorScriptServiceServer
inner *service.Service
}
func NewScriptsController(
inner *service.Service,
) Scripts {
return Scripts{
inner: inner,
}
}
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
func (svc *Scripts) Create(ctx context.Context, req *validator.ScriptCreate) (*validator.ScriptID, error) {
ResourceID := int64(0)
if req.ResourceID != nil {
ResourceID = int64(*req.ResourceID)
}
script, err := svc.inner.CreateScript(ctx, model.Script{
ID: 0,
Name: req.Name,
Hash: int64(model.HashSource(req.Source)),
Source: req.Source,
ResourceType: model.ResourceType(req.ResourceType),
ResourceID: ResourceID,
})
if err != nil {
return nil, err
}
return &validator.ScriptID{
ID: uint64(script.ID),
}, nil
}
// ListScripts implements listScripts operation.
//
// Get list of scripts.
//
// GET /scripts
func (svc *Scripts) List(ctx context.Context, params *validator.ScriptListRequest) (*validator.ScriptListResponse, error) {
filter := service.NewScriptFilter()
if params.Filter.Hash != nil {
filter.SetHash(int64(*params.Filter.Hash))
}
if params.Filter.Name != nil {
filter.SetName(*params.Filter.Name)
}
if params.Filter.Source != nil {
filter.SetSource(*params.Filter.Source)
}
if params.Filter.ResourceType != nil {
filter.SetResourceType(int32(*params.Filter.ResourceType))
}
if params.Filter.ResourceID != nil {
filter.SetResourceID(int64(*params.Filter.ResourceID))
}
items, err := svc.inner.ListScripts(ctx, filter, model.Page{
Number: int32(params.Page.Number),
Size: int32(params.Page.Size),
})
if err != nil {
return nil, err
}
resp := validator.ScriptListResponse{}
resp.Scripts = make([]*validator.Script, len(items))
for i, item := range items {
resource_id := uint64(item.ResourceID)
resp.Scripts[i] = &validator.Script{
ID: uint64(item.ID),
Name: item.Name,
Hash: uint64(item.Hash),
Source: item.Source,
ResourceType: validator.ResourceType(item.ResourceType),
ResourceID: &resource_id,
}
}
return &resp, nil
}
// GetScript implements getScript operation.
//
// Get the specified script by ID.
//
// GET /scripts/{ScriptID}
func (svc *Scripts) Get(ctx context.Context, params *validator.ScriptID) (*validator.Script, error) {
ScriptID := int64(params.ID)
script, err := svc.inner.GetScript(ctx, ScriptID)
if err != nil {
return nil, err
}
ResourceID := uint64(script.ResourceID)
return &validator.Script{
ID: uint64(script.ID),
Name: script.Name,
Hash: uint64(script.Hash),
Source: script.Source,
ResourceType: validator.ResourceType(script.ResourceType),
ResourceID: &ResourceID,
}, nil
}

View File

@@ -0,0 +1,403 @@
package validator_controller
import (
"context"
"errors"
"fmt"
"git.itzana.me/strafesnet/go-grpc/validator"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"git.itzana.me/strafesnet/maps-service/pkg/service"
)
type Submissions struct {
*validator.UnimplementedValidatorSubmissionServiceServer
inner *service.Service
}
func NewSubmissionsController(
inner *service.Service,
) Submissions {
return Submissions{
inner: inner,
}
}
var(
// prevent two mapfixes with same asset id
ActiveSubmissionStatuses = []model.SubmissionStatus{
model.SubmissionStatusUploading,
model.SubmissionStatusValidated,
model.SubmissionStatusValidating,
model.SubmissionStatusAcceptedUnvalidated,
model.SubmissionStatusChangesRequested,
model.SubmissionStatusSubmitted,
model.SubmissionStatusUnderConstruction,
}
)
var(
ErrActiveSubmissionSameAssetID = errors.New("There is an active submission with the same AssetID")
)
// UpdateSubmissionValidatedModel implements patchSubmissionModel operation.
//
// Update model following role restrictions.
//
// POST /submissions/{SubmissionID}/validated-model
func (svc *Submissions) SetValidatedModel(ctx context.Context, params *validator.ValidatedModelRequest) (*validator.NullResponse, error) {
SubmissionID := int64(params.ID)
// check if Status is ChangesRequested|Submitted|UnderConstruction
update := service.NewSubmissionUpdate()
update.SetValidatedAssetID(params.ValidatedModelID)
update.SetValidatedAssetVersion(params.ValidatedModelVersion)
// DO NOT reset completed when validated model is updated
// update.Add("completed", false)
allowed_statuses := []model.SubmissionStatus{model.SubmissionStatusValidating}
err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update)
if err != nil {
return nil, err
}
event_data := model.AuditEventDataChangeValidatedModel{
ValidatedModelID: params.ValidatedModelID,
ValidatedModelVersion: params.ValidatedModelVersion,
}
err = svc.inner.CreateAuditEventChangeValidatedModel(
ctx,
model.ValidatorUserID,
model.Resource{
ID: SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
//
// Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
func (svc *Submissions) SetStatusSubmitted(ctx context.Context, params *validator.SubmittedRequest) (*validator.NullResponse, error) {
SubmissionID := int64(params.ID)
// transaction
target_status := model.SubmissionStatusSubmitted
update := service.NewSubmissionUpdate()
update.SetStatusID(target_status)
update.SetAssetVersion(uint64(params.ModelVersion))
update.SetDisplayName(params.DisplayName)
update.SetCreator(params.Creator)
update.SetGameID(uint32(params.GameID))
allowed_statuses := []model.SubmissionStatus{model.SubmissionStatusSubmitting}
err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update)
if err != nil {
return nil, err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
err = svc.inner.CreateAuditEventAction(
ctx,
model.ValidatorUserID,
model.Resource{
ID: SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> RequestChanges.
//
// POST /submissions/{SubmissionID}/status/validator-request-changes
func (svc *Submissions) SetStatusRequestChanges(ctx context.Context, params *validator.SubmissionID) (*validator.NullResponse, error) {
SubmissionID := int64(params.ID)
// transaction
target_status := model.SubmissionStatusChangesRequested
update := service.NewSubmissionUpdate()
update.SetStatusID(target_status)
allowed_statuses :=[]model.SubmissionStatus{model.SubmissionStatusSubmitting}
err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update)
if err != nil {
return nil, err
}
// push an action audit event
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
err = svc.inner.CreateAuditEventAction(
ctx,
model.ValidatorUserID,
model.Resource{
ID: SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
//
// Role Validator changes status from Validating -> Validated.
//
// POST /submissions/{SubmissionID}/status/validator-validated
func (svc *Submissions) SetStatusValidated(ctx context.Context, params *validator.SubmissionID) (*validator.NullResponse, error) {
SubmissionID := int64(params.ID)
// transaction
target_status := model.SubmissionStatusValidated
update := service.NewSubmissionUpdate()
update.SetStatusID(target_status)
allowed_statuses :=[]model.SubmissionStatus{model.SubmissionStatusValidating}
err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update)
if err != nil {
return nil, err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
err = svc.inner.CreateAuditEventAction(
ctx,
model.ValidatorUserID,
model.Resource{
ID: SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/validator-failed
func (svc *Submissions) SetStatusFailed(ctx context.Context, params *validator.SubmissionID) (*validator.NullResponse, error) {
SubmissionID := int64(params.ID)
// transaction
target_status := model.SubmissionStatusAcceptedUnvalidated
update := service.NewSubmissionUpdate()
update.SetStatusID(target_status)
allowed_statuses :=[]model.SubmissionStatus{model.SubmissionStatusValidating}
err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update)
if err != nil {
return nil, err
}
// push an action audit event
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
err = svc.inner.CreateAuditEventAction(
ctx,
model.ValidatorUserID,
model.Resource{
ID: SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /submissions/{SubmissionID}/status/validator-uploaded
func (svc *Submissions) SetStatusUploaded(ctx context.Context, params *validator.StatusUploadedRequest) (*validator.NullResponse, error) {
SubmissionID := int64(params.ID)
// transaction
target_status := model.SubmissionStatusUploaded
update := service.NewSubmissionUpdate()
update.SetStatusID(target_status)
update.SetUploadedAssetID(params.UploadedAssetID)
allowed_statuses :=[]model.SubmissionStatus{model.SubmissionStatusUploading}
err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update)
if err != nil {
return nil, err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
err = svc.inner.CreateAuditEventAction(
ctx,
model.ValidatorUserID,
model.Resource{
ID: SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// CreateSubmissionAuditError implements createSubmissionAuditError operation.
//
// Post an error to the audit log
//
// POST /submissions/{SubmissionID}/error
func (svc *Submissions) CreateAuditError(ctx context.Context, params *validator.AuditErrorRequest) (*validator.NullResponse, error) {
SubmissionID := int64(params.ID)
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
err := svc.inner.CreateAuditEventError(
ctx,
model.ValidatorUserID,
model.Resource{
ID: SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// CreateSubmissionAuditCheckList implements createSubmissionAuditCheckList operation.
//
// Post a checklist to the audit log
//
// POST /submissions/{SubmissionID}/checklist
func (svc *Submissions) CreateAuditChecklist(ctx context.Context, params *validator.AuditChecklistRequest) (*validator.NullResponse, error) {
SubmissionID := int64(params.ID)
check_list := make([]model.Check, len(params.CheckList))
for i, check := range params.CheckList {
check_list[i] = model.Check{
Name: check.Name,
Summary: check.Summary,
Passed: check.Passed,
}
}
event_data := model.AuditEventDataCheckList{
CheckList: check_list,
}
err := svc.inner.CreateAuditEventCheckList(
ctx,
model.ValidatorUserID,
model.Resource{
ID: SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
if err != nil {
return nil, err
}
return &validator.NullResponse{}, nil
}
// POST /submissions
func (svc *Submissions) Create(ctx context.Context, request *validator.SubmissionCreate) (*validator.SubmissionID, error) {
var Submitter=uint64(request.AssetOwner);
var Status=model.SubmissionStatus(request.Status);
var roles=model.Roles(request.Roles);
// Check if an active submission with the same asset id exists
{
filter := service.NewSubmissionFilter()
filter.SetAssetID(request.AssetID)
filter.SetAssetVersion(request.AssetVersion)
filter.SetStatuses(ActiveSubmissionStatuses)
active_submissions, err := svc.inner.ListSubmissions(ctx, filter, model.Page{
Number: 1,
Size: 1,
},datastore.ListSortDisabled)
if err != nil {
return nil, err
}
if len(active_submissions) != 0{
return nil, ErrActiveSubmissionSameAssetID
}
}
operation_id := int32(request.OperationID)
operation, err := svc.inner.GetOperation(ctx, operation_id)
if err != nil {
return nil, err
}
// check if user owns asset
is_submitter := operation.Owner == Submitter
// check if user is map admin
has_submission_review := roles & model.RolesSubmissionReview == model.RolesSubmissionReview
// if neither, u not allowed
if !is_submitter && !has_submission_review {
return nil, ErrNotAssetOwner
}
submission, err := svc.inner.CreateSubmission(ctx, model.Submission{
ID: 0,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: request.GameID,
Submitter: Submitter,
AssetID: request.AssetID,
AssetVersion: request.AssetVersion,
Completed: false,
StatusID: Status,
})
if err != nil {
return nil, err
}
// mark the operation as completed and provide the path
params := service.NewOperationCompleteParams(fmt.Sprintf("/submissions/%d", submission.ID))
err = svc.inner.CompleteOperation(ctx, operation_id, params)
if err != nil {
return nil, err
}
return &validator.SubmissionID{
ID: uint64(submission.ID),
}, nil
}

1068
pkg/web_api/mapfixes.go Normal file

File diff suppressed because it is too large Load Diff

140
pkg/web_api/maps.go Normal file
View File

@@ -0,0 +1,140 @@
package web_api
import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"git.itzana.me/strafesnet/maps-service/pkg/roblox"
"git.itzana.me/strafesnet/maps-service/pkg/service"
)
// MigrateMaps implements migrateMaps operation.
//
// Perform maps migration.
//
// POST /migrate
func (svc *Service) MigrateMaps(ctx context.Context) (error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleSubmissionRelease()
if err != nil {
return err
}
if !has_role {
return ErrPermissionDeniedNeedRoleSubmissionRelease
}
return svc.inner.TEMP_DoMapsMigration(ctx)
}
// ListMaps implements listMaps operation.
//
// Get list of maps.
//
// GET /maps
func (svc *Service) ListMaps(ctx context.Context, params api.ListMapsParams) ([]api.Map, error) {
filter := service.NewMapFilter()
if display_name, display_name_ok := params.DisplayName.Get(); display_name_ok{
filter.SetDisplayName(display_name)
}
if creator, creator_ok := params.Creator.Get(); creator_ok{
filter.SetCreator(creator)
}
if game_id, game_id_ok := params.GameID.Get(); game_id_ok{
filter.SetGameID(uint32(game_id))
}
items, err := svc.inner.ListMaps(ctx,
filter,
model.Page{
Size: params.Limit,
Number: params.Page,
},
)
if err != nil {
return nil, err
}
var resp []api.Map
for _, item := range items {
resp = append(resp, api.Map{
ID: item.ID,
DisplayName: item.DisplayName,
Creator: item.Creator,
GameID: int32(item.GameID),
Date: item.Date.Unix(),
})
}
return resp, nil
}
// GetMap implements getScript operation.
//
// Get the specified script by ID.
//
// GET /maps/{MapID}
func (svc *Service) GetMap(ctx context.Context, params api.GetMapParams) (*api.Map, error) {
mapResponse, err := svc.inner.GetMap(ctx, params.MapID)
if err != nil {
return nil, err
}
return &api.Map{
ID: mapResponse.ID,
DisplayName: mapResponse.DisplayName,
Creator: mapResponse.Creator,
GameID: int32(mapResponse.GameID),
Date: mapResponse.Date.Unix(),
}, nil
}
// DownloadMapAsset invokes downloadMapAsset operation.
//
// Download the map asset.
//
// GET /maps/{MapID}/download
func (svc *Service) DownloadMapAsset(ctx context.Context, params api.DownloadMapAssetParams) (ok api.DownloadMapAssetOK, err error) {
userInfo, success := ctx.Value("UserInfo").(UserInfoHandle)
if !success {
return ok, ErrUserInfo
}
has_role, err := userInfo.HasRoleMapDownload()
if err != nil {
return ok, err
}
if !has_role {
return ok, ErrPermissionDeniedNeedRoleMapDownload
}
// Ensure map exists in the db!
// This could otherwise be used to access any asset
_, err = svc.inner.GetMap(ctx, params.MapID)
if err != nil {
return ok, err
}
info, err := svc.roblox.GetAssetLocation(roblox.GetAssetLatestRequest{
AssetID: uint64(params.MapID),
})
if err != nil{
return ok, err
}
// download the complete file
asset, err := svc.roblox.DownloadAsset(info)
if err != nil{
return ok, err
}
ok.Data = asset
return ok, nil
}

46
pkg/web_api/operations.go Normal file
View File

@@ -0,0 +1,46 @@
package web_api
import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/api"
)
// GetOperation implements getOperation operation.
//
// Get the specified operation by ID.
//
// GET /operations/{OperationID}
func (svc *Service) GetOperation(ctx context.Context, params api.GetOperationParams) (*api.Operation, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
// You must be the operation owner to read it
operation, err := svc.inner.GetOperation(ctx, params.OperationID)
if err != nil {
return nil, err
}
userId, err := userInfo.GetUserID()
if err != nil {
return nil, err
}
// check if caller is the submitter
has_role := userId == operation.Owner
if !has_role {
return nil, ErrPermissionDeniedNotSubmitter
}
return &api.Operation{
OperationID: operation.ID,
Date: operation.CreatedAt.Unix(),
Owner: int64(operation.Owner),
Status: int32(operation.StatusID),
StatusMessage: operation.StatusMessage,
Path: operation.Path,
}, nil
}

View File

@@ -0,0 +1,149 @@
package web_api
import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"git.itzana.me/strafesnet/maps-service/pkg/service"
)
// CreateScriptPolicy implements createScriptPolicy operation.
//
// Create a new script policy.
//
// POST /script-policy
func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolicyCreate) (*api.ScriptPolicyID, error) {
err := CheckHasRoleScriptWrite(ctx)
if err != nil {
return nil, err
}
from_script, err := svc.inner.GetScript(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.inner.CreateScriptPolicy(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.ScriptPolicyID{
ScriptPolicyID: script.ID,
}, nil
}
// ListScriptPolicy implements listScriptPolicy operation.
//
// Get list of script policies.
//
// GET /script-policy
func (svc *Service) ListScriptPolicy(ctx context.Context, params api.ListScriptPolicyParams) ([]api.ScriptPolicy, error) {
filter := service.NewScriptPolicyFilter()
if hash_hex, ok := params.FromScriptHash.Get(); ok{
hash_parsed, err := model.HashParse(hash_hex)
if err != nil {
return nil, err
}
filter.SetFromScriptHash(int64(hash_parsed))
}
if to_script_id, to_script_id_ok := params.ToScriptID.Get(); to_script_id_ok{
filter.SetToScriptID(to_script_id)
}
if policy, policy_ok := params.Policy.Get(); policy_ok{
filter.SetPolicy(policy)
}
items, err := svc.inner.ListScriptPolicies(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
})
if err != nil {
return nil, err
}
var resp []api.ScriptPolicy
for _, item := range items {
resp = append(resp, api.ScriptPolicy{
ID: item.ID,
FromScriptHash: model.HashFormat(uint64(item.FromScriptHash)),
ToScriptID: item.ToScriptID,
Policy: int32(item.Policy),
})
}
return resp, nil
}
// DeleteScriptPolicy implements deleteScriptPolicy operation.
//
// Delete the specified script policy by ID.
//
// DELETE /script-policy/{ScriptPolicyID}
func (svc *Service) DeleteScriptPolicy(ctx context.Context, params api.DeleteScriptPolicyParams) error {
err := CheckHasRoleScriptWrite(ctx)
if err != nil {
return err
}
return svc.inner.DeleteScriptPolicy(ctx, params.ScriptPolicyID)
}
// GetScriptPolicy implements getScriptPolicy operation.
//
// Get the specified script policy by ID.
//
// GET /script-policy/{ScriptPolicyID}
func (svc *Service) GetScriptPolicy(ctx context.Context, params api.GetScriptPolicyParams) (*api.ScriptPolicy, error) {
policy, err := svc.inner.GetScriptPolicy(ctx, params.ScriptPolicyID)
if err != nil {
return nil, err
}
return &api.ScriptPolicy{
ID: policy.ID,
FromScriptHash: model.HashFormat(uint64(policy.FromScriptHash)),
ToScriptID: policy.ToScriptID,
Policy: int32(policy.Policy),
}, nil
}
// UpdateScriptPolicy implements updateScriptPolicy operation.
//
// Update the specified script policy by ID.
//
// POST /script-policy/{ScriptPolicyID}
func (svc *Service) UpdateScriptPolicy(ctx context.Context, req *api.ScriptPolicyUpdate, params api.UpdateScriptPolicyParams) error {
err := CheckHasRoleScriptWrite(ctx)
if err != nil {
return err
}
filter := service.NewScriptPolicyFilter()
if from_script_id, ok := req.FromScriptID.Get(); ok {
from_script, err := svc.inner.GetScript(ctx, from_script_id)
if err != nil {
return err
}
filter.SetFromScriptHash(from_script.Hash)
}
if to_script_id, to_script_id_ok := req.ToScriptID.Get(); to_script_id_ok{
filter.SetToScriptID(to_script_id)
}
if policy, policy_ok := req.Policy.Get(); policy_ok{
filter.SetPolicy(policy)
}
return svc.inner.UpdateScriptPolicy(ctx, req.ID, filter)
}

170
pkg/web_api/scripts.go Normal file
View File

@@ -0,0 +1,170 @@
package web_api
import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"git.itzana.me/strafesnet/maps-service/pkg/service"
)
func CheckHasRoleScriptWrite(ctx context.Context) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return ErrUserInfo
}
has_role, err := userInfo.HasRoleScriptWrite()
if err != nil {
return err
}
if !has_role {
return ErrPermissionDeniedNeedRoleScriptWrite
}
return nil
}
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ScriptID, error) {
err := CheckHasRoleScriptWrite(ctx)
if err != nil {
return nil, err
}
script, err := svc.inner.CreateScript(ctx, model.Script{
ID: 0,
Name: req.Name,
Hash: int64(model.HashSource(req.Source)),
Source: req.Source,
ResourceType: model.ResourceType(req.ResourceType),
ResourceID: req.ResourceID.Or(0),
})
if err != nil {
return nil, err
}
return &api.ScriptID{
ScriptID: script.ID,
}, nil
}
// ListScripts implements listScripts operation.
//
// Get list of scripts.
//
// GET /scripts
func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParams) ([]api.Script, error) {
filter := service.NewScriptFilter()
if hash_hex, ok := params.Hash.Get(); ok{
hash_parsed, err := model.HashParse(hash_hex)
if err != nil {
return nil, err
}
filter.SetHash(int64(hash_parsed))
}
if name, name_ok := params.Name.Get(); name_ok{
filter.SetName(name)
}
if source, source_ok := params.Source.Get(); source_ok{
filter.SetSource(source)
}
if resource_type, resource_type_ok := params.ResourceType.Get(); resource_type_ok{
filter.SetResourceType(resource_type)
}
if resource_id, resource_id_ok := params.ResourceID.Get(); resource_id_ok{
filter.SetResourceID(resource_id)
}
items, err := svc.inner.ListScripts(ctx, filter, model.Page{
Number: params.Page,
Size: params.Limit,
})
if err != nil {
return nil, err
}
var resp []api.Script
for _, item := range items {
resp = append(resp, api.Script{
ID: item.ID,
Name: item.Name,
Hash: model.HashFormat(uint64(item.Hash)),
Source: item.Source,
ResourceType: int32(item.ResourceType),
ResourceID: item.ResourceID,
})
}
return resp, 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 {
err := CheckHasRoleScriptWrite(ctx)
if err != nil {
return err
}
return svc.inner.DeleteScript(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) {
script, err := svc.inner.GetScript(ctx, params.ScriptID)
if err != nil {
return nil, err
}
return &api.Script{
ID: script.ID,
Name: script.Name,
Hash: model.HashFormat(uint64(script.Hash)),
Source: script.Source,
ResourceType: int32(script.ResourceType),
ResourceID: script.ResourceID,
}, 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 {
err := CheckHasRoleScriptWrite(ctx)
if err != nil {
return err
}
filter := service.NewScriptFilter()
if name, name_ok := req.Name.Get(); name_ok{
filter.SetName(name)
}
if source, source_ok := req.Source.Get(); source_ok{
filter.SetSource(source)
filter.SetHash(int64(model.HashSource(source)))
}
if resource_type, resource_type_ok := req.ResourceType.Get(); resource_type_ok{
filter.SetResourceType(resource_type)
}
if resource_id, resource_id_ok := req.ResourceID.Get(); resource_id_ok{
filter.SetResourceID(resource_id)
}
return svc.inner.UpdateScript(ctx, req.ID, filter)
}

View File

@@ -1,10 +1,12 @@
package service
package web_api
import (
"context"
"errors"
"git.itzana.me/strafesnet/go-grpc/auth"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
var (
@@ -14,38 +16,6 @@ var (
ErrInvalidSession = errors.New("Session invalid")
)
// Submissions roles bitflag
type Roles int32
var (
RolesSubmissionUpload Roles = 1<<6
RolesSubmissionReview Roles = 1<<5
RolesSubmissionRelease Roles = 1<<4
RolesScriptWrite Roles = 1<<3
RolesMapfixUpload Roles = 1<<2
RolesMapfixReview Roles = 1<<1
RolesMapDownload Roles = 1<<0
RolesEmpty Roles = 0
)
// StrafesNET group roles
type GroupRole int32
var (
// has ScriptWrite
RoleQuat GroupRole = 255
RoleItzaname GroupRole = 254
RoleStagingDeveloper GroupRole = 240
RolesAll Roles = ^RolesEmpty
// has SubmissionUpload
RoleMapAdmin GroupRole = 128
RolesMapAdmin Roles = RolesSubmissionRelease|RolesSubmissionUpload|RolesSubmissionReview|RolesMapCouncil
// has MapfixReview
RoleMapCouncil GroupRole = 64
RolesMapCouncil Roles = RolesMapfixReview|RolesMapfixUpload|RolesMapAccess
// access to downloading maps
RoleMapAccess GroupRole = 32
RolesMapAccess Roles = RolesMapDownload
)
type UserInfoHandle struct {
// Would love to know a better way to do this
svc *SecurityHandler
@@ -88,7 +58,7 @@ func (usr UserInfoHandle) Validate() (bool, error) {
}
return validate.Valid, nil
}
func (usr UserInfoHandle) hasRoles(wantRoles Roles) (bool, error) {
func (usr UserInfoHandle) hasRoles(wantRoles model.Roles) (bool, error) {
haveroles, err := usr.GetRoles()
if err != nil {
return false, err
@@ -96,27 +66,27 @@ func (usr UserInfoHandle) hasRoles(wantRoles Roles) (bool, error) {
return haveroles & wantRoles == wantRoles, nil
}
func (usr UserInfoHandle) GetRoles() (Roles, error) {
func (usr UserInfoHandle) GetRoles() (model.Roles, error) {
roles, err := usr.svc.Client.GetGroupRole(*usr.ctx, &auth.IdMessage{
SessionID: usr.sessionId,
})
if err != nil {
return RolesEmpty, err
return model.RolesEmpty, err
}
// map roles into bitflag
rolesBitflag := RolesEmpty;
rolesBitflag := model.RolesEmpty;
for _, r := range roles.Roles {
switch GroupRole(r.Rank){
case RoleQuat, RoleItzaname, RoleStagingDeveloper:
rolesBitflag|=RolesAll
case RoleMapAdmin:
rolesBitflag|=RolesMapAdmin
case RoleMapCouncil:
rolesBitflag|=RolesMapCouncil
case RoleMapAccess:
rolesBitflag|=RolesMapAccess
switch model.GroupRole(r.Rank){
case model.RoleQuat, model.RoleItzaname, model.RoleStagingDeveloper:
rolesBitflag|=model.RolesAll
case model.RoleMapAdmin:
rolesBitflag|=model.RolesMapAdmin
case model.RoleMapCouncil:
rolesBitflag|=model.RolesMapCouncil
case model.RoleMapAccess:
rolesBitflag|=model.RolesMapAccess
}
}
return rolesBitflag, nil
@@ -124,25 +94,25 @@ func (usr UserInfoHandle) GetRoles() (Roles, error) {
// RoleThumbnail
func (usr UserInfoHandle) HasRoleMapfixUpload() (bool, error) {
return usr.hasRoles(RolesMapfixUpload)
return usr.hasRoles(model.RolesMapfixUpload)
}
func (usr UserInfoHandle) HasRoleMapfixReview() (bool, error) {
return usr.hasRoles(RolesMapfixReview)
return usr.hasRoles(model.RolesMapfixReview)
}
func (usr UserInfoHandle) HasRoleMapDownload() (bool, error) {
return usr.hasRoles(RolesMapDownload)
return usr.hasRoles(model.RolesMapDownload)
}
func (usr UserInfoHandle) HasRoleSubmissionRelease() (bool, error) {
return usr.hasRoles(RolesSubmissionRelease)
return usr.hasRoles(model.RolesSubmissionRelease)
}
func (usr UserInfoHandle) HasRoleSubmissionUpload() (bool, error) {
return usr.hasRoles(RolesSubmissionUpload)
return usr.hasRoles(model.RolesSubmissionUpload)
}
func (usr UserInfoHandle) HasRoleSubmissionReview() (bool, error) {
return usr.hasRoles(RolesSubmissionReview)
return usr.hasRoles(model.RolesSubmissionReview)
}
func (usr UserInfoHandle) HasRoleScriptWrite() (bool, error) {
return usr.hasRoles(RolesScriptWrite)
return usr.hasRoles(model.RolesScriptWrite)
}
/// Not implemented
func (usr UserInfoHandle) HasRoleMaptest() (bool, error) {

68
pkg/web_api/service.go Normal file
View File

@@ -0,0 +1,68 @@
package web_api
import (
"context"
"errors"
"fmt"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/roblox"
"git.itzana.me/strafesnet/maps-service/pkg/service"
)
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")
ErrDelayReset = errors.New("Please give the validator at least 10 seconds to operate before attempting to reset the status")
ErrPermissionDeniedNotSubmitter = fmt.Errorf("%w: You must be the submitter to perform this action", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionRelease = fmt.Errorf("%w: Need Role SubmissionRelease", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapfixUpload = fmt.Errorf("%w: Need Role MapfixUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapfixReview = fmt.Errorf("%w: Need Role MapfixReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionUpload = fmt.Errorf("%w: Need Role SubmissionUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionReview = fmt.Errorf("%w: Need Role SubmissionReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapDownload = fmt.Errorf("%w: Need Role MapDownload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleScriptWrite = fmt.Errorf("%w: Need Role ScriptWrite", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMaptest = fmt.Errorf("%w: Need Role Maptest", ErrPermissionDenied)
ErrNegativeID = errors.New("A negative ID was provided")
)
type Service struct {
inner *service.Service
roblox roblox.Client
}
func NewService(
inner *service.Service,
roblox roblox.Client,
) Service {
return Service{
inner: inner,
roblox: roblox,
}
}
// NewError creates *ErrorStatusCode from error returned by handler.
//
// Used for common default response.
func (svc *Service) NewError(ctx context.Context, err error) *api.ErrorStatusCode {
status := 500
if errors.Is(err, datastore.ErrNotExist) {
status = 404
}
if errors.Is(err, ErrPermissionDenied) {
status = 403
}
if errors.Is(err, ErrUserInfo) {
status = 401
}
return &api.ErrorStatusCode{
StatusCode: status,
Response: api.Error{
Code: int64(status),
Message: err.Error(),
},
}
}

View File

@@ -1,4 +1,4 @@
package service
package web_api
import (
"context"

1178
pkg/web_api/submissions.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,12 @@
[package]
name = "maps-validation"
version = "0.1.1"
edition = "2021"
edition = "2024"
[dependencies]
submissions-api = { path = "api", features = ["internal"], default-features = false, registry = "strafesnet" }
async-nats = "0.41.0"
async-nats = "0.42.0"
futures = "0.3.31"
rbx_asset = { version = "0.4.5", registry = "strafesnet" }
rbx_asset = { version = "0.4.7", registry = "strafesnet" }
rbx_binary = "1.0.0"
rbx_dom_weak = "3.0.0"
rbx_reflection_database = "1.0.3"
@@ -18,3 +17,5 @@ siphasher = "1.0.1"
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread", "signal"] }
heck = "0.5.0"
lazy-regex = "3.4.1"
rust-grpc = { version = "1.2.1", registry = "strafesnet" }
tonic = "0.13.1"

View File

@@ -1,7 +1,7 @@
[package]
name = "submissions-api"
version = "0.7.2"
edition = "2021"
version = "0.8.2"
edition = "2024"
publish = ["strafesnet"]
repository = "https://git.itzana.me/StrafesNET/maps-service"
license = "MIT OR Apache-2.0"
@@ -11,13 +11,9 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = { version = "0.4.41", features = ["serde"] }
reqwest = { version = "0", features = ["json"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_repr = "0.1.19"
url = "2"
[features]
default = ["external"]
internal = []
external = []

View File

@@ -33,11 +33,6 @@ impl Context{
self.client.get(url)
.send().await
}
#[cfg(feature="internal")]
pub async fn post_empty_body(&self,url:impl reqwest::IntoUrl)->Result<reqwest::Response,reqwest::Error>{
self.client.post(url)
.send().await
}
pub async fn post(&self,url:impl reqwest::IntoUrl,body:impl Into<reqwest::Body>)->Result<reqwest::Response,reqwest::Error>{
self.client.post(url)
.header("Content-Type","application/json")

View File

@@ -36,7 +36,7 @@ impl Context{
if let Some(resource_type)=config.ResourceType{
query_pairs.append_pair("ResourceType",(resource_type as i32).to_string().as_str());
}
if let Some(resource_id)=config.ResourceID{
if let Some(ResourceID(resource_id))=config.ResourceID{
query_pairs.append_pair("ResourceID",resource_id.to_string().as_str());
}
}
@@ -46,7 +46,7 @@ impl Context{
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_script_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptResponse>,SingleItemError>{
pub async fn get_script_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptResponse>,ScriptSingleItemError>{
let scripts=self.get_scripts(GetScriptsRequest{
Page:1,
Limit:2,
@@ -57,7 +57,7 @@ impl Context{
ResourceID:None,
}).await.map_err(SingleItemError::Other)?;
if 1<scripts.len(){
return Err(SingleItemError::DuplicateItems);
return Err(SingleItemError::DuplicateItems(scripts.into_iter().map(|item|item.ID).collect()));
}
Ok(scripts.into_iter().next())
}
@@ -106,7 +106,7 @@ impl Context{
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_script_policy_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
pub async fn get_script_policy_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptPolicyResponse>,ScriptPolicySingleItemError>{
let policies=self.get_script_policies(GetScriptPoliciesRequest{
Page:1,
Limit:2,
@@ -115,7 +115,7 @@ impl Context{
Policy:None,
}).await.map_err(SingleItemError::Other)?;
if 1<policies.len(){
return Err(SingleItemError::DuplicateItems);
return Err(SingleItemError::DuplicateItems(policies.into_iter().map(|item|item.ID).collect()));
}
Ok(policies.into_iter().next())
}
@@ -150,6 +150,84 @@ impl Context{
self.0.delete(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
pub async fn get_submissions(&self,config:GetSubmissionsRequest<'_>)->Result<SubmissionsResponse,Error>{
let url_raw=format!("{}/submissions",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
let mut query_pairs=url.query_pairs_mut();
query_pairs.append_pair("Page",config.Page.to_string().as_str());
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
if let Some(sort)=config.Sort{
query_pairs.append_pair("Sort",(sort as u8).to_string().as_str());
}
if let Some(display_name)=config.DisplayName{
query_pairs.append_pair("DisplayName",display_name);
}
if let Some(creator)=config.Creator{
query_pairs.append_pair("Creator",creator);
}
if let Some(game_id)=config.GameID{
query_pairs.append_pair("GameID",(game_id as u8).to_string().as_str());
}
if let Some(submitter)=config.Submitter{
query_pairs.append_pair("Submitter",submitter.to_string().as_str());
}
if let Some(asset_id)=config.AssetID{
query_pairs.append_pair("AssetID",asset_id.to_string().as_str());
}
if let Some(uploaded_asset_id)=config.UploadedAssetID{
query_pairs.append_pair("UploadedAssetID",uploaded_asset_id.to_string().as_str());
}
if let Some(status_id)=config.StatusID{
query_pairs.append_pair("StatusID",(status_id as u8).to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_maps(&self,config:GetMapsRequest<'_>)->Result<Vec<MapResponse>,Error>{
let url_raw=format!("{}/maps",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
let mut query_pairs=url.query_pairs_mut();
query_pairs.append_pair("Page",config.Page.to_string().as_str());
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
if let Some(sort)=config.Sort{
query_pairs.append_pair("Sort",(sort as u8).to_string().as_str());
}
if let Some(display_name)=config.DisplayName{
query_pairs.append_pair("DisplayName",display_name);
}
if let Some(creator)=config.Creator{
query_pairs.append_pair("Creator",creator);
}
if let Some(game_id)=config.GameID{
query_pairs.append_pair("GameID",(game_id as u8).to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn release_submissions(&self,config:ReleaseRequest<'_>)->Result<(),Error>{
let url_raw=format!("{}/release-submissions",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
let body=serde_json::to_string(config.schedule).map_err(Error::JSON)?;
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
}

View File

@@ -1,219 +0,0 @@
use crate::types::*;
#[derive(Clone)]
pub struct Context(crate::context::Context);
// conditionally include specified query pairs
macro_rules! query_pairs{
($url:expr,)=>{
$url
};
($url:expr,$(($param:expr,$value:expr))+)=>{
{
let mut url=$url;
url.query_pairs_mut()
$(.append_pair($param,$value))*;
url
}
};
}
// there are lots of action endpoints and they all follow the same pattern
macro_rules! action{
($system:expr,$fname:ident,$config:ident,$config_type:ident,$action:expr,$config_submission_id:expr,$(($param:expr,$value:expr))*)=>{
pub async fn $fname(&self,$config:$config_type)->Result<(),Error>{
let url_raw=format!(concat!("{}/",$system,"/{}/",$action),self.0.base_url,$config_submission_id);
let url=query_pairs!(reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?,$(($param,$value))*);
response_ok(
self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
};
}
impl Context{
pub fn new(base_url:String)->reqwest::Result<Self>{
Ok(Self(crate::context::Context::new(base_url,None)?))
}
pub async fn get_script(&self,config:GetScriptRequest)->Result<ScriptResponse,Error>{
let url_raw=format!("{}/scripts/{}",self.0.base_url,config.ScriptID.0);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_scripts(&self,config:GetScriptsRequest<'_>)->Result<Vec<ScriptResponse>,Error>{
let url_raw=format!("{}/scripts",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
let mut query_pairs=url.query_pairs_mut();
query_pairs.append_pair("Page",config.Page.to_string().as_str());
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
if let Some(name)=config.Name{
query_pairs.append_pair("Name",name);
}
if let Some(hash)=config.Hash{
query_pairs.append_pair("Hash",hash);
}
if let Some(source)=config.Source{
query_pairs.append_pair("Source",source);
}
if let Some(resource_type)=config.ResourceType{
query_pairs.append_pair("ResourceType",(resource_type as i32).to_string().as_str());
}
if let Some(resource_id)=config.ResourceID{
query_pairs.append_pair("ResourceID",resource_id.to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_script_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptResponse>,SingleItemError>{
let scripts=self.get_scripts(GetScriptsRequest{
Page:1,
Limit:2,
Hash:Some(config.hash),
Name:None,
Source:None,
ResourceType:None,
ResourceID:None,
}).await.map_err(SingleItemError::Other)?;
if 1<scripts.len(){
return Err(SingleItemError::DuplicateItems);
}
Ok(scripts.into_iter().next())
}
pub async fn create_script(&self,config:CreateScriptRequest<'_>)->Result<ScriptIDResponse,Error>{
let url_raw=format!("{}/scripts",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
let body=serde_json::to_string(&config).map_err(Error::JSON)?;
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_script_policies(&self,config:GetScriptPoliciesRequest<'_>)->Result<Vec<ScriptPolicyResponse>,Error>{
let url_raw=format!("{}/script-policy",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
let mut query_pairs=url.query_pairs_mut();
query_pairs.append_pair("Page",config.Page.to_string().as_str());
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
if let Some(hash)=config.FromScriptHash{
query_pairs.append_pair("FromScriptHash",hash);
}
if let Some(script_id)=config.ToScriptID{
query_pairs.append_pair("ToScriptID",script_id.0.to_string().as_str());
}
if let Some(policy)=config.Policy{
query_pairs.append_pair("Policy",(policy as i32).to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_script_policy_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
let policies=self.get_script_policies(GetScriptPoliciesRequest{
Page:1,
Limit:2,
FromScriptHash:Some(config.hash),
ToScriptID:None,
Policy:None,
}).await.map_err(SingleItemError::Other)?;
if 1<policies.len(){
return Err(SingleItemError::DuplicateItems);
}
Ok(policies.into_iter().next())
}
pub async fn create_script_policy(&self,config:CreateScriptPolicyRequest)->Result<ScriptPolicyIDResponse,Error>{
let url_raw=format!("{}/script-policy",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
let body=serde_json::to_string(&config).map_err(Error::JSON)?;
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn create_submission(&self,config:CreateSubmissionRequest<'_>)->Result<SubmissionIDResponse,Error>{
let url_raw=format!("{}/submissions",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
let body=serde_json::to_string(&config).map_err(Error::JSON)?;
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
// simple submission endpoints
action!("submissions",action_submission_request_changes,config,ActionSubmissionRequestChangesRequest,"status/validator-request-changes",config.SubmissionID,
("ErrorMessage",config.ErrorMessage.as_str())
);
action!("submissions",action_submission_submitted,config,ActionSubmissionSubmittedRequest,"status/validator-submitted",config.SubmissionID,
("ModelVersion",config.ModelVersion.to_string().as_str())
("DisplayName",config.DisplayName.as_str())
("Creator",config.Creator.as_str())
("GameID",config.GameID.to_string().as_str())
);
action!("submissions",action_submission_validated,config,SubmissionID,"status/validator-validated",config.0,);
action!("submissions",update_submission_validated_model,config,UpdateSubmissionModelRequest,"validated-model",config.SubmissionID,
("ValidatedModelID",config.ModelID.to_string().as_str())
("ValidatedModelVersion",config.ModelVersion.to_string().as_str())
);
action!("submissions",action_submission_uploaded,config,ActionSubmissionUploadedRequest,"status/validator-uploaded",config.SubmissionID,
("UploadedAssetID",config.UploadedAssetID.to_string().as_str())
);
action!("submissions",action_submission_accepted,config,ActionSubmissionAcceptedRequest,"status/validator-failed",config.SubmissionID,
("ErrorMessage",config.ErrorMessage.as_str())
);
pub async fn create_mapfix(&self,config:CreateMapfixRequest<'_>)->Result<MapfixIDResponse,Error>{
let url_raw=format!("{}/mapfixes",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
let body=serde_json::to_string(&config).map_err(Error::JSON)?;
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
// simple mapfixes endpoints
action!("mapfixes",action_mapfix_request_changes,config,ActionMapfixRequestChangesRequest,"status/validator-request-changes",config.MapfixID,
("ErrorMessage",config.ErrorMessage.as_str())
);
action!("mapfixes",action_mapfix_submitted,config,ActionMapfixSubmittedRequest,"status/validator-submitted",config.MapfixID,
("ModelVersion",config.ModelVersion.to_string().as_str())
("DisplayName",config.DisplayName.as_str())
("Creator",config.Creator.as_str())
("GameID",config.GameID.to_string().as_str())
);
action!("mapfixes",action_mapfix_validated,config,MapfixID,"status/validator-validated",config.0,);
action!("mapfixes",update_mapfix_validated_model,config,UpdateMapfixModelRequest,"validated-model",config.MapfixID,
("ValidatedModelID",config.ModelID.to_string().as_str())
("ValidatedModelVersion",config.ModelVersion.to_string().as_str())
);
action!("mapfixes",action_mapfix_uploaded,config,ActionMapfixUploadedRequest,"status/validator-uploaded",config.MapfixID,);
action!("mapfixes",action_mapfix_accepted,config,ActionMapfixAcceptedRequest,"status/validator-failed",config.MapfixID,
("ErrorMessage",config.ErrorMessage.as_str())
);
// simple operation endpoint
action!("operations",action_operation_failed,config,ActionOperationFailedRequest,"status/operation-failed",config.OperationID,
("StatusMessage",config.StatusMessage.as_str())
);
}

View File

@@ -2,11 +2,6 @@ mod context;
pub use context::Cookie;
pub mod types;
#[cfg(feature="internal")]
pub mod internal;
#[cfg(feature="external")]
pub mod external;
//lazy reexports

View File

@@ -14,16 +14,21 @@ impl std::fmt::Display for Error{
impl std::error::Error for Error{}
#[derive(Debug)]
pub enum SingleItemError{
DuplicateItems,
pub enum SingleItemError<Items>{
DuplicateItems(Items),
Other(Error),
}
impl std::fmt::Display for SingleItemError{
impl<Items> std::fmt::Display for SingleItemError<Items>
where
Items:std::fmt::Debug
{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for SingleItemError{}
impl<Items> std::error::Error for SingleItemError<Items> where Items:std::fmt::Debug{}
pub type ScriptSingleItemError=SingleItemError<Vec<ScriptID>>;
pub type ScriptPolicySingleItemError=SingleItemError<Vec<ScriptPolicyID>>;
#[allow(dead_code)]
#[derive(Debug)]
@@ -62,15 +67,23 @@ pub async fn response_ok(response:reqwest::Response)->Result<reqwest::Response,R
}
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq,Ord,PartialOrd,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
#[repr(u8)]
pub enum GameID{
Bhop=1,
Surf=2,
KreedzClimb=3,
FlyTrials=5,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct CreateMapfixRequest<'a>{
pub OperationID:i32,
pub OperationID:OperationID,
pub AssetOwner:i64,
pub DisplayName:&'a str,
pub Creator:&'a str,
pub GameID:i32,
pub GameID:GameID,
pub AssetID:u64,
pub AssetVersion:u64,
pub TargetAssetID:u64,
@@ -85,11 +98,11 @@ pub struct MapfixIDResponse{
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct CreateSubmissionRequest<'a>{
pub OperationID:i32,
pub OperationID:OperationID,
pub AssetOwner:i64,
pub DisplayName:&'a str,
pub Creator:&'a str,
pub GameID:i32,
pub GameID:GameID,
pub AssetID:u64,
pub AssetVersion:u64,
pub Status:u32,
@@ -101,9 +114,9 @@ pub struct SubmissionIDResponse{
pub SubmissionID:SubmissionID,
}
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde::Serialize,serde::Deserialize)]
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq,serde::Serialize,serde::Deserialize)]
pub struct ScriptID(pub(crate)i64);
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq,serde::Serialize,serde::Deserialize)]
pub struct ScriptPolicyID(pub(crate)i64);
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
@@ -132,7 +145,7 @@ pub struct GetScriptsRequest<'a>{
#[serde(skip_serializing_if="Option::is_none")]
pub ResourceType:Option<ResourceType>,
#[serde(skip_serializing_if="Option::is_none")]
pub ResourceID:Option<i64>,
pub ResourceID:Option<ResourceID>,
}
#[derive(Clone,Copy,Debug)]
pub struct HashRequest<'a>{
@@ -146,7 +159,7 @@ pub struct ScriptResponse{
pub Hash:String,
pub Source:String,
pub ResourceType:ResourceType,
pub ResourceID:i64,
pub ResourceID:ResourceID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
@@ -155,7 +168,7 @@ pub struct CreateScriptRequest<'a>{
pub Source:&'a str,
pub ResourceType:ResourceType,
#[serde(skip_serializing_if="Option::is_none")]
pub ResourceID:Option<i64>,
pub ResourceID:Option<ResourceID>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
@@ -225,49 +238,160 @@ pub struct UpdateScriptPolicyRequest{
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct UpdateSubmissionModelRequest{
pub SubmissionID:i64,
pub SubmissionID:SubmissionID,
pub ModelID:u64,
pub ModelVersion:u64,
}
#[derive(Clone,Debug)]
pub enum Sort{
Disabled=0,
DisplayNameAscending=1,
DisplayNameDescending=2,
DateAscending=3,
DateDescending=4,
}
#[derive(Clone,Debug,serde_repr::Deserialize_repr)]
#[repr(u8)]
pub enum SubmissionStatus{
// Phase: Creation
UnderConstruction=0,
ChangesRequested=1,
// Phase: Review
Submitting=2,
Submitted=3,
// Phase: Testing
AcceptedUnvalidated=4, // pending script review, can re-trigger validation
Validating=5,
Validated=6,
Uploading=7,
Uploaded=8, // uploaded to the group, but pending release
// Phase: Final SubmissionStatus
Rejected=9,
Released=10,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct GetSubmissionsRequest<'a>{
pub Page:u32,
pub Limit:u32,
pub Sort:Option<Sort>,
pub DisplayName:Option<&'a str>,
pub Creator:Option<&'a str>,
pub GameID:Option<GameID>,
pub Submitter:Option<u64>,
pub AssetID:Option<u64>,
pub UploadedAssetID:Option<u64>,
pub StatusID:Option<SubmissionStatus>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct SubmissionResponse{
pub ID:SubmissionID,
pub DisplayName:String,
pub Creator:String,
pub GameID:GameID,
pub CreatedAt:i64,
pub UpdatedAt:i64,
pub Submitter:u64,
pub AssetID:u64,
pub AssetVersion:u64,
pub UploadedAssetID:u64,
pub StatusID:SubmissionStatus,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct SubmissionsResponse{
pub Total:u64,
pub Submissions:Vec<SubmissionResponse>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct GetMapsRequest<'a>{
pub Page:u32,
pub Limit:u32,
pub Sort:Option<Sort>,
pub DisplayName:Option<&'a str>,
pub Creator:Option<&'a str>,
pub GameID:Option<GameID>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct MapResponse{
pub ID:i64,
pub DisplayName:String,
pub Creator:String,
pub GameID:GameID,
pub Date:i64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct Check{
pub Name:&'static str,
pub Summary:String,
pub Passed:bool,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionSubmittedRequest{
pub SubmissionID:i64,
pub SubmissionID:SubmissionID,
pub ModelVersion:u64,
pub DisplayName:String,
pub Creator:String,
pub GameID:u32,
pub GameID:GameID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionRequestChangesRequest{
pub SubmissionID:i64,
pub ErrorMessage:String,
pub SubmissionID:SubmissionID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionUploadedRequest{
pub SubmissionID:i64,
pub SubmissionID:SubmissionID,
pub UploadedAssetID:u64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionAcceptedRequest{
pub SubmissionID:i64,
pub SubmissionID:SubmissionID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct CreateSubmissionAuditErrorRequest{
pub SubmissionID:SubmissionID,
pub ErrorMessage:String,
}
#[derive(Clone,Copy,Debug,serde::Deserialize)]
pub struct SubmissionID(pub i64);
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct CreateSubmissionAuditCheckListRequest<'a>{
pub SubmissionID:SubmissionID,
pub CheckList:&'a [Check],
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq,serde::Serialize,serde::Deserialize)]
pub struct SubmissionID(pub(crate)i64);
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct UpdateMapfixModelRequest{
pub MapfixID:i64,
pub MapfixID:MapfixID,
pub ModelID:u64,
pub ModelVersion:u64,
}
@@ -275,39 +399,82 @@ pub struct UpdateMapfixModelRequest{
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixSubmittedRequest{
pub MapfixID:i64,
pub MapfixID:MapfixID,
pub ModelVersion:u64,
pub DisplayName:String,
pub Creator:String,
pub GameID:u32,
pub GameID:GameID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixRequestChangesRequest{
pub MapfixID:i64,
pub ErrorMessage:String,
pub MapfixID:MapfixID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixUploadedRequest{
pub MapfixID:i64,
pub MapfixID:MapfixID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixAcceptedRequest{
pub MapfixID:i64,
pub MapfixID:MapfixID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct CreateMapfixAuditErrorRequest{
pub MapfixID:MapfixID,
pub ErrorMessage:String,
}
#[derive(Clone,Copy,Debug,serde::Deserialize)]
pub struct MapfixID(pub i64);
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct CreateMapfixAuditCheckListRequest<'a>{
pub MapfixID:MapfixID,
pub CheckList:&'a [Check],
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq,serde::Serialize,serde::Deserialize)]
pub struct MapfixID(pub(crate)i64);
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionOperationFailedRequest{
pub OperationID:i32,
pub OperationID:OperationID,
pub StatusMessage:String,
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq,serde::Serialize,serde::Deserialize)]
pub struct OperationID(pub(crate)i64);
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq,serde::Serialize,serde::Deserialize)]
pub struct ResourceID(pub(crate)i64);
#[derive(Clone,Copy,Debug)]
pub enum Resource{
Submission(SubmissionID),
Mapfix(MapfixID),
}
impl Resource{
pub fn split(self)->(ResourceType,ResourceID){
match self{
Resource::Mapfix(MapfixID(mapfix_id))=>(ResourceType::Mapfix,ResourceID(mapfix_id)),
Resource::Submission(SubmissionID(submission_id))=>(ResourceType::Submission,ResourceID(submission_id)),
}
}
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct ReleaseInfo{
pub SubmissionID:SubmissionID,
pub Date:chrono::DateTime<chrono::Utc>,
}
pub struct ReleaseRequest<'a>{
pub schedule:&'a [ReleaseInfo],
}

View File

@@ -1,8 +1,10 @@
use std::collections::{HashSet,HashMap};
use crate::download::download_asset_version;
use crate::rbx_util::{class_is_a,get_mapinfo,get_root_instance,read_dom,ReadDomError,GameID,ParseGameIDError,MapInfo,GetRootInstanceError,StringValueError};
use crate::rbx_util::{get_mapinfo,get_root_instance,read_dom,ReadDomError,GameID,ParseGameIDError,MapInfo,GetRootInstanceError,StringValueError};
use heck::{ToSnakeCase,ToTitleCase};
use rbx_dom_weak::Instance;
use rust_grpc::validator::Check;
#[allow(dead_code)]
#[derive(Debug)]
@@ -12,6 +14,7 @@ pub enum Error{
Download(crate::download::Error),
ModelFileDecode(ReadDomError),
GetRootInstance(GetRootInstanceError),
IntoMapInfoOwned(IntoMapInfoOwnedError),
ToJsonValue(serde_json::Error),
}
impl std::fmt::Display for Error{
@@ -23,13 +26,15 @@ impl std::error::Error for Error{}
#[allow(nonstandard_style)]
pub struct CheckRequest{
pub ModelID:u64,
ModelID:u64,
SkipChecks:bool,
}
impl From<crate::nats_types::CheckMapfixRequest> for CheckRequest{
fn from(value:crate::nats_types::CheckMapfixRequest)->Self{
Self{
ModelID:value.ModelID,
SkipChecks:value.SkipChecks,
}
}
}
@@ -37,6 +42,7 @@ impl From<crate::nats_types::CheckSubmissionRequest> for CheckRequest{
fn from(value:crate::nats_types::CheckSubmissionRequest)->Self{
Self{
ModelID:value.ModelID,
SkipChecks:value.SkipChecks,
}
}
}
@@ -203,10 +209,10 @@ impl std::fmt::Display for WormholeElement{
/// Count various map elements
#[derive(Default)]
struct Counts<'a>{
mode_start_counts:HashMap<ModeID,Vec<&'a str>>,
mode_finish_counts:HashMap<ModeID,Vec<&'a str>>,
mode_anticheat_counts:HashMap<ModeID,Vec<&'a str>>,
teleport_counts:HashMap<StageID,Vec<&'a str>>,
mode_start_counts:HashMap<ModeID,Vec<&'a Instance>>,
mode_finish_counts:HashMap<ModeID,Vec<&'a Instance>>,
mode_anticheat_counts:HashMap<ModeID,Vec<&'a Instance>>,
teleport_counts:HashMap<StageID,Vec<&'a Instance>>,
spawn_counts:HashMap<StageID,u64>,
wormhole_in_counts:HashMap<WormholeID,u64>,
wormhole_out_counts:HashMap<WormholeID,u64>,
@@ -217,6 +223,7 @@ pub struct ModelInfo<'a>{
model_name:&'a str,
map_info:MapInfo<'a>,
counts:Counts<'a>,
unanchored_parts:Vec<&'a Instance>,
}
pub fn get_model_info<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&'a rbx_dom_weak::Instance)->ModelInfo<'a>{
@@ -225,27 +232,41 @@ pub fn get_model_info<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&'a rbx_d
// count objects (default count is 0)
let mut counts=Counts::default();
for instance in dom.descendants_of(model_instance.referent()){
if class_is_a(instance.class.as_str(),"BasePart"){
// Zones
match instance.name.parse(){
Ok(ModeElement{zone:Zone::Start,mode_id})=>counts.mode_start_counts.entry(mode_id).or_default().push(instance.name.as_str()),
Ok(ModeElement{zone:Zone::Finish,mode_id})=>counts.mode_finish_counts.entry(mode_id).or_default().push(instance.name.as_str()),
Ok(ModeElement{zone:Zone::Anticheat,mode_id})=>counts.mode_anticheat_counts.entry(mode_id).or_default().push(instance.name.as_str()),
Err(_)=>(),
}
// Spawns & Teleports
match instance.name.parse(){
Ok(StageElement{behaviour:StageElementBehaviour::Teleport,stage_id})=>counts.teleport_counts.entry(stage_id).or_default().push(instance.name.as_str()),
Ok(StageElement{behaviour:StageElementBehaviour::Spawn,stage_id})=>*counts.spawn_counts.entry(stage_id).or_insert(0)+=1,
Err(_)=>(),
}
// Wormholes
match instance.name.parse(){
Ok(WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id})=>*counts.wormhole_in_counts.entry(wormhole_id).or_insert(0)+=1,
Ok(WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id})=>*counts.wormhole_out_counts.entry(wormhole_id).or_insert(0)+=1,
Err(_)=>(),
}
// locate unanchored parts
let mut unanchored_parts=Vec::new();
let anchored_ustr=rbx_dom_weak::ustr("Anchored");
let db=rbx_reflection_database::get();
let base_part=&db.classes["BasePart"];
let base_parts=dom.descendants_of(model_instance.referent()).filter(|&instance|
db.classes.get(instance.class.as_str()).is_some_and(|class|
db.has_superclass(class,base_part)
)
);
for instance in base_parts{
// Zones
match instance.name.parse(){
Ok(ModeElement{zone:Zone::Start,mode_id})=>counts.mode_start_counts.entry(mode_id).or_default().push(instance),
Ok(ModeElement{zone:Zone::Finish,mode_id})=>counts.mode_finish_counts.entry(mode_id).or_default().push(instance),
Ok(ModeElement{zone:Zone::Anticheat,mode_id})=>counts.mode_anticheat_counts.entry(mode_id).or_default().push(instance),
Err(_)=>(),
}
// Spawns & Teleports
match instance.name.parse(){
Ok(StageElement{behaviour:StageElementBehaviour::Teleport,stage_id})=>counts.teleport_counts.entry(stage_id).or_default().push(instance),
Ok(StageElement{behaviour:StageElementBehaviour::Spawn,stage_id})=>*counts.spawn_counts.entry(stage_id).or_insert(0)+=1,
Err(_)=>(),
}
// Wormholes
match instance.name.parse(){
Ok(WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id})=>*counts.wormhole_in_counts.entry(wormhole_id).or_insert(0)+=1,
Ok(WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id})=>*counts.wormhole_out_counts.entry(wormhole_id).or_insert(0)+=1,
Err(_)=>(),
}
// Unanchored parts
if let Some(rbx_dom_weak::types::Variant::Bool(false))=instance.properties.get(&anchored_ustr){
unanchored_parts.push(instance);
}
}
@@ -254,6 +275,7 @@ pub fn get_model_info<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&'a rbx_d
model_name:model_instance.name.as_str(),
map_info,
counts,
unanchored_parts,
}
}
@@ -376,6 +398,23 @@ pub struct MapInfoOwned{
pub creator:String,
pub game_id:GameID,
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum IntoMapInfoOwnedError{
DisplayName(StringValueError),
Creator(StringValueError),
GameID(ParseGameIDError),
}
impl TryFrom<MapInfo<'_>> for MapInfoOwned{
type Error=IntoMapInfoOwnedError;
fn try_from(value:MapInfo<'_>)->Result<Self,Self::Error>{
Ok(Self{
display_name:value.display_name.map_err(IntoMapInfoOwnedError::DisplayName)?.to_owned(),
creator:value.creator.map_err(IntoMapInfoOwnedError::Creator)?.to_owned(),
game_id:value.game_id.map_err(IntoMapInfoOwnedError::GameID)?,
})
}
}
// Named dummy types for readability
struct Exists;
@@ -402,15 +441,15 @@ struct MapCheck<'a>{
// MapStart must exist
mapstart:Result<Exists,Absent>,
// No duplicate map starts (including bonuses)
mode_start_counts:DuplicateCheck<ModeID,Vec<&'a str>>,
mode_start_counts:DuplicateCheck<ModeID,Vec<&'a Instance>>,
// At least one finish zone for each start zone, and no finishes with no start
mode_finish_counts:SetDifferenceCheck<SetDifferenceCheckContextAtLeastOne<ModeID,Vec<&'a str>>>,
mode_finish_counts:SetDifferenceCheck<SetDifferenceCheckContextAtLeastOne<ModeID,Vec<&'a Instance>>>,
// Check for dangling MapAnticheat zones (no associated MapStart)
mode_anticheat_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<ModeID,Vec<&'a str>>>,
mode_anticheat_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<ModeID,Vec<&'a Instance>>>,
// Spawn1 must exist
spawn1:Result<Exists,Absent>,
// Check for dangling Teleport# (no associated Spawn#)
teleport_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<StageID,Vec<&'a str>>>,
teleport_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<StageID,Vec<&'a Instance>>>,
// No duplicate Spawn#
spawn_counts:DuplicateCheck<StageID,u64>,
// Check for dangling WormholeIn# (no associated WormholeOut#)
@@ -418,6 +457,9 @@ struct MapCheck<'a>{
// No duplicate WormholeOut# (duplicate WormholeIn# ok)
// No dangling WormholeOut#
wormhole_out_counts:DuplicateCheck<WormholeID,u64>,
// === GENERAL CHECKS ===
unanchored_parts:Result<(),Vec<&'a Instance>>,
}
impl<'a> ModelInfo<'a>{
@@ -491,6 +533,13 @@ impl<'a> ModelInfo<'a>{
// There must be exactly one of any perticular wormhole out id in the map.
let wormhole_out_counts=DuplicateCheckContext(self.counts.wormhole_out_counts).check(|&c|1<c);
// There must not be any unanchored parts
let unanchored_parts=if self.unanchored_parts.is_empty(){
Ok(())
}else{
Err(self.unanchored_parts)
};
MapCheck{
model_class,
model_name,
@@ -506,6 +555,7 @@ impl<'a> ModelInfo<'a>{
spawn_counts,
wormhole_in_counts,
wormhole_out_counts,
unanchored_parts,
}
}
}
@@ -528,6 +578,7 @@ impl MapCheck<'_>{
spawn_counts:DuplicateCheck(Ok(())),
wormhole_in_counts:SetDifferenceCheck(Ok(())),
wormhole_out_counts:DuplicateCheck(Ok(())),
unanchored_parts:Ok(()),
}=>{
Ok(MapInfoOwned{
display_name:display_name.to_owned(),
@@ -585,40 +636,31 @@ impl<D:std::fmt::Display> std::fmt::Display for Duplicates<D>{
}
}
#[derive(serde::Serialize)]
struct CheckSummary{
name:&'static str,
summary:String,
passed:bool,
details:serde_json::Value,
}
impl CheckSummary{
const fn passed(name:&'static str)->Self{
Self{
name,
macro_rules! passed{
($name:literal)=>{
Check{
name:$name.to_owned(),
summary:String::new(),
passed:true,
details:serde_json::Value::Null,
}
}
}
macro_rules! summary{
($name:literal,$summary:expr,$details:expr)=>{
CheckSummary{
name:$name,
($name:literal,$summary:expr)=>{
Check{
name:$name.to_owned(),
summary:$summary,
passed:false,
details:serde_json::to_value($details)?,
}
};
}
macro_rules! summary_format{
($name:literal,$fmt:literal,$details:expr)=>{
CheckSummary{
name:$name,
($name:literal,$fmt:literal)=>{
Check{
name:$name.to_owned(),
summary:format!($fmt),
passed:false,
details:serde_json::to_value($details)?,
}
};
}
@@ -628,137 +670,150 @@ macro_rules! summary_format{
impl MapCheck<'_>{
fn itemize(&self)->Result<MapCheckList,serde_json::Error>{
let model_class=match &self.model_class{
StringCheck(Ok(()))=>CheckSummary::passed("ModelClass"),
StringCheck(Err(context))=>summary_format!("ModelClass","Invalid model class: {context}",()),
StringCheck(Ok(()))=>passed!("ModelClass"),
StringCheck(Err(context))=>summary_format!("ModelClass","Invalid model class: {context}"),
};
let model_name=match &self.model_name{
StringCheck(Ok(()))=>CheckSummary::passed("ModelName"),
StringCheck(Err(context))=>summary_format!("ModelName","Model name must have snake_case: {context}",()),
StringCheck(Ok(()))=>passed!("ModelName"),
StringCheck(Err(context))=>summary_format!("ModelName","Model name must have snake_case: {context}"),
};
let display_name=match &self.display_name{
Ok(Ok(StringCheck(Ok(_))))=>CheckSummary::passed("DisplayName"),
Ok(Ok(StringCheck(Err(context))))=>summary_format!("DisplayName","DisplayName must have Title Case: {context}",()),
Ok(Err(context))=>summary_format!("DisplayName","Invalid DisplayName: {context}",()),
Err(StringValueError::ObjectNotFound)=>summary!("DisplayName","Missing DisplayName StringValue".to_owned(),()),
Err(StringValueError::ValueNotSet)=>summary!("DisplayName","DisplayName Value not set".to_owned(),()),
Err(StringValueError::NonStringValue)=>summary!("DisplayName","DisplayName Value is not a String".to_owned(),()),
Ok(Ok(StringCheck(Ok(_))))=>passed!("DisplayName"),
Ok(Ok(StringCheck(Err(context))))=>summary_format!("DisplayName","DisplayName must have Title Case: {context}"),
Ok(Err(context))=>summary_format!("DisplayName","Invalid DisplayName: {context}"),
Err(StringValueError::ObjectNotFound)=>summary!("DisplayName","Missing DisplayName StringValue".to_owned()),
Err(StringValueError::ValueNotSet)=>summary!("DisplayName","DisplayName Value not set".to_owned()),
Err(StringValueError::NonStringValue)=>summary!("DisplayName","DisplayName Value is not a String".to_owned()),
};
let creator=match &self.creator{
Ok(Ok(_))=>CheckSummary::passed("Creator"),
Ok(Err(context))=>summary_format!("Creator","Invalid Creator: {context}",()),
Err(StringValueError::ObjectNotFound)=>summary!("Creator","Missing Creator StringValue".to_owned(),()),
Err(StringValueError::ValueNotSet)=>summary!("Creator","Creator Value not set".to_owned(),()),
Err(StringValueError::NonStringValue)=>summary!("Creator","Creator Value is not a String".to_owned(),()),
Ok(Ok(_))=>passed!("Creator"),
Ok(Err(context))=>summary_format!("Creator","Invalid Creator: {context}"),
Err(StringValueError::ObjectNotFound)=>summary!("Creator","Missing Creator StringValue".to_owned()),
Err(StringValueError::ValueNotSet)=>summary!("Creator","Creator Value not set".to_owned()),
Err(StringValueError::NonStringValue)=>summary!("Creator","Creator Value is not a String".to_owned()),
};
let game_id=match &self.game_id{
Ok(_)=>CheckSummary::passed("GameID"),
Err(ParseGameIDError)=>summary!("GameID","Model name must be prefixed with bhop_ surf_ or flytrials_".to_owned(),()),
Ok(_)=>passed!("GameID"),
Err(ParseGameIDError)=>summary!("GameID","Model name must be prefixed with bhop_ surf_ or flytrials_".to_owned()),
};
let mapstart=match &self.mapstart{
Ok(Exists)=>CheckSummary::passed("MapStart"),
Err(Absent)=>summary_format!("MapStart","Model has no MapStart",()),
Ok(Exists)=>passed!("MapStart"),
Err(Absent)=>summary_format!("MapStart","Model has no MapStart"),
};
let duplicate_start=match &self.mode_start_counts{
DuplicateCheck(Ok(()))=>CheckSummary::passed("DuplicateStart"),
DuplicateCheck(Ok(()))=>passed!("DuplicateStart"),
DuplicateCheck(Err(DuplicateCheckContext(context)))=>{
let context=Separated::new(", ",||context.iter().map(|(&mode_id,names)|
Duplicates::new(ModeElement{zone:Zone::Start,mode_id},names.len())
let context=Separated::new(", ",||context.iter().map(|(&mode_id,instances)|
Duplicates::new(ModeElement{zone:Zone::Start,mode_id},instances.len())
));
summary_format!("DuplicateStart","Duplicate start zones: {context}",())
summary_format!("DuplicateStart","Duplicate start zones: {context}")
}
};
let (extra_finish,missing_finish)=match &self.mode_finish_counts{
SetDifferenceCheck(Ok(()))=>(CheckSummary::passed("ExtraFinish"),CheckSummary::passed("MissingFinish")),
SetDifferenceCheck(Ok(()))=>(passed!("DanglingFinish"),passed!("MissingFinish")),
SetDifferenceCheck(Err(context))=>(
if context.extra.is_empty(){
CheckSummary::passed("ExtraFinish")
passed!("DanglingFinish")
}else{
let plural=if context.extra.len()==1{"zone"}else{"zones"};
let context=Separated::new(", ",||context.extra.iter().map(|(&mode_id,_names)|
let context=Separated::new(", ",||context.extra.iter().map(|(&mode_id,_instances)|
ModeElement{zone:Zone::Finish,mode_id}
));
summary_format!("ExtraFinish","No matching start zone for finish {plural}: {context}",())
summary_format!("DanglingFinish","No matching start zone for finish {plural}: {context}")
},
if context.missing.is_empty(){
CheckSummary::passed("MissingFinish")
passed!("MissingFinish")
}else{
let plural=if context.missing.len()==1{"zone"}else{"zones"};
let context=Separated::new(", ",||context.missing.iter().map(|&mode_id|
ModeElement{zone:Zone::Finish,mode_id}
));
summary_format!("MissingFinish","Missing finish {plural}: {context}",())
summary_format!("MissingFinish","Missing finish {plural}: {context}")
}
),
};
let dangling_anticheat=match &self.mode_anticheat_counts{
SetDifferenceCheck(Ok(()))=>CheckSummary::passed("DanglingAnticheat"),
SetDifferenceCheck(Ok(()))=>passed!("DanglingAnticheat"),
SetDifferenceCheck(Err(context))=>{
if context.extra.is_empty(){
CheckSummary::passed("DanglingAnticheat")
passed!("DanglingAnticheat")
}else{
let plural=if context.extra.len()==1{"zone"}else{"zones"};
let context=Separated::new(", ",||context.extra.iter().map(|(&mode_id,_names)|
let context=Separated::new(", ",||context.extra.iter().map(|(&mode_id,_instances)|
ModeElement{zone:Zone::Anticheat,mode_id}
));
summary_format!("DanglingAnticheat","No matching start zone for anticheat {plural}: {context}",())
summary_format!("DanglingAnticheat","No matching start zone for anticheat {plural}: {context}")
}
}
};
let spawn1=match &self.spawn1{
Ok(Exists)=>CheckSummary::passed("Spawn1"),
Err(Absent)=>summary_format!("Spawn1","Model has no Spawn1",()),
Ok(Exists)=>passed!("Spawn1"),
Err(Absent)=>summary_format!("Spawn1","Model has no Spawn1"),
};
let dangling_teleport=match &self.teleport_counts{
SetDifferenceCheck(Ok(()))=>CheckSummary::passed("DanglingTeleport"),
SetDifferenceCheck(Ok(()))=>passed!("DanglingTeleport"),
SetDifferenceCheck(Err(context))=>{
let unique_names:HashSet<_>=context.extra.values().flat_map(|names|names.iter().copied()).collect();
let unique_names:HashSet<_>=context.extra.values().flat_map(|instances|
instances.iter().map(|instance|instance.name.as_str())
).collect();
let plural=if unique_names.len()==1{"object"}else{"objects"};
let context=Separated::new(", ",||&unique_names);
summary_format!("DanglingTeleport","No matching Spawn for {plural}: {context}",())
summary_format!("DanglingTeleport","No matching Spawn for {plural}: {context}")
}
};
let duplicate_spawns=match &self.spawn_counts{
DuplicateCheck(Ok(()))=>CheckSummary::passed("DuplicateSpawn"),
DuplicateCheck(Ok(()))=>passed!("DuplicateSpawn"),
DuplicateCheck(Err(DuplicateCheckContext(context)))=>{
let context=Separated::new(", ",||context.iter().map(|(&stage_id,&names)|
Duplicates::new(StageElement{behaviour:StageElementBehaviour::Spawn,stage_id},names as usize)
let context=Separated::new(", ",||context.iter().map(|(&stage_id,&instances)|
Duplicates::new(StageElement{behaviour:StageElementBehaviour::Spawn,stage_id},instances as usize)
));
summary_format!("DuplicateSpawn","Duplicate Spawn: {context}",())
summary_format!("DuplicateSpawn","Duplicate Spawn: {context}")
}
};
let (extra_wormhole_in,missing_wormhole_in)=match &self.wormhole_in_counts{
SetDifferenceCheck(Ok(()))=>(CheckSummary::passed("ExtraWormholeIn"),CheckSummary::passed("MissingWormholeIn")),
SetDifferenceCheck(Ok(()))=>(passed!("ExtraWormholeIn"),passed!("MissingWormholeIn")),
SetDifferenceCheck(Err(context))=>(
if context.extra.is_empty(){
CheckSummary::passed("ExtraWormholeIn")
passed!("ExtraWormholeIn")
}else{
let context=Separated::new(", ",||context.extra.iter().map(|(&wormhole_id,_names)|
let context=Separated::new(", ",||context.extra.iter().map(|(&wormhole_id,_instances)|
WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id}
));
summary_format!("ExtraWormholeIn","WormholeIn with no matching WormholeOut: {context}",())
summary_format!("ExtraWormholeIn","WormholeIn with no matching WormholeOut: {context}")
},
if context.missing.is_empty(){
CheckSummary::passed("MissingWormholeIn")
passed!("MissingWormholeIn")
}else{
// This counts WormholeIn objects, but
// flipped logic is easier to understand
let context=Separated::new(", ",||context.missing.iter().map(|&wormhole_id|
WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id}
));
summary_format!("MissingWormholeIn","WormholeOut with no matching WormholeIn: {context}",())
summary_format!("MissingWormholeIn","WormholeOut with no matching WormholeIn: {context}")
}
)
};
let duplicate_wormhole_out=match &self.wormhole_out_counts{
DuplicateCheck(Ok(()))=>CheckSummary::passed("DuplicateWormholeOut"),
DuplicateCheck(Ok(()))=>passed!("DuplicateWormholeOut"),
DuplicateCheck(Err(DuplicateCheckContext(context)))=>{
let context=Separated::new(", ",||context.iter().map(|(&wormhole_id,&names)|
Duplicates::new(WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id},names as usize)
let context=Separated::new(", ",||context.iter().map(|(&wormhole_id,&instances)|
Duplicates::new(WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id},instances as usize)
));
summary_format!("DuplicateWormholeOut","Duplicate WormholeOut: {context}",())
summary_format!("DuplicateWormholeOut","Duplicate WormholeOut: {context}")
}
};
Ok(MapCheckList{checks:Box::new([
let unanchored_parts=match &self.unanchored_parts{
Ok(())=>passed!("UnanchoredParts"),
Err(unanchored_parts)=>{
let count=unanchored_parts.len();
let plural=if count==1{"part"}else{"parts"};
let context=Separated::new(", ",||unanchored_parts.iter().map(|&instance|
instance.name.as_str()
).take(20));
summary_format!("UnanchoredParts","{count} unanchored {plural}: {context}")
}
};
Ok(MapCheckList{checks:vec![
model_class,
model_name,
display_name,
@@ -775,34 +830,23 @@ impl MapCheck<'_>{
extra_wormhole_in,
missing_wormhole_in,
duplicate_wormhole_out,
])})
unanchored_parts,
]})
}
}
#[derive(serde::Serialize)]
struct MapCheckList{
checks:Box<[CheckSummary;16]>,
}
impl MapCheckList{
fn summary(&self)->String{
Separated::new("; ",||self.checks.iter().filter_map(|check|
(!check.passed).then_some(check.summary.as_str())
)).to_string()
}
pub struct MapCheckList{
pub checks:Vec<Check>,
}
pub struct Summary{
pub summary:String,
pub json:serde_json::Value,
}
pub struct CheckReportAndVersion{
pub status:Result<MapInfoOwned,Summary>,
pub struct CheckListAndVersion{
pub status:Result<MapInfoOwned,MapCheckList>,
pub version:u64,
}
impl crate::message_handler::MessageHandler{
pub async fn check_inner(&self,check_info:CheckRequest)->Result<CheckReportAndVersion,Error>{
pub async fn check_inner(&self,check_info:CheckRequest)->Result<CheckListAndVersion,Error>{
// discover asset creator and latest version
let info=self.cloud_context.get_asset_info(
rbx_asset::cloud::GetAssetLatestRequest{asset_id:check_info.ModelID}
@@ -827,6 +871,17 @@ impl crate::message_handler::MessageHandler{
// extract the root instance
let model_instance=get_root_instance(&dom).map_err(Error::GetRootInstance)?;
// skip checks
if check_info.SkipChecks{
// extract required fields
let map_info=get_mapinfo(&dom,model_instance);
let map_info_owned=map_info.try_into().map_err(Error::IntoMapInfoOwned)?;
let status=Ok(map_info_owned);
// return early
return Ok(CheckListAndVersion{status,version});
}
// extract information from the model
let model_info=get_model_info(&dom,model_instance);
@@ -836,13 +891,10 @@ impl crate::message_handler::MessageHandler{
// check the report, generate an error message if it fails the check
let status=match map_check.result(){
Ok(map_info)=>Ok(map_info),
Err(Ok(summary))=>Err(Summary{
summary:summary.summary(),
json:serde_json::to_value(&summary).map_err(Error::ToJsonValue)?,
}),
Err(Ok(check_list))=>Err(check_list),
Err(Err(e))=>return Err(Error::ToJsonValue(e)),
};
Ok(CheckReportAndVersion{status,version})
Ok(CheckListAndVersion{status,version})
}
}

View File

@@ -1,11 +1,11 @@
use crate::check::CheckReportAndVersion;
use crate::check::CheckListAndVersion;
use crate::nats_types::CheckMapfixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Check(crate::check::Error),
ApiActionMapfixCheck(submissions_api::Error),
ApiActionMapfixCheck(tonic::Status),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@@ -21,36 +21,49 @@ impl crate::message_handler::MessageHandler{
// update the mapfix depending on the result
match check_result{
Ok(CheckReportAndVersion{status:Ok(map_info),version})=>self.api.action_mapfix_submitted(
submissions_api::types::ActionMapfixSubmittedRequest{
MapfixID:mapfix_id,
ModelVersion:version,
DisplayName:map_info.display_name,
Creator:map_info.creator,
GameID:map_info.game_id as u32,
}
).await.map_err(Error::ApiActionMapfixCheck)?,
Ok(CheckListAndVersion{status:Ok(map_info),version})=>{
self.mapfixes.set_status_submitted(
rust_grpc::validator::SubmittedRequest{
id:mapfix_id,
model_version:version,
display_name:map_info.display_name,
creator:map_info.creator,
game_id:map_info.game_id as u32,
}
).await.map_err(Error::ApiActionMapfixCheck)?;
// Do not proceed to request changes
return Ok(());
},
// update the mapfix model status to request changes
Ok(CheckReportAndVersion{status:Err(report),..})=>self.api.action_mapfix_request_changes(
submissions_api::types::ActionMapfixRequestChangesRequest{
MapfixID:mapfix_id,
ErrorMessage:report.summary,
}
).await.map_err(Error::ApiActionMapfixCheck)?,
Ok(CheckListAndVersion{status:Err(check_list),..})=>{
self.mapfixes.create_audit_checklist(
rust_grpc::validator::AuditChecklistRequest{
id:mapfix_id,
check_list:check_list.checks,
}
).await.map_err(Error::ApiActionMapfixCheck)?;
},
// update the mapfix model status to request changes
Err(e)=>{
// log error
println!("[check_mapfix] Error: {e}");
self.api.action_mapfix_request_changes(
submissions_api::types::ActionMapfixRequestChangesRequest{
MapfixID:mapfix_id,
ErrorMessage:e.to_string(),
self.mapfixes.create_audit_error(
rust_grpc::validator::AuditErrorRequest{
id:mapfix_id,
error_message:e.to_string(),
}
).await.map_err(Error::ApiActionMapfixCheck)?;
},
}
self.mapfixes.set_status_request_changes(
rust_grpc::validator::MapfixId{
id:mapfix_id,
}
).await.map_err(Error::ApiActionMapfixCheck)?;
Ok(())
}
}

View File

@@ -1,11 +1,11 @@
use crate::check::CheckReportAndVersion;
use crate::check::CheckListAndVersion;
use crate::nats_types::CheckSubmissionRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Check(crate::check::Error),
ApiActionSubmissionCheck(submissions_api::Error),
ApiActionSubmissionCheck(tonic::Status),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@@ -22,36 +22,49 @@ impl crate::message_handler::MessageHandler{
// update the submission depending on the result
match check_result{
// update the submission model status to submitted
Ok(CheckReportAndVersion{status:Ok(map_info),version})=>self.api.action_submission_submitted(
submissions_api::types::ActionSubmissionSubmittedRequest{
SubmissionID:submission_id,
ModelVersion:version,
DisplayName:map_info.display_name,
Creator:map_info.creator,
GameID:map_info.game_id as u32,
}
).await.map_err(Error::ApiActionSubmissionCheck)?,
Ok(CheckListAndVersion{status:Ok(map_info),version})=>{
self.submissions.set_status_submitted(
rust_grpc::validator::SubmittedRequest{
id:submission_id,
model_version:version,
display_name:map_info.display_name,
creator:map_info.creator,
game_id:map_info.game_id as u32,
}
).await.map_err(Error::ApiActionSubmissionCheck)?;
// Do not proceed to request changes
return Ok(());
},
// update the submission model status to request changes
Ok(CheckReportAndVersion{status:Err(report),..})=>self.api.action_submission_request_changes(
submissions_api::types::ActionSubmissionRequestChangesRequest{
SubmissionID:submission_id,
ErrorMessage:report.summary,
Ok(CheckListAndVersion{status:Err(check_list),..})=>{
self.submissions.create_audit_checklist(
rust_grpc::validator::AuditChecklistRequest{
id:submission_id,
check_list:check_list.checks,
}
).await.map_err(Error::ApiActionSubmissionCheck)?,
).await.map_err(Error::ApiActionSubmissionCheck)?;
},
// update the submission model status to request changes
Err(e)=>{
// log error
println!("[check_submission] Error: {e}");
self.api.action_submission_request_changes(
submissions_api::types::ActionSubmissionRequestChangesRequest{
SubmissionID:submission_id,
ErrorMessage:e.to_string(),
self.submissions.create_audit_error(
rust_grpc::validator::AuditErrorRequest{
id:submission_id,
error_message:e.to_string(),
}
).await.map_err(Error::ApiActionSubmissionCheck)?;
},
}
self.submissions.set_status_request_changes(
rust_grpc::validator::SubmissionId{
id:submission_id,
}
).await.map_err(Error::ApiActionSubmissionCheck)?;
Ok(())
}
}

View File

@@ -5,7 +5,7 @@ use crate::create::CreateRequest;
#[derive(Debug)]
pub enum Error{
Create(crate::create::Error),
ApiActionMapfixCreate(submissions_api::Error),
ApiActionMapfixCreate(tonic::Status),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@@ -22,22 +22,22 @@ impl crate::message_handler::MessageHandler{
}).await.map_err(Error::Create)?;
// call create on api
self.api.create_mapfix(submissions_api::types::CreateMapfixRequest{
OperationID:create_info.OperationID,
AssetOwner:create_request.AssetOwner as i64,
DisplayName:create_request.DisplayName.as_deref().unwrap_or_default(),
Creator:create_request.Creator.as_deref().unwrap_or_default(),
self.mapfixes.create(rust_grpc::validator::MapfixCreate{
operation_id:create_info.OperationID,
asset_owner:create_request.AssetOwner,
display_name:create_request.DisplayName.unwrap_or_default(),
creator:create_request.Creator.unwrap_or_default(),
// not great TODO: make this great
GameID:create_request.GameID.unwrap_or(crate::rbx_util::GameID::Bhop) as i32,
AssetID:create_info.ModelID,
AssetVersion:create_request.AssetVersion,
TargetAssetID:create_info.TargetAssetID,
Description:create_info.Description.as_str(),
game_id:create_request.GameID.unwrap_or(crate::rbx_util::GameID::Bhop) as u32,
asset_id:create_info.ModelID,
asset_version:create_request.AssetVersion,
target_asset_id:create_info.TargetAssetID,
description:create_info.Description,
}).await.map_err(Error::ApiActionMapfixCreate)?;
Ok(())
}
pub async fn create_mapfix(&self,create_info:CreateMapfixRequest)->Result<(),submissions_api::Error>{
pub async fn create_mapfix(&self,create_info:CreateMapfixRequest)->Result<(),tonic::Status>{
let operation_id=create_info.OperationID;
let create_result=self.create_mapfix_inner(create_info).await;
@@ -46,9 +46,9 @@ impl crate::message_handler::MessageHandler{
// log error
println!("[create_mapfix] Error: {e}");
self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{
OperationID:operation_id,
StatusMessage:e.to_string(),
self.operations.fail(rust_grpc::validator::OperationFailRequest{
operation_id,
status_message:e.to_string(),
}).await?;
}

View File

@@ -6,7 +6,7 @@ use crate::rbx_util::GameID;
#[derive(Debug)]
pub enum Error{
Create(crate::create::Error),
ApiActionSubmissionCreate(submissions_api::Error),
ApiActionSubmissionCreate(tonic::Status),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@@ -23,35 +23,35 @@ impl crate::message_handler::MessageHandler{
// grab values from submission form, otherwise try to fill blanks from map data
let display_name=if create_info.DisplayName.is_empty(){
create_request.DisplayName.as_deref().unwrap_or_default()
create_request.DisplayName.unwrap_or_default()
}else{
create_info.DisplayName.as_str()
create_info.DisplayName
};
let creator=if create_info.Creator.is_empty(){
create_request.Creator.as_deref().unwrap_or_default()
create_request.Creator.unwrap_or_default()
}else{
create_info.Creator.as_str()
create_info.Creator
};
let game_id=create_info.GameID.try_into().ok().or(create_request.GameID).unwrap_or(GameID::Bhop);
// call create on api
self.api.create_submission(submissions_api::types::CreateSubmissionRequest{
OperationID:create_info.OperationID,
AssetOwner:create_request.AssetOwner as i64,
DisplayName:display_name,
Creator:creator,
GameID:game_id as i32,
AssetID:create_info.ModelID,
AssetVersion:create_request.AssetVersion,
Status:create_info.Status,
Roles:create_info.Roles,
self.submissions.create(rust_grpc::validator::SubmissionCreate{
operation_id:create_info.OperationID,
asset_owner:create_request.AssetOwner,
display_name:display_name,
creator:creator,
game_id:game_id as u32,
asset_id:create_info.ModelID,
asset_version:create_request.AssetVersion,
status:create_info.Status,
roles:create_info.Roles,
}).await.map_err(Error::ApiActionSubmissionCreate)?;
Ok(())
}
pub async fn create_submission(&self,create_info:CreateSubmissionRequest)->Result<(),submissions_api::Error>{
pub async fn create_submission(&self,create_info:CreateSubmissionRequest)->Result<(),tonic::Status>{
let operation_id=create_info.OperationID;
let create_result=self.create_submission_inner(create_info).await;
@@ -60,9 +60,9 @@ impl crate::message_handler::MessageHandler{
// log error
println!("[create_submission] Error: {e}");
self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{
OperationID:operation_id,
StatusMessage:e.to_string(),
self.operations.fail(rust_grpc::validator::OperationFailRequest{
operation_id:operation_id,
status_message:e.to_string(),
}).await?;
}

View File

@@ -0,0 +1,19 @@
use rust_grpc::validator::{ScriptId,ScriptPolicyId};
#[derive(Debug)]
#[expect(dead_code)]
pub enum SingleItemError<Items>{
DuplicateItems(Items),
Other(tonic::Status),
}
impl<Items> std::fmt::Display for SingleItemError<Items>
where
Items:std::fmt::Debug
{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl<Items> std::error::Error for SingleItemError<Items> where Items:std::fmt::Debug{}
pub type ScriptSingleItemError=SingleItemError<Vec<ScriptId>>;
pub type ScriptPolicySingleItemError=SingleItemError<Vec<ScriptPolicyId>>;

View File

@@ -0,0 +1,23 @@
use crate::endpoint;
use rust_grpc::validator::*;
pub type ValidatorMapfixesServiceClient=rust_grpc::validator::validator_mapfix_service_client::ValidatorMapfixServiceClient<tonic::transport::channel::Channel>;
#[derive(Clone)]
pub struct Service{
client:ValidatorMapfixesServiceClient,
}
impl Service{
pub fn new(
client:ValidatorMapfixesServiceClient,
)->Self{
Self{client}
}
endpoint!(create,MapfixCreate,MapfixId);
endpoint!(create_audit_error,AuditErrorRequest,NullResponse);
endpoint!(create_audit_checklist,AuditChecklistRequest,NullResponse);
endpoint!(set_validated_model,ValidatedModelRequest,NullResponse);
endpoint!(set_status_submitted,SubmittedRequest,NullResponse);
endpoint!(set_status_request_changes,MapfixId,NullResponse);
endpoint!(set_status_validated,MapfixId,NullResponse);
endpoint!(set_status_failed,MapfixId,NullResponse);
endpoint!(set_status_uploaded,MapfixId,NullResponse);
}

View File

@@ -0,0 +1,17 @@
pub mod error;
pub mod mapfixes;
pub mod operations;
pub mod scripts;
pub mod script_policy;
pub mod submissions;
#[macro_export]
macro_rules! endpoint{
($fn:ident,$in:ident,$out:ident)=>{
pub async fn $fn(&self,request:$in)->Result<$out,tonic::Status>{
Ok(self.client.clone().$fn(request).await?.into_inner())
}
};
}

View File

@@ -0,0 +1,15 @@
use crate::endpoint;
use rust_grpc::validator::*;
pub type ValidatorOperationsServiceClient=rust_grpc::validator::validator_operation_service_client::ValidatorOperationServiceClient<tonic::transport::channel::Channel>;
#[derive(Clone)]
pub struct Service{
client:ValidatorOperationsServiceClient,
}
impl Service{
pub fn new(
client:ValidatorOperationsServiceClient,
)->Self{
Self{client}
}
endpoint!(fail,OperationFailRequest,NullResponse);
}

View File

@@ -0,0 +1,34 @@
use crate::endpoint;
use crate::grpc::error::ScriptPolicySingleItemError;
use rust_grpc::validator::*;
pub type ValidatorScriptPolicyServiceClient=rust_grpc::validator::validator_script_policy_service_client::ValidatorScriptPolicyServiceClient<tonic::transport::channel::Channel>;
#[derive(Clone)]
pub struct Service{
client:ValidatorScriptPolicyServiceClient,
}
impl Service{
pub fn new(
client:ValidatorScriptPolicyServiceClient,
)->Self{
Self{client}
}
endpoint!(create,ScriptPolicyCreate,ScriptPolicyId);
endpoint!(list,ScriptPolicyListRequest,ScriptPolicyListResponse);
pub async fn get_from_hash(&self,hash:u64)->Result<Option<ScriptPolicy>,ScriptPolicySingleItemError>{
let policies=self.list(ScriptPolicyListRequest{
filter:Some(ScriptPolicyFilter{
from_script_hash:Some(hash),
to_script_id:None,
policy:None,
}),
page:Some(Pagination{
number:1,
size:2,
}),
}).await.map_err(ScriptPolicySingleItemError::Other)?;
if 1<policies.script_policies.len(){
return Err(ScriptPolicySingleItemError::DuplicateItems(policies.script_policies.into_iter().map(|item|ScriptPolicyId{id:item.id}).collect()));
}
Ok(policies.script_policies.into_iter().next())
}
}

View File

@@ -0,0 +1,37 @@
use crate::endpoint;
use crate::grpc::error::ScriptSingleItemError;
use rust_grpc::validator::*;
pub type ValidatorScriptsServiceClient=rust_grpc::validator::validator_script_service_client::ValidatorScriptServiceClient<tonic::transport::channel::Channel>;
#[derive(Clone)]
pub struct Service{
client:ValidatorScriptsServiceClient,
}
impl Service{
pub fn new(
client:ValidatorScriptsServiceClient,
)->Self{
Self{client}
}
endpoint!(create,ScriptCreate,ScriptId);
endpoint!(get,ScriptId,Script);
endpoint!(list,ScriptListRequest,ScriptListResponse);
pub async fn get_from_hash(&self,hash:u64)->Result<Option<Script>,ScriptSingleItemError>{
let policies=self.list(ScriptListRequest{
filter:Some(ScriptFilter{
hash:Some(hash),
name:None,
source:None,
resource_type:None,
resource_id:None,
}),
page:Some(Pagination{
number:1,
size:2,
}),
}).await.map_err(ScriptSingleItemError::Other)?;
if 1<policies.scripts.len(){
return Err(ScriptSingleItemError::DuplicateItems(policies.scripts.into_iter().map(|item|ScriptId{id:item.id}).collect()));
}
Ok(policies.scripts.into_iter().next())
}
}

View File

@@ -0,0 +1,23 @@
use crate::endpoint;
use rust_grpc::validator::*;
pub type ValidatorSubmissionsServiceClient=rust_grpc::validator::validator_submission_service_client::ValidatorSubmissionServiceClient<tonic::transport::channel::Channel>;
#[derive(Clone)]
pub struct Service{
client:ValidatorSubmissionsServiceClient,
}
impl Service{
pub fn new(
client:ValidatorSubmissionsServiceClient,
)->Self{
Self{client}
}
endpoint!(create,SubmissionCreate,SubmissionId);
endpoint!(create_audit_error,AuditErrorRequest,NullResponse);
endpoint!(create_audit_checklist,AuditChecklistRequest,NullResponse);
endpoint!(set_validated_model,ValidatedModelRequest,NullResponse);
endpoint!(set_status_submitted,SubmittedRequest,NullResponse);
endpoint!(set_status_request_changes,SubmissionId,NullResponse);
endpoint!(set_status_validated,SubmissionId,NullResponse);
endpoint!(set_status_failed,SubmissionId,NullResponse);
endpoint!(set_status_uploaded,StatusUploadedRequest,NullResponse);
}

Some files were not shown because too many files have changed in this diff Show More