91 Commits

Author SHA1 Message Date
c3f497be48 wip: hide reset buttons
All checks were successful
continuous-integration/drone/push Build is passing
2025-05-13 22:58:53 -07:00
a95e6b7a9a docker: add AUTH_HOST env var to docker compose
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-04-15 18:55:34 -07:00
ic3w0lf
4c1aef9113 Update README
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-04-15 18:57:35 -06:00
ic3w0lf
c98d170423 Remove hardcoded auth URLs
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-04-15 18:50:40 -06:00
6d14047f57 web: unused imports
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-04-15 16:49:05 -07:00
41663624d3 web: conditionally show avatar when logged in 2025-04-15 16:49:05 -07:00
49b9b41085 web: create login button
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-15 16:20:52 -07:00
3614018794 web: remove redirect 2025-04-15 16:20:48 -07:00
872b98aa74 web: explain admin buttons a bit better
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-15 15:56:52 -07:00
d5c8477869 web: const enum typescript xD
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-15 15:45:16 -07:00
b600ca582b web: show submit button for admin on ChangesRequested status 2025-04-15 15:45:16 -07:00
adbcbed9ac submissions: allow admin to submit from changes requested 2025-04-15 15:45:16 -07:00
8f8d685f71 validator: plumb fields 2025-04-15 15:45:16 -07:00
a669de3c0b submissions: allow bypass by admin in internal CreateSubmission 2025-04-15 15:45:16 -07:00
649b941d5f submissions: implement CreateSubmissionAdmin endpoint 2025-04-15 15:45:16 -07:00
1b4456f30a submissions: add initial fields 2025-04-15 15:31:55 -07:00
d34a5c7091 openapi: generate 2025-04-15 15:13:53 -07:00
2f36877cb6 openapi: admin create endpoint 2025-04-15 15:13:44 -07:00
3a124b8190 web: add hidden admin submit page 2025-04-15 14:23:25 -07:00
6cc6da4879 web: display username in audit events
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-15 12:46:35 -07:00
123b0c9a81 web: add Username field to AuditEvent
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-15 12:43:36 -07:00
54b0abbbf3 web: tweak submit button text
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-13 17:16:55 -07:00
1b0384da11 submissions: fetch usernames from data service
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-13 17:14:44 -07:00
e0cebfd80e submissions: rename svc.Client to svc.Maps 2025-04-13 17:14:21 -07:00
5ba52ecb57 openapi: generate 2025-04-13 17:02:31 -07:00
9e42050a65 openapi: include usernames in AuditEvent 2025-04-13 17:02:28 -07:00
c817bfc8c8 validator: flatten check matches 2025-04-13 16:33:23 -07:00
8f97ca6690 validator: tweak error message
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-12 21:01:23 -07:00
f220cb62bc validator: fix empty check
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-12 12:31:05 -07:00
f090fd7d68 validator: fix duplicate checks
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-12 12:29:17 -07:00
404e1281ff validator: improve "extra" error messages
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-12 12:02:34 -07:00
e4f710c83f validator: include original names of some objects in error message
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-12 11:58:27 -07:00
a942c81ea8 validator: add teleport and wormhole set difference checks
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-12 11:39:25 -07:00
109b24061a validator: pluralize some error messages 2025-04-12 11:33:32 -07:00
ddef30984f validator: remove placeholder comments 2025-04-11 23:42:43 -07:00
9331f37d70 validator: remove explicit StringEmptyCheck newtype
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-11 23:20:48 -07:00
c4f910c1f0 validator: comment ModelInfo::check
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-04-11 23:11:59 -07:00
343a4011dd validator: tweak write_zone macro
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-11 23:06:10 -07:00
c63997d161 validator: implement dangling anticheat zone check
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-11 22:59:37 -07:00
ea58fcedc9 validator: save some loc with default
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-11 22:30:55 -07:00
50e3fb283c validator: comment ModelInfo::check
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-11 22:25:49 -07:00
aa513a7973 validator: code tweaks 2025-04-11 22:20:59 -07:00
eff9097456 validator: remove newline
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-11 21:59:37 -07:00
668c5fef51 validator: move function call so get_model_info is infallible
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-11 21:46:44 -07:00
57db5f738e todo
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-11 21:03:57 -07:00
3789755a19 submissions: add updated info to validator-submitted
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-04-11 21:01:58 -07:00
ee6c37ab9d openapi: generate 2025-04-11 21:01:58 -07:00
12bfbfb0a0 openapi: add updated info to validator-submitted 2025-04-11 21:01:58 -07:00
c57a53692d validator: code tweaks 2025-04-11 21:01:58 -07:00
ccf07c5931 validator: rename AtLeastOneMatchingAndNoExtraCheck to SetDifferenceCheck
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-11 20:37:37 -07:00
6efab4f411 validator: annotate MapCheck fields
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-11 20:19:49 -07:00
34d1db02a5 web: implement audit log on submissions
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-11 19:36:54 -07:00
d86ed0cdf5 web: marginally improve audit events
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-04-11 19:27:25 -07:00
d19763349e web: fetch audit events and generate comments
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-11 19:03:01 -07:00
5846e92924 validator: write check error message
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-11 18:03:45 -07:00
34b8d7475d validator: rustify map check 2025-04-11 17:55:59 -07:00
a5daa2df4a validator: update metadata on Submitted 2025-04-11 17:38:21 -07:00
1b73af9fe2 validator: allow create without valid metadata 2025-04-11 17:38:21 -07:00
8433030562 web: add submission fields 2025-04-11 17:38:21 -07:00
8372665fd3 submissions: fields plumbing 2025-04-11 17:38:21 -07:00
d24b342738 openapi: generate 2025-04-11 13:11:51 -07:00
796f31aadf openapi: add fields to submission create 2025-04-11 13:08:22 -07:00
44f8736838 web: add description on mapfix page
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-11 13:04:52 -07:00
b7e5d82c13 web: add description form field 2025-04-11 13:04:52 -07:00
169007f16e validator: description plumbing 2025-04-11 13:04:52 -07:00
2519c9faa1 submissions: description plumbing 2025-04-11 13:04:52 -07:00
1ff6bdbd4c openapi: generate 2025-04-11 13:00:10 -07:00
d1ca9bdab9 openapi: add Description to mapfix create 2025-04-11 12:59:42 -07:00
c76ff3b687 validation: use to_string instead of format
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-11 01:51:32 +00:00
a42501d254 submissions-api: change StatusMessage to ErrorMessage 2025-04-11 01:51:32 +00:00
f915c51ba4 web: remove StatusMessage 2025-04-11 01:51:32 +00:00
ff9da333eb submissions: push audit error event on error endpoints 2025-04-11 01:51:32 +00:00
1dabd216aa openapi: generate 2025-04-11 01:51:32 +00:00
cc7e890580 openapi: change StatusMessage to ErrorMessage 2025-04-11 01:51:32 +00:00
99d1b38535 submissions: remove StatusMessage 2025-04-11 01:51:32 +00:00
12ca1b7dab submissions: AuditEventTypeError 2025-04-11 01:51:32 +00:00
fa1b44f172 update deps
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-10 17:21:45 -07:00
03519e9337 validator: marginally improve map check clarity
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-09 21:04:51 -07:00
60b6d30379 validator: fix map check bug
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-09 20:53:25 -07:00
19b8f7b7a2 validator: use newlines in check report
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-09 20:37:58 -07:00
4f586c6176 web: add reset submit button
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-09 19:55:38 -07:00
d1a70509b7 submissions: implement map checks
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-04-09 19:50:17 -07:00
95bfb87c6e validator: implement map checks 2025-04-09 19:50:17 -07:00
de0cf37918 validator: add heck + lazy_regex deps 2025-04-09 19:48:44 -07:00
f1fd826c62 submissions-api: implement validator-submitted endpoint 2025-04-09 19:48:44 -07:00
1380a00872 submissions: receive asset version 2025-04-09 19:39:13 -07:00
174a210f81 submissions: implement validator-request-changes endpoints 2025-04-09 19:39:13 -07:00
67a03f394f openapi: generate 2025-04-09 19:39:13 -07:00
6eebe404d5 openapi: validator-request-changes endpoint 2025-04-09 19:39:13 -07:00
1d409218a5 validation: factor out asset download 2025-04-09 19:39:13 -07:00
e2c72c90c7 validator: prepare for checks 2025-04-08 17:06:54 -07:00
90 changed files with 6341 additions and 1741 deletions

152
Cargo.lock generated
View File

@@ -148,9 +148,9 @@ checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "blake3"
version = "1.7.0"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b17679a8d69b6d7fd9cd9801a536cec9fa5e5970b69f9d4747f70b39b031f5e7"
checksum = "389a099b34312839e16420d499a9cad9650541715937ffbdd40d36f49e77eeb3"
dependencies = [
"arrayref",
"arrayvec",
@@ -191,9 +191,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.17"
version = "1.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
dependencies = [
"shlex",
]
@@ -320,9 +320,9 @@ dependencies = [
[[package]]
name = "deranged"
version = "0.4.1"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058"
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
dependencies = [
"powerfmt",
"serde",
@@ -388,9 +388,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.10"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [
"libc",
"windows-sys 0.59.0",
@@ -410,9 +410,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "flate2"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
dependencies = [
"crc32fast",
"miniz_oxide",
@@ -601,6 +601,12 @@ version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "http"
version = "1.3.1"
@@ -696,9 +702,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.10"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2"
dependencies = [
"bytes",
"futures-channel",
@@ -706,6 +712,7 @@ dependencies = [
"http",
"http-body",
"hyper",
"libc",
"pin-project-lite",
"socket2",
"tokio",
@@ -715,9 +722,9 @@ dependencies = [
[[package]]
name = "iana-time-zone"
version = "0.1.62"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2fd658b06e56721792c5df4475705b6cda790e9298d19d2f8af083457bcd127"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@@ -878,9 +885,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.8.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown",
@@ -908,6 +915,29 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy-regex"
version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126"
dependencies = [
"lazy-regex-proc_macros",
"once_cell",
"regex",
]
[[package]]
name = "lazy-regex-proc_macros"
version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -922,9 +952,9 @@ checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
[[package]]
name = "linux-raw-sys"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "litemap"
@@ -963,6 +993,8 @@ version = "0.1.1"
dependencies = [
"async-nats",
"futures",
"heck",
"lazy-regex",
"rbx_asset",
"rbx_binary",
"rbx_dom_weak",
@@ -999,9 +1031,9 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.8.5"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]
@@ -1090,9 +1122,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "openssl"
version = "0.10.71"
version = "0.10.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd"
checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
dependencies = [
"bitflags 2.9.0",
"cfg-if",
@@ -1122,9 +1154,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.106"
version = "0.9.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd"
checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07"
dependencies = [
"cc",
"libc",
@@ -1297,10 +1329,11 @@ dependencies = [
[[package]]
name = "rbx_asset"
version = "0.4.3"
version = "0.4.4-pre2"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "077e2a0b201a777dfd2ff822766ae7d0c8c3003206115da57f7bce15ee73cbc7"
checksum = "a32b98d3f303002faae783b5c1087e35c36a260b56470fd14a0606f35169ab35"
dependencies = [
"bytes",
"chrono",
"flate2",
"reqwest",
@@ -1513,9 +1546,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.0.3"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96"
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
dependencies = [
"bitflags 2.9.0",
"errno",
@@ -1526,9 +1559,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.25"
version = "0.23.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c"
checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
dependencies = [
"once_cell",
"ring",
@@ -1766,15 +1799,15 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.14.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "socket2"
version = "0.5.8"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
dependencies = [
"libc",
"windows-sys 0.52.0",
@@ -1941,9 +1974,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.44.1"
version = "1.44.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
dependencies = [
"backtrace",
"bytes",
@@ -2271,11 +2304,37 @@ dependencies = [
[[package]]
name = "windows-core"
version = "0.52.0"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-targets 0.52.6",
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings 0.4.0",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
@@ -2291,7 +2350,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
dependencies = [
"windows-result",
"windows-strings",
"windows-strings 0.3.1",
"windows-targets 0.53.0",
]
@@ -2313,6 +2372,15 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
@@ -2482,9 +2550,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
[[package]]
name = "xml-rs"
version = "0.8.25"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4"
checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda"
[[package]]
name = "yoke"

View File

@@ -26,10 +26,11 @@ Prerequisite: golang installed
Prerequisite: bun installed
The environment variable `API_HOST` will need to be set for the middleware.
The environment variables `API_HOST` and `AUTH_HOST` will need to be set for the middleware.
Example `.env` in web's root:
```
API_HOST="http://localhost:8082/v1/"
AUTH_HOST="http://localhost:8083/"
```
1. `cd web`

View File

@@ -50,6 +50,7 @@ services:
- "3000:3000"
environment:
- API_HOST=http://submissions:8082/v1
- AUTH_HOST=http://localhost:8080/
validation:
image:

View File

@@ -47,14 +47,14 @@ paths:
required: true
schema:
type: integer
format: uint64
format: int64
minimum: 0
- name: ValidatedModelVersion
in: query
required: true
schema:
type: integer
format: uint64
format: int64
minimum: 0
responses:
"204":
@@ -73,6 +73,56 @@ paths:
- 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
@@ -107,7 +157,7 @@ paths:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: StatusMessage
- name: ErrorMessage
in: query
required: true
schema:
@@ -202,14 +252,14 @@ paths:
required: true
schema:
type: integer
format: uint64
format: int64
minimum: 0
- name: ValidatedModelVersion
in: query
required: true
schema:
type: integer
format: uint64
format: int64
minimum: 0
responses:
"204":
@@ -228,6 +278,56 @@ paths:
- 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
@@ -262,7 +362,7 @@ paths:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: StatusMessage
- name: ErrorMessage
in: query
required: true
schema:
@@ -291,7 +391,7 @@ paths:
required: true
schema:
type: integer
format: uint64
format: int64
minimum: 0
responses:
"204":
@@ -321,13 +421,13 @@ paths:
in: query
schema:
type: integer
format: uint64
format: int64
minimum: 0
- name: Policy
in: query
schema:
type: integer
format: uint32
format: int32
minimum: 0
responses:
"200":
@@ -397,13 +497,13 @@ paths:
in: query
schema:
type: integer
format: uint32
format: int32
minimum: 0
- name: ResourceID
in: query
schema:
type: integer
format: uint64
format: int64
minimum: 0
responses:
"200":
@@ -474,7 +574,7 @@ components:
description: The unique identifier for a submission.
schema:
type: integer
format: uint64
format: int64
minimum: 0
OperationID:
name: OperationID
@@ -483,7 +583,7 @@ components:
description: The unique identifier for a long-running operation.
schema:
type: integer
format: uint32
format: int32
minimum: 0
SubmissionID:
name: SubmissionID
@@ -492,7 +592,7 @@ components:
description: The unique identifier for a submission.
schema:
type: integer
format: uint64
format: int64
minimum: 0
ScriptID:
name: ScriptID
@@ -501,7 +601,7 @@ components:
description: The unique identifier for a script.
schema:
type: integer
format: uint64
format: int64
minimum: 0
Page:
name: Page
@@ -509,7 +609,7 @@ components:
required: true
schema:
type: integer
format: uint32
format: int32
minimum: 1
Limit:
name: Limit
@@ -517,7 +617,7 @@ components:
required: true
schema:
type: integer
format: uint32
format: int32
minimum: 1
maximum: 100
schemas:
@@ -528,7 +628,7 @@ components:
properties:
MapfixID:
type: integer
format: uint64
format: int64
minimum: 0
SubmissionID:
required:
@@ -537,7 +637,7 @@ components:
properties:
SubmissionID:
type: integer
format: uint64
format: int64
minimum: 0
ScriptID:
required:
@@ -546,7 +646,7 @@ components:
properties:
ScriptID:
type: integer
format: uint64
format: int64
minimum: 0
ScriptPolicyID:
required:
@@ -555,7 +655,7 @@ components:
properties:
ScriptPolicyID:
type: integer
format: uint64
format: int64
minimum: 0
MapfixCreate:
required:
@@ -567,15 +667,16 @@ components:
- AssetID
- AssetVersion
- TargetAssetID
- Description
type: object
properties:
OperationID:
type: integer
format: uint32
format: int32
minimum: 0
AssetOwner:
type: integer
format: uint64
format: int64
minimum: 0
DisplayName:
type: string
@@ -585,20 +686,23 @@ components:
maxLength: 128
GameID:
type: integer
format: uint32
format: int32
minimum: 0
AssetID:
type: integer
format: uint64
format: int64
minimum: 0
AssetVersion:
type: integer
format: uint64
format: int64
minimum: 0
TargetAssetID:
type: integer
format: uint64
format: int64
minimum: 0
Description:
type: string
maxLength: 256
SubmissionCreate:
required:
- OperationID
@@ -608,15 +712,17 @@ components:
- GameID
- AssetID
- AssetVersion
- Status
- Roles
type: object
properties:
OperationID:
type: integer
format: uint32
format: int32
minimum: 0
AssetOwner:
type: integer
format: uint64
format: int64
minimum: 0
DisplayName:
type: string
@@ -626,16 +732,24 @@ components:
maxLength: 128
GameID:
type: integer
format: uint32
format: int32
minimum: 0
AssetID:
type: integer
format: uint64
format: int64
minimum: 0
AssetVersion:
type: integer
format: uint64
format: int64
minimum: 0
Status:
type: integer
format: uint32
minimum: 0
maximum: 9
Roles:
type: integer
format: uint32
Script:
required:
- ID
@@ -648,7 +762,7 @@ components:
properties:
ID:
type: integer
format: uint64
format: int64
minimum: 0
Name:
type: string
@@ -662,11 +776,11 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: uint32
format: int32
minimum: 0
ResourceID:
type: integer
format: uint64
format: int64
minimum: 0
ScriptCreate:
required:
@@ -684,11 +798,11 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: uint32
format: int32
minimum: 0
ResourceID:
type: integer
format: uint64
format: int64
minimum: 0
ScriptPolicy:
required:
@@ -700,7 +814,7 @@ components:
properties:
ID:
type: integer
format: uint64
format: int64
minimum: 0
FromScriptHash:
type: string
@@ -708,11 +822,11 @@ components:
maxLength: 16
ToScriptID:
type: integer
format: uint64
format: int64
minimum: 0
Policy:
type: integer
format: uint32
format: int32
minimum: 0
ScriptPolicyCreate:
required:
@@ -723,15 +837,15 @@ components:
properties:
FromScriptID:
type: integer
format: uint64
format: int64
minimum: 0
ToScriptID:
type: integer
format: uint64
format: int64
minimum: 0
Policy:
type: integer
format: uint32
format: int32
minimum: 0
Error:
description: Represents error object
@@ -739,7 +853,7 @@ components:
properties:
code:
type: integer
format: uint64
format: int64
minimum: 0
message:
type: string

View File

@@ -104,14 +104,14 @@ paths:
in: query
schema:
type: integer
format: uint32
format: int32
minimum: 1
maximum: 5
- name: Sort
in: query
schema:
type: integer
format: uint32
format: int32
minimum: 0
maximum: 4
responses:
@@ -175,39 +175,39 @@ paths:
in: query
schema:
type: integer
format: uint32
format: int32
minimum: 1
maximum: 5
- name: Sort
in: query
schema:
type: integer
format: uint32
format: int32
minimum: 0
maximum: 4
- name: Submitter
in: query
schema:
type: integer
format: uint64
format: int64
minimum: 0
- name: AssetID
in: query
schema:
type: integer
format: uint64
format: int64
minimum: 0
- name: TargetAssetID
in: query
schema:
type: integer
format: uint64
format: int64
minimum: 0
- name: StatusID
in: query
schema:
type: integer
format: uint32
format: int32
minimum: 0
maximum: 9
responses:
@@ -332,14 +332,14 @@ paths:
required: true
schema:
type: integer
format: uint64
format: int64
minimum: 0
- name: ModelVersion
in: query
required: true
schema:
type: integer
format: uint64
format: int64
minimum: 0
responses:
"204":
@@ -582,39 +582,39 @@ paths:
in: query
schema:
type: integer
format: uint32
format: int32
minimum: 1
maximum: 5
- name: Sort
in: query
schema:
type: integer
format: uint32
format: int32
minimum: 0
maximum: 4
- name: Submitter
in: query
schema:
type: integer
format: uint64
format: int64
minimum: 0
- name: AssetID
in: query
schema:
type: integer
format: uint64
format: int64
minimum: 0
- name: UploadedAssetID
in: query
schema:
type: integer
format: uint64
format: int64
minimum: 0
- name: StatusID
in: query
schema:
type: integer
format: uint32
format: int32
minimum: 0
maximum: 10
responses:
@@ -654,6 +654,31 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions-admin:
post:
summary: Trigger the validator to create a new submission
operationId: createSubmissionAdmin
tags:
- Submissions
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SubmissionTriggerCreate'
responses:
"201":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/OperationID"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}:
get:
summary: Retrieve map with ID
@@ -739,14 +764,14 @@ paths:
required: true
schema:
type: integer
format: uint64
format: int64
minimum: 0
- name: ModelVersion
in: query
required: true
schema:
type: integer
format: uint64
format: int64
minimum: 0
responses:
"204":
@@ -989,13 +1014,13 @@ paths:
in: query
schema:
type: integer
format: uint64
format: int64
minimum: 0
- name: Policy
in: query
schema:
type: integer
format: uint32
format: int32
minimum: 0
responses:
"200":
@@ -1126,13 +1151,13 @@ paths:
in: query
schema:
type: integer
format: uint32
format: int32
minimum: 0
- name: ResourceID
in: query
schema:
type: integer
format: uint64
format: int64
minimum: 0
responses:
"200":
@@ -1247,7 +1272,7 @@ components:
description: The unique identifier for a map.
schema:
type: integer
format: uint64
format: int64
minimum: 0
MapfixID:
name: MapfixID
@@ -1256,7 +1281,7 @@ components:
description: The unique identifier for a mapfix.
schema:
type: integer
format: uint64
format: int64
minimum: 0
OperationID:
name: OperationID
@@ -1265,7 +1290,7 @@ components:
description: The unique identifier for a long-running operation.
schema:
type: integer
format: uint32
format: int32
minimum: 0
SubmissionID:
name: SubmissionID
@@ -1274,7 +1299,7 @@ components:
description: The unique identifier for a submission.
schema:
type: integer
format: uint64
format: int64
minimum: 0
ScriptID:
name: ScriptID
@@ -1283,7 +1308,7 @@ components:
description: The unique identifier for a script.
schema:
type: integer
format: uint64
format: int64
minimum: 0
ScriptPolicyID:
name: ScriptPolicyID
@@ -1292,7 +1317,7 @@ components:
description: The unique identifier for a script policy.
schema:
type: integer
format: uint64
format: int64
minimum: 0
Page:
name: Page
@@ -1300,7 +1325,7 @@ components:
required: true
schema:
type: integer
format: uint32
format: int32
minimum: 1
Limit:
name: Limit
@@ -1308,7 +1333,7 @@ components:
required: true
schema:
type: integer
format: uint32
format: int32
minimum: 0
maximum: 100
schemas:
@@ -1318,6 +1343,7 @@ components:
- ID
- Date
- User
- Username
- ResourceType
- ResourceID
- EventType
@@ -1325,23 +1351,26 @@ components:
properties:
ID:
type: integer
format: uint64
format: int64
Date:
type: integer
format: int64
User:
type: integer
format: uint64
format: int64
Username:
type: string
maxLength: 64
ResourceType:
type: integer
format: uint32
format: int32
description: Is this a submission or is it a mapfix
ResourceID:
type: integer
format: uint64
format: int64
EventType:
type: integer
format: uint32
format: int32
EventData:
type: object
description: Arbitrary event data
@@ -1353,7 +1382,7 @@ components:
properties:
OperationID:
type: integer
format: uint32
format: int32
minimum: 0
ScriptID:
required:
@@ -1362,7 +1391,7 @@ components:
properties:
ScriptID:
type: integer
format: uint64
format: int64
minimum: 0
ScriptPolicyID:
required:
@@ -1371,7 +1400,7 @@ components:
properties:
ScriptPolicyID:
type: integer
format: uint64
format: int64
minimum: 0
Roles:
required:
@@ -1380,7 +1409,7 @@ components:
properties:
Roles:
type: integer
format: uint32
format: int32
minimum: 0
User:
required:
@@ -1391,7 +1420,7 @@ components:
properties:
UserID:
type: integer
format: uint64
format: int64
minimum: 0
Username:
type: string
@@ -1410,7 +1439,7 @@ components:
properties:
ID:
type: integer
format: uint64
format: int64
minimum: 0
DisplayName:
type: string
@@ -1420,7 +1449,7 @@ components:
maxLength: 128
GameID:
type: integer
format: uint32
format: int32
minimum: 0
Date:
type: integer
@@ -1440,12 +1469,12 @@ components:
- Completed
- TargetAssetID
- StatusID
- StatusMessage
- Description
type: object
properties:
ID:
type: integer
format: uint64
format: int64
minimum: 0
DisplayName:
type: string
@@ -1455,7 +1484,7 @@ components:
maxLength: 128
GameID:
type: integer
format: uint32
format: int32
minimum: 0
CreatedAt:
type: integer
@@ -1467,27 +1496,27 @@ components:
minimum: 0
Submitter:
type: integer
format: uint64
format: int64
minimum: 0
AssetID:
type: integer
format: uint64
format: int64
minimum: 0
AssetVersion:
type: integer
format: uint64
format: int64
minimum: 0
Completed:
type: boolean
TargetAssetID:
type: integer
format: uint64
format: int64
minimum: 0
StatusID:
type: integer
format: uint32
format: int32
minimum: 0
StatusMessage:
Description:
type: string
maxLength: 256
Mapfixes:
@@ -1498,7 +1527,7 @@ components:
properties:
Total:
type: integer
format: uint64
format: int64
minimum: 0
Mapfixes:
type: array
@@ -1508,16 +1537,20 @@ components:
required:
- AssetID
- TargetAssetID
- Description
type: object
properties:
AssetID:
type: integer
format: uint64
format: int64
minimum: 0
TargetAssetID:
type: integer
format: uint64
format: int64
minimum: 0
Description:
type: string
maxLength: 256
Operation:
required:
- OperationID
@@ -1530,7 +1563,7 @@ components:
properties:
OperationID:
type: integer
format: uint32
format: int32
minimum: 0
Date:
type: integer
@@ -1538,11 +1571,11 @@ components:
minimum: 0
Owner:
type: integer
format: uint64
format: int64
minimum: 0
Status:
type: integer
format: uint32
format: int32
minimum: 0
StatusMessage:
type: string
@@ -1566,12 +1599,11 @@ components:
- Completed
# - UploadedAssetID
- StatusID
- StatusMessage
type: object
properties:
ID:
type: integer
format: uint64
format: int64
minimum: 0
DisplayName:
type: string
@@ -1581,49 +1613,46 @@ components:
maxLength: 128
GameID:
type: integer
format: uint32
format: int32
minimum: 0
CreatedAt:
type: integer
format: uint64
format: int64
minimum: 0
UpdatedAt:
type: integer
format: uint64
format: int64
minimum: 0
Submitter:
type: integer
format: uint64
format: int64
minimum: 0
AssetID:
type: integer
format: uint64
format: int64
minimum: 0
AssetVersion:
type: integer
format: uint64
format: int64
minimum: 0
ValidatedAssetID:
type: integer
format: uint64
format: int64
minimum: 0
ValidatedAssetVersion:
type: integer
format: uint64
format: int64
minimum: 0
Completed:
type: boolean
UploadedAssetID:
type: integer
format: uint64
format: int64
minimum: 0
StatusID:
type: integer
format: uint32
format: int32
minimum: 0
StatusMessage:
type: string
maxLength: 256
Submissions:
required:
- Total
@@ -1632,7 +1661,7 @@ components:
properties:
Total:
type: integer
format: uint64
format: int64
minimum: 0
Submissions:
type: array
@@ -1641,11 +1670,24 @@ components:
SubmissionTriggerCreate:
required:
- AssetID
- DisplayName
- Creator
- GameID
type: object
properties:
AssetID:
type: integer
format: uint64
format: int64
minimum: 0
DisplayName:
type: string
maxLength: 128
Creator:
type: string
maxLength: 128
GameID:
type: integer
format: int32
minimum: 0
ReleaseInfo:
required:
@@ -1655,7 +1697,7 @@ components:
properties:
SubmissionID:
type: integer
format: uint64
format: int64
minimum: 0
Date:
type: string
@@ -1672,7 +1714,7 @@ components:
properties:
ID:
type: integer
format: uint64
format: int64
minimum: 0
Name:
type: string
@@ -1686,11 +1728,11 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: uint32
format: int32
minimum: 0
ResourceID:
type: integer
format: uint64
format: int64
minimum: 0
ScriptCreate:
required:
@@ -1708,11 +1750,11 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: uint32
format: int32
minimum: 0
ResourceID:
type: integer
format: uint64
format: int64
minimum: 0
ScriptUpdate:
required:
@@ -1721,7 +1763,7 @@ components:
properties:
ID:
type: integer
format: uint64
format: int64
minimum: 0
Name:
type: string
@@ -1731,11 +1773,11 @@ components:
maxLength: 1048576
ResourceType:
type: integer
format: uint32
format: int32
minimum: 0
ResourceID:
type: integer
format: uint64
format: int64
minimum: 0
ScriptPolicy:
required:
@@ -1747,7 +1789,7 @@ components:
properties:
ID:
type: integer
format: uint64
format: int64
minimum: 0
FromScriptHash:
type: string
@@ -1755,11 +1797,11 @@ components:
maxLength: 16
ToScriptID:
type: integer
format: uint64
format: int64
minimum: 0
Policy:
type: integer
format: uint32
format: int32
minimum: 0
ScriptPolicyCreate:
required:
@@ -1770,15 +1812,15 @@ components:
properties:
FromScriptID:
type: integer
format: uint64
format: int64
minimum: 0
ToScriptID:
type: integer
format: uint64
format: int64
minimum: 0
Policy:
type: integer
format: uint32
format: int32
minimum: 0
ScriptPolicyUpdate:
required:
@@ -1787,19 +1829,19 @@ components:
properties:
ID:
type: integer
format: uint64
format: int64
minimum: 0
FromScriptID:
type: integer
format: uint64
format: int64
minimum: 0
ToScriptID:
type: integer
format: uint64
format: int64
minimum: 0
Policy:
type: integer
format: uint32
format: int32
minimum: 0
Error:
description: Represents error object
@@ -1807,7 +1849,7 @@ components:
properties:
code:
type: integer
format: uint64
format: int64
minimum: 0
message:
type: string

View File

@@ -181,6 +181,12 @@ type Invoker interface {
//
// POST /submissions
CreateSubmission(ctx context.Context, request *SubmissionTriggerCreate) (*OperationID, error)
// CreateSubmissionAdmin invokes createSubmissionAdmin operation.
//
// Trigger the validator to create a new submission.
//
// POST /submissions-admin
CreateSubmissionAdmin(ctx context.Context, request *SubmissionTriggerCreate) (*OperationID, error)
// CreateSubmissionAuditComment invokes createSubmissionAuditComment operation.
//
// Post a comment to the audit log.
@@ -444,7 +450,7 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -568,7 +574,7 @@ func (c *Client) sendActionMapfixReject(ctx context.Context, params ActionMapfix
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -692,7 +698,7 @@ func (c *Client) sendActionMapfixRequestChanges(ctx context.Context, params Acti
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -817,7 +823,7 @@ func (c *Client) sendActionMapfixResetSubmitting(ctx context.Context, params Act
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -941,7 +947,7 @@ func (c *Client) sendActionMapfixRetryValidate(ctx context.Context, params Actio
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -1065,7 +1071,7 @@ func (c *Client) sendActionMapfixRevoke(ctx context.Context, params ActionMapfix
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -1189,7 +1195,7 @@ func (c *Client) sendActionMapfixTriggerSubmit(ctx context.Context, params Actio
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -1313,7 +1319,7 @@ func (c *Client) sendActionMapfixTriggerUpload(ctx context.Context, params Actio
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -1437,7 +1443,7 @@ func (c *Client) sendActionMapfixTriggerValidate(ctx context.Context, params Act
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -1561,7 +1567,7 @@ func (c *Client) sendActionMapfixValidated(ctx context.Context, params ActionMap
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -1685,7 +1691,7 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -1809,7 +1815,7 @@ func (c *Client) sendActionSubmissionReject(ctx context.Context, params ActionSu
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -1933,7 +1939,7 @@ func (c *Client) sendActionSubmissionRequestChanges(ctx context.Context, params
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -2058,7 +2064,7 @@ func (c *Client) sendActionSubmissionResetSubmitting(ctx context.Context, params
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -2182,7 +2188,7 @@ func (c *Client) sendActionSubmissionRetryValidate(ctx context.Context, params A
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -2306,7 +2312,7 @@ func (c *Client) sendActionSubmissionRevoke(ctx context.Context, params ActionSu
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -2430,7 +2436,7 @@ func (c *Client) sendActionSubmissionTriggerSubmit(ctx context.Context, params A
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -2554,7 +2560,7 @@ func (c *Client) sendActionSubmissionTriggerUpload(ctx context.Context, params A
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -2678,7 +2684,7 @@ func (c *Client) sendActionSubmissionTriggerValidate(ctx context.Context, params
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -2802,7 +2808,7 @@ func (c *Client) sendActionSubmissionValidated(ctx context.Context, params Actio
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -3034,7 +3040,7 @@ func (c *Client) sendCreateMapfixAuditComment(ctx context.Context, request Creat
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -3429,6 +3435,114 @@ func (c *Client) sendCreateSubmission(ctx context.Context, request *SubmissionTr
return result, nil
}
// CreateSubmissionAdmin invokes createSubmissionAdmin operation.
//
// Trigger the validator to create a new submission.
//
// POST /submissions-admin
func (c *Client) CreateSubmissionAdmin(ctx context.Context, request *SubmissionTriggerCreate) (*OperationID, error) {
res, err := c.sendCreateSubmissionAdmin(ctx, request)
return res, err
}
func (c *Client) sendCreateSubmissionAdmin(ctx context.Context, request *SubmissionTriggerCreate) (res *OperationID, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createSubmissionAdmin"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions-admin"),
}
// 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, CreateSubmissionAdminOperation,
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] = "/submissions-admin"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
if err := encodeCreateSubmissionAdminRequest(request, r); err != nil {
return res, errors.Wrap(err, "encode request")
}
{
type bitset = [1]uint8
var satisfied bitset
{
stage = "Security:CookieAuth"
switch err := c.securityCookieAuth(ctx, CreateSubmissionAdminOperation, 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 := decodeCreateSubmissionAdminResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// CreateSubmissionAuditComment invokes createSubmissionAuditComment operation.
//
// Post a comment to the audit log.
@@ -3485,7 +3599,7 @@ func (c *Client) sendCreateSubmissionAuditComment(ctx context.Context, request C
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -3612,7 +3726,7 @@ func (c *Client) sendDeleteScript(ctx context.Context, params DeleteScriptParams
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.ScriptID))
return e.EncodeValue(conv.Int64ToString(params.ScriptID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -3735,7 +3849,7 @@ func (c *Client) sendDeleteScriptPolicy(ctx context.Context, params DeleteScript
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.ScriptPolicyID))
return e.EncodeValue(conv.Int64ToString(params.ScriptPolicyID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -3858,7 +3972,7 @@ func (c *Client) sendGetMap(ctx context.Context, params GetMapParams) (res *Map,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapID))
return e.EncodeValue(conv.Int64ToString(params.MapID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -3948,7 +4062,7 @@ func (c *Client) sendGetMapfix(ctx context.Context, params GetMapfixParams) (res
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -4038,7 +4152,7 @@ func (c *Client) sendGetOperation(ctx context.Context, params GetOperationParams
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint32ToString(params.OperationID))
return e.EncodeValue(conv.Int32ToString(params.OperationID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -4161,7 +4275,7 @@ func (c *Client) sendGetScript(ctx context.Context, params GetScriptParams) (res
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.ScriptID))
return e.EncodeValue(conv.Int64ToString(params.ScriptID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -4251,7 +4365,7 @@ func (c *Client) sendGetScriptPolicy(ctx context.Context, params GetScriptPolicy
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.ScriptPolicyID))
return e.EncodeValue(conv.Int64ToString(params.ScriptPolicyID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -4341,7 +4455,7 @@ func (c *Client) sendGetSubmission(ctx context.Context, params GetSubmissionPara
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -4431,7 +4545,7 @@ func (c *Client) sendListMapfixAuditEvents(ctx context.Context, params ListMapfi
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -4455,7 +4569,7 @@ func (c *Client) sendListMapfixAuditEvents(ctx context.Context, params ListMapfi
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Page))
return e.EncodeValue(conv.Int32ToString(params.Page))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -4469,7 +4583,7 @@ func (c *Client) sendListMapfixAuditEvents(ctx context.Context, params ListMapfi
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Limit))
return e.EncodeValue(conv.Int32ToString(params.Limit))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -4559,7 +4673,7 @@ func (c *Client) sendListMapfixes(ctx context.Context, params ListMapfixesParams
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Page))
return e.EncodeValue(conv.Int32ToString(params.Page))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -4573,7 +4687,7 @@ func (c *Client) sendListMapfixes(ctx context.Context, params ListMapfixesParams
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Limit))
return e.EncodeValue(conv.Int32ToString(params.Limit))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -4622,7 +4736,7 @@ func (c *Client) sendListMapfixes(ctx context.Context, params ListMapfixesParams
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.GameID.Get(); ok {
return e.EncodeValue(conv.Uint32ToString(val))
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
@@ -4639,7 +4753,7 @@ func (c *Client) sendListMapfixes(ctx context.Context, params ListMapfixesParams
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.Sort.Get(); ok {
return e.EncodeValue(conv.Uint32ToString(val))
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
@@ -4656,7 +4770,7 @@ func (c *Client) sendListMapfixes(ctx context.Context, params ListMapfixesParams
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.Submitter.Get(); ok {
return e.EncodeValue(conv.Uint64ToString(val))
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil {
@@ -4673,7 +4787,7 @@ func (c *Client) sendListMapfixes(ctx context.Context, params ListMapfixesParams
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.AssetID.Get(); ok {
return e.EncodeValue(conv.Uint64ToString(val))
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil {
@@ -4690,7 +4804,7 @@ func (c *Client) sendListMapfixes(ctx context.Context, params ListMapfixesParams
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.TargetAssetID.Get(); ok {
return e.EncodeValue(conv.Uint64ToString(val))
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil {
@@ -4707,7 +4821,7 @@ func (c *Client) sendListMapfixes(ctx context.Context, params ListMapfixesParams
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.StatusID.Get(); ok {
return e.EncodeValue(conv.Uint32ToString(val))
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
@@ -4799,7 +4913,7 @@ func (c *Client) sendListMaps(ctx context.Context, params ListMapsParams) (res [
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Page))
return e.EncodeValue(conv.Int32ToString(params.Page))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -4813,7 +4927,7 @@ func (c *Client) sendListMaps(ctx context.Context, params ListMapsParams) (res [
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Limit))
return e.EncodeValue(conv.Int32ToString(params.Limit))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -4862,7 +4976,7 @@ func (c *Client) sendListMaps(ctx context.Context, params ListMapsParams) (res [
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.GameID.Get(); ok {
return e.EncodeValue(conv.Uint32ToString(val))
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
@@ -4879,7 +4993,7 @@ func (c *Client) sendListMaps(ctx context.Context, params ListMapsParams) (res [
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.Sort.Get(); ok {
return e.EncodeValue(conv.Uint32ToString(val))
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
@@ -4971,7 +5085,7 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Page))
return e.EncodeValue(conv.Int32ToString(params.Page))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -4985,7 +5099,7 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Limit))
return e.EncodeValue(conv.Int32ToString(params.Limit))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -5017,7 +5131,7 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.ToScriptID.Get(); ok {
return e.EncodeValue(conv.Uint64ToString(val))
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil {
@@ -5034,7 +5148,7 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.Policy.Get(); ok {
return e.EncodeValue(conv.Uint32ToString(val))
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
@@ -5126,7 +5240,7 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Page))
return e.EncodeValue(conv.Int32ToString(params.Page))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -5140,7 +5254,7 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Limit))
return e.EncodeValue(conv.Int32ToString(params.Limit))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -5206,7 +5320,7 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.ResourceType.Get(); ok {
return e.EncodeValue(conv.Uint32ToString(val))
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
@@ -5223,7 +5337,7 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.ResourceID.Get(); ok {
return e.EncodeValue(conv.Uint64ToString(val))
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil {
@@ -5310,7 +5424,7 @@ func (c *Client) sendListSubmissionAuditEvents(ctx context.Context, params ListS
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -5334,7 +5448,7 @@ func (c *Client) sendListSubmissionAuditEvents(ctx context.Context, params ListS
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Page))
return e.EncodeValue(conv.Int32ToString(params.Page))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -5348,7 +5462,7 @@ func (c *Client) sendListSubmissionAuditEvents(ctx context.Context, params ListS
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Limit))
return e.EncodeValue(conv.Int32ToString(params.Limit))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -5438,7 +5552,7 @@ func (c *Client) sendListSubmissions(ctx context.Context, params ListSubmissions
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Page))
return e.EncodeValue(conv.Int32ToString(params.Page))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -5452,7 +5566,7 @@ func (c *Client) sendListSubmissions(ctx context.Context, params ListSubmissions
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Limit))
return e.EncodeValue(conv.Int32ToString(params.Limit))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -5501,7 +5615,7 @@ func (c *Client) sendListSubmissions(ctx context.Context, params ListSubmissions
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.GameID.Get(); ok {
return e.EncodeValue(conv.Uint32ToString(val))
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
@@ -5518,7 +5632,7 @@ func (c *Client) sendListSubmissions(ctx context.Context, params ListSubmissions
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.Sort.Get(); ok {
return e.EncodeValue(conv.Uint32ToString(val))
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
@@ -5535,7 +5649,7 @@ func (c *Client) sendListSubmissions(ctx context.Context, params ListSubmissions
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.Submitter.Get(); ok {
return e.EncodeValue(conv.Uint64ToString(val))
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil {
@@ -5552,7 +5666,7 @@ func (c *Client) sendListSubmissions(ctx context.Context, params ListSubmissions
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.AssetID.Get(); ok {
return e.EncodeValue(conv.Uint64ToString(val))
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil {
@@ -5569,7 +5683,7 @@ func (c *Client) sendListSubmissions(ctx context.Context, params ListSubmissions
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.UploadedAssetID.Get(); ok {
return e.EncodeValue(conv.Uint64ToString(val))
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil {
@@ -5586,7 +5700,7 @@ func (c *Client) sendListSubmissions(ctx context.Context, params ListSubmissions
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.StatusID.Get(); ok {
return e.EncodeValue(conv.Uint32ToString(val))
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
@@ -6096,7 +6210,7 @@ func (c *Client) sendSetMapfixCompleted(ctx context.Context, params SetMapfixCom
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -6220,7 +6334,7 @@ func (c *Client) sendSetSubmissionCompleted(ctx context.Context, params SetSubmi
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -6344,7 +6458,7 @@ func (c *Client) sendUpdateMapfixModel(ctx context.Context, params UpdateMapfixM
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -6368,7 +6482,7 @@ func (c *Client) sendUpdateMapfixModel(ctx context.Context, params UpdateMapfixM
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint64ToString(params.ModelID))
return e.EncodeValue(conv.Int64ToString(params.ModelID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -6382,7 +6496,7 @@ func (c *Client) sendUpdateMapfixModel(ctx context.Context, params UpdateMapfixM
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint64ToString(params.ModelVersion))
return e.EncodeValue(conv.Int64ToString(params.ModelVersion))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -6500,7 +6614,7 @@ func (c *Client) sendUpdateScript(ctx context.Context, request *ScriptUpdate, pa
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.ScriptID))
return e.EncodeValue(conv.Int64ToString(params.ScriptID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -6626,7 +6740,7 @@ func (c *Client) sendUpdateScriptPolicy(ctx context.Context, request *ScriptPoli
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.ScriptPolicyID))
return e.EncodeValue(conv.Int64ToString(params.ScriptPolicyID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -6752,7 +6866,7 @@ func (c *Client) sendUpdateSubmissionModel(ctx context.Context, params UpdateSub
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -6776,7 +6890,7 @@ func (c *Client) sendUpdateSubmissionModel(ctx context.Context, params UpdateSub
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint64ToString(params.ModelID))
return e.EncodeValue(conv.Int64ToString(params.ModelID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -6790,7 +6904,7 @@ func (c *Client) sendUpdateSubmissionModel(ctx context.Context, params UpdateSub
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint64ToString(params.ModelVersion))
return e.EncodeValue(conv.Int64ToString(params.ModelVersion))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}

View File

@@ -4922,6 +4922,201 @@ func (s *Server) handleCreateSubmissionRequest(args [0]string, argsEscaped bool,
}
}
// handleCreateSubmissionAdminRequest handles createSubmissionAdmin operation.
//
// Trigger the validator to create a new submission.
//
// POST /submissions-admin
func (s *Server) handleCreateSubmissionAdminRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createSubmissionAdmin"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions-admin"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), CreateSubmissionAdminOperation,
trace.WithAttributes(otelAttrs...),
serverSpanKind,
)
defer span.End()
// Add Labeler to context.
labeler := &Labeler{attrs: otelAttrs}
ctx = contextWithLabeler(ctx, labeler)
// Run stopwatch.
startTime := time.Now()
defer func() {
elapsedDuration := time.Since(startTime)
attrSet := labeler.AttributeSet()
attrs := attrSet.ToSlice()
code := statusWriter.status
if code != 0 {
codeAttr := semconv.HTTPResponseStatusCode(code)
attrs = append(attrs, codeAttr)
span.SetAttributes(codeAttr)
}
attrOpt := metric.WithAttributes(attrs...)
// Increment request counter.
s.requests.Add(ctx, 1, attrOpt)
// Use floating point division here for higher precision (instead of Millisecond method).
s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt)
}()
var (
recordError = func(stage string, err error) {
span.RecordError(err)
// https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status
// Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges,
// unless there was another error (e.g., network error receiving the response body; or 3xx codes with
// max redirects exceeded), in which case status MUST be set to Error.
code := statusWriter.status
if code >= 100 && code < 500 {
span.SetStatus(codes.Error, stage)
}
attrSet := labeler.AttributeSet()
attrs := attrSet.ToSlice()
if code != 0 {
attrs = append(attrs, semconv.HTTPResponseStatusCode(code))
}
s.errors.Add(ctx, 1, metric.WithAttributes(attrs...))
}
err error
opErrContext = ogenerrors.OperationContext{
Name: CreateSubmissionAdminOperation,
ID: "createSubmissionAdmin",
}
)
{
type bitset = [1]uint8
var satisfied bitset
{
sctx, ok, err := s.securityCookieAuth(ctx, CreateSubmissionAdminOperation, r)
if err != nil {
err = &ogenerrors.SecurityError{
OperationContext: opErrContext,
Security: "CookieAuth",
Err: err,
}
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
defer recordError("Security:CookieAuth", err)
}
return
}
if ok {
satisfied[0] |= 1 << 0
ctx = sctx
}
}
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 {
err = &ogenerrors.SecurityError{
OperationContext: opErrContext,
Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied,
}
if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil {
defer recordError("Security", err)
}
return
}
}
request, close, err := s.decodeCreateSubmissionAdminRequest(r)
if err != nil {
err = &ogenerrors.DecodeRequestError{
OperationContext: opErrContext,
Err: err,
}
defer recordError("DecodeRequest", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
defer func() {
if err := close(); err != nil {
recordError("CloseRequest", err)
}
}()
var response *OperationID
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: CreateSubmissionAdminOperation,
OperationSummary: "Trigger the validator to create a new submission",
OperationID: "createSubmissionAdmin",
Body: request,
Params: middleware.Parameters{},
Raw: r,
}
type (
Request = *SubmissionTriggerCreate
Params = struct{}
Response = *OperationID
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
nil,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
response, err = s.h.CreateSubmissionAdmin(ctx, request)
return response, err
},
)
} else {
response, err = s.h.CreateSubmissionAdmin(ctx, request)
}
if err != nil {
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {
if err := encodeErrorResponse(errRes, w, span); err != nil {
defer recordError("Internal", err)
}
return
}
if errors.Is(err, ht.ErrNotImplemented) {
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil {
defer recordError("Internal", err)
}
return
}
if err := encodeCreateSubmissionAdminResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleCreateSubmissionAuditCommentRequest handles createSubmissionAuditComment operation.
//
// Post a comment to the audit log.

File diff suppressed because it is too large Load Diff

View File

@@ -31,6 +31,7 @@ const (
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
CreateSubmissionAdminOperation OperationName = "CreateSubmissionAdmin"
CreateSubmissionAuditCommentOperation OperationName = "CreateSubmissionAuditComment"
DeleteScriptOperation OperationName = "DeleteScript"
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"

File diff suppressed because it is too large Load Diff

View File

@@ -334,6 +334,77 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
}
}
func (s *Server) decodeCreateSubmissionAdminRequest(r *http.Request) (
req *SubmissionTriggerCreate,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = multierr.Append(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
if r.ContentLength == 0 {
return req, close, validate.ErrBodyRequired
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return req, close, err
}
if len(buf) == 0 {
return req, close, validate.ErrBodyRequired
}
d := jx.DecodeBytes(buf)
var request SubmissionTriggerCreate
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) decodeCreateSubmissionAuditCommentRequest(r *http.Request) (
req CreateSubmissionAuditCommentReq,
close func() error,

View File

@@ -77,6 +77,20 @@ func encodeCreateSubmissionRequest(
return nil
}
func encodeCreateSubmissionAdminRequest(
req *SubmissionTriggerCreate,
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 encodeCreateSubmissionAuditCommentRequest(
req CreateSubmissionAuditCommentReq,
r *http.Request,

View File

@@ -1679,6 +1679,107 @@ func decodeCreateSubmissionResponse(resp *http.Response) (res *OperationID, _ er
return res, errors.Wrap(defRes, "error")
}
func decodeCreateSubmissionAdminResponse(resp *http.Response) (res *OperationID, _ error) {
switch resp.StatusCode {
case 201:
// Code 201.
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response OperationID
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
// Validate response.
if err := func() error {
if err := response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
}
return &response, nil
default:
return res, validate.InvalidContentType(ct)
}
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
// 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 decodeCreateSubmissionAuditCommentResponse(resp *http.Response) (res *CreateSubmissionAuditCommentNoContent, _ error) {
switch resp.StatusCode {
case 204:
@@ -2511,6 +2612,23 @@ func decodeListMapfixAuditEventsResponse(resp *http.Response) (res []AuditEvent,
if response == nil {
return errors.New("nil is invalid value")
}
var failures []validate.FieldError
for i, elem := range response {
if err := func() error {
if err := elem.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: fmt.Sprintf("[%d]", i),
Error: err,
})
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
@@ -3099,6 +3217,23 @@ func decodeListSubmissionAuditEventsResponse(resp *http.Response) (res []AuditEv
if response == nil {
return errors.New("nil is invalid value")
}
var failures []validate.FieldError
for i, elem := range response {
if err := func() error {
if err := elem.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: fmt.Sprintf("[%d]", i),
Error: err,
})
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")

View File

@@ -216,6 +216,20 @@ func encodeCreateSubmissionResponse(response *OperationID, w http.ResponseWriter
return nil
}
func encodeCreateSubmissionAdminResponse(response *OperationID, 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 encodeCreateSubmissionAuditCommentResponse(response *CreateSubmissionAuditCommentNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))

View File

@@ -870,6 +870,26 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
switch elem[0] {
case '-': // Prefix: "-admin"
if l := len("-admin"); len(elem) >= l && elem[0:l] == "-admin" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleCreateSubmissionAdminRequest([0]string{}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case '/': // Prefix: "/"
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
@@ -2315,6 +2335,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
}
switch elem[0] {
case '-': // Prefix: "-admin"
if l := len("-admin"); len(elem) >= l && elem[0:l] == "-admin" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = CreateSubmissionAdminOperation
r.summary = "Trigger the validator to create a new submission"
r.operationID = "createSubmissionAdmin"
r.pathPattern = "/submissions-admin"
r.args = args
r.count = 0
return r, true
default:
return
}
}
case '/': // Prefix: "/"
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {

File diff suppressed because it is too large Load Diff

View File

@@ -160,6 +160,12 @@ type Handler interface {
//
// POST /submissions
CreateSubmission(ctx context.Context, req *SubmissionTriggerCreate) (*OperationID, error)
// CreateSubmissionAdmin implements createSubmissionAdmin operation.
//
// Trigger the validator to create a new submission.
//
// POST /submissions-admin
CreateSubmissionAdmin(ctx context.Context, req *SubmissionTriggerCreate) (*OperationID, error)
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
//
// Post a comment to the audit log.

View File

@@ -240,6 +240,15 @@ func (UnimplementedHandler) CreateSubmission(ctx context.Context, req *Submissio
return r, ht.ErrNotImplemented
}
// CreateSubmissionAdmin implements createSubmissionAdmin operation.
//
// Trigger the validator to create a new submission.
//
// POST /submissions-admin
func (UnimplementedHandler) CreateSubmissionAdmin(ctx context.Context, req *SubmissionTriggerCreate) (r *OperationID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
//
// Post a comment to the audit log.

View File

@@ -10,6 +10,37 @@ import (
"github.com/ogen-go/ogen/validate"
)
func (s *AuditEvent) 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: 64,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Username)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Username",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *Error) Validate() error {
if s == nil {
return validate.ErrNilPointer
@@ -408,13 +439,13 @@ func (s *Mapfix) Validate() error {
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.StatusMessage)); err != nil {
}).Validate(string(s.Description)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "StatusMessage",
Name: "Description",
Error: err,
})
}
@@ -470,6 +501,25 @@ func (s *MapfixTriggerCreate) Validate() error {
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 256,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.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}
}
@@ -1751,25 +1801,6 @@ func (s *Submission) Validate() error {
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 256,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.StatusMessage)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "StatusMessage",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
@@ -1802,6 +1833,64 @@ func (s *SubmissionTriggerCreate) Validate() error {
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 len(failures) > 0 {
return &validate.Error{Fields: failures}
}

View File

@@ -6,6 +6,7 @@ import (
"git.itzana.me/strafesnet/go-grpc/auth"
"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/gormstore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
@@ -125,7 +126,8 @@ func serve(ctx *cli.Context) error {
svc := &service.Service{
DB: db,
Nats: js,
Client: maps.NewMapsServiceClient(conn),
Maps: maps.NewMapsServiceClient(conn),
Users: users.NewUsersServiceClient(conn),
}
conn, err = grpc.Dial(ctx.String("auth-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials()))

View File

@@ -49,7 +49,7 @@ type Mapfixes interface {
IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.MapfixStatus, values OptionalMap) (model.Mapfix, error)
Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) ([]model.Mapfix, error)
ListWithTotal(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) (uint64, []model.Mapfix, error)
ListWithTotal(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) (int64, []model.Mapfix, error)
}
type Operations interface {

View File

@@ -10,17 +10,7 @@ func Optional() OptionalMap {
return OptionalMap{filter: map[string]interface{}{}}
}
func (q OptionalMap) Add2(column string, value interface{}) OptionalMap {
q.filter[column] = value
return q
}
func (q OptionalMap) AddPostgresInt32(column string, value uint32) OptionalMap {
q.filter[column] = value
return q
}
func (q OptionalMap) AddPostgresInt64(column string, value uint64) OptionalMap {
func (q OptionalMap) Add(column string, value interface{}) OptionalMap {
q.filter[column] = value
return q
}

View File

@@ -131,7 +131,7 @@ func (env *Mapfixes) List(ctx context.Context, filters datastore.OptionalMap, pa
return maps, nil
}
func (env *Mapfixes) ListWithTotal(ctx context.Context, filters datastore.OptionalMap, page model.Page, sort datastore.ListSort) (uint64, []model.Mapfix, error) {
func (env *Mapfixes) ListWithTotal(ctx context.Context, filters datastore.OptionalMap, page model.Page, sort datastore.ListSort) (int64, []model.Mapfix, error) {
// grab page items
maps, err := env.List(ctx, filters, page, sort)
if err != nil{
@@ -144,5 +144,5 @@ func (env *Mapfixes) ListWithTotal(ctx context.Context, filters datastore.Option
return 0, nil, err
}
return uint64(total), maps, nil
return total, maps, nil
}

View File

@@ -34,6 +34,12 @@ type Invoker interface {
//
// POST /mapfixes/{MapfixID}/status/validator-failed
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixRequestChanges invokes 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 invokes actionMapfixSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
@@ -64,6 +70,12 @@ type Invoker interface {
//
// POST /submissions/{SubmissionID}/status/validator-failed
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionRequestChanges invokes 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 invokes actionSubmissionSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
@@ -241,7 +253,7 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -257,15 +269,15 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "StatusMessage" parameter.
// Encode "ErrorMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "StatusMessage",
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.StatusMessage))
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -294,6 +306,115 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf
return result, nil
}
// ActionMapfixRequestChanges invokes actionMapfixRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
//
// POST /mapfixes/{MapfixID}/status/validator-request-changes
func (c *Client) ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error {
_, err := c.sendActionMapfixRequestChanges(ctx, params)
return err
}
func (c *Client) sendActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) (res *ActionMapfixRequestChangesNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixRequestChanges"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-request-changes"),
}
// 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, ActionMapfixRequestChangesOperation,
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/validator-request-changes"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "ErrorMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeActionMapfixRequestChangesResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
@@ -350,7 +471,7 @@ func (c *Client) sendActionMapfixSubmitted(ctx context.Context, params ActionMap
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -363,6 +484,66 @@ func (c *Client) sendActionMapfixSubmitted(ctx context.Context, params ActionMap
pathParts[2] = "/status/validator-submitted"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "ModelVersion" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ModelVersion",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int64ToString(params.ModelVersion))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
{
// Encode "DisplayName" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "DisplayName",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.DisplayName))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
{
// Encode "Creator" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "Creator",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.Creator))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
{
// Encode "GameID" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "GameID",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int32ToString(params.GameID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
@@ -441,7 +622,7 @@ func (c *Client) sendActionMapfixUploaded(ctx context.Context, params ActionMapf
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -532,7 +713,7 @@ func (c *Client) sendActionMapfixValidated(ctx context.Context, params ActionMap
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -623,7 +804,7 @@ func (c *Client) sendActionOperationFailed(ctx context.Context, params ActionOpe
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint32ToString(params.OperationID))
return e.EncodeValue(conv.Int32ToString(params.OperationID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -732,7 +913,7 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -748,15 +929,15 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "StatusMessage" parameter.
// Encode "ErrorMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "StatusMessage",
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.StatusMessage))
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -785,6 +966,115 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action
return result, nil
}
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
//
// POST /submissions/{SubmissionID}/status/validator-request-changes
func (c *Client) ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error {
_, err := c.sendActionSubmissionRequestChanges(ctx, params)
return err
}
func (c *Client) sendActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) (res *ActionSubmissionRequestChangesNoContent, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionSubmissionRequestChanges"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validator-request-changes"),
}
// 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, ActionSubmissionRequestChangesOperation,
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/validator-request-changes"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "ErrorMessage" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ErrorMessage",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.ErrorMessage))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
return res, errors.Wrap(err, "create request")
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeActionSubmissionRequestChangesResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
@@ -841,7 +1131,7 @@ func (c *Client) sendActionSubmissionSubmitted(ctx context.Context, params Actio
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -854,6 +1144,66 @@ func (c *Client) sendActionSubmissionSubmitted(ctx context.Context, params Actio
pathParts[2] = "/status/validator-submitted"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeQueryParams"
q := uri.NewQueryEncoder()
{
// Encode "ModelVersion" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "ModelVersion",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int64ToString(params.ModelVersion))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
{
// Encode "DisplayName" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "DisplayName",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.DisplayName))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
{
// Encode "Creator" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "Creator",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.StringToString(params.Creator))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
{
// Encode "GameID" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "GameID",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Int32ToString(params.GameID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
u.RawQuery = q.Values().Encode()
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u)
if err != nil {
@@ -932,7 +1282,7 @@ func (c *Client) sendActionSubmissionUploaded(ctx context.Context, params Action
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -956,7 +1306,7 @@ func (c *Client) sendActionSubmissionUploaded(ctx context.Context, params Action
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint64ToString(params.UploadedAssetID))
return e.EncodeValue(conv.Int64ToString(params.UploadedAssetID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -1041,7 +1391,7 @@ func (c *Client) sendActionSubmissionValidated(ctx context.Context, params Actio
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -1432,7 +1782,7 @@ func (c *Client) sendGetScript(ctx context.Context, params GetScriptParams) (res
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.ScriptID))
return e.EncodeValue(conv.Int64ToString(params.ScriptID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -1527,7 +1877,7 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Page))
return e.EncodeValue(conv.Int32ToString(params.Page))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -1541,7 +1891,7 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Limit))
return e.EncodeValue(conv.Int32ToString(params.Limit))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -1573,7 +1923,7 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.ToScriptID.Get(); ok {
return e.EncodeValue(conv.Uint64ToString(val))
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil {
@@ -1590,7 +1940,7 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.Policy.Get(); ok {
return e.EncodeValue(conv.Uint32ToString(val))
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
@@ -1682,7 +2032,7 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Page))
return e.EncodeValue(conv.Int32ToString(params.Page))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -1696,7 +2046,7 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint32ToString(params.Limit))
return e.EncodeValue(conv.Int32ToString(params.Limit))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -1762,7 +2112,7 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.ResourceType.Get(); ok {
return e.EncodeValue(conv.Uint32ToString(val))
return e.EncodeValue(conv.Int32ToString(val))
}
return nil
}); err != nil {
@@ -1779,7 +2129,7 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams)
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.ResourceID.Get(); ok {
return e.EncodeValue(conv.Uint64ToString(val))
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil {
@@ -1866,7 +2216,7 @@ func (c *Client) sendUpdateMapfixValidatedModel(ctx context.Context, params Upda
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.MapfixID))
return e.EncodeValue(conv.Int64ToString(params.MapfixID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -1890,7 +2240,7 @@ func (c *Client) sendUpdateMapfixValidatedModel(ctx context.Context, params Upda
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint64ToString(params.ValidatedModelID))
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -1904,7 +2254,7 @@ func (c *Client) sendUpdateMapfixValidatedModel(ctx context.Context, params Upda
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint64ToString(params.ValidatedModelVersion))
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelVersion))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -1989,7 +2339,7 @@ func (c *Client) sendUpdateSubmissionValidatedModel(ctx context.Context, params
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.Uint64ToString(params.SubmissionID))
return e.EncodeValue(conv.Int64ToString(params.SubmissionID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
@@ -2013,7 +2363,7 @@ func (c *Client) sendUpdateSubmissionValidatedModel(ctx context.Context, params
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint64ToString(params.ValidatedModelID))
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelID))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
@@ -2027,7 +2377,7 @@ func (c *Client) sendUpdateSubmissionValidatedModel(ctx context.Context, params
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
return e.EncodeValue(conv.Uint64ToString(params.ValidatedModelVersion))
return e.EncodeValue(conv.Int64ToString(params.ValidatedModelVersion))
}); err != nil {
return res, errors.Wrap(err, "encode query")
}

View File

@@ -129,9 +129,9 @@ func (s *Server) handleActionMapfixAcceptedRequest(args [1]string, argsEscaped b
In: "path",
}: params.MapfixID,
{
Name: "StatusMessage",
Name: "ErrorMessage",
In: "query",
}: params.StatusMessage,
}: params.ErrorMessage,
},
Raw: r,
}
@@ -183,6 +183,159 @@ func (s *Server) handleActionMapfixAcceptedRequest(args [1]string, argsEscaped b
}
}
// handleActionMapfixRequestChangesRequest handles actionMapfixRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
//
// POST /mapfixes/{MapfixID}/status/validator-request-changes
func (s *Server) handleActionMapfixRequestChangesRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionMapfixRequestChanges"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/validator-request-changes"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixRequestChangesOperation,
trace.WithAttributes(otelAttrs...),
serverSpanKind,
)
defer span.End()
// Add Labeler to context.
labeler := &Labeler{attrs: otelAttrs}
ctx = contextWithLabeler(ctx, labeler)
// Run stopwatch.
startTime := time.Now()
defer func() {
elapsedDuration := time.Since(startTime)
attrSet := labeler.AttributeSet()
attrs := attrSet.ToSlice()
code := statusWriter.status
if code != 0 {
codeAttr := semconv.HTTPResponseStatusCode(code)
attrs = append(attrs, codeAttr)
span.SetAttributes(codeAttr)
}
attrOpt := metric.WithAttributes(attrs...)
// Increment request counter.
s.requests.Add(ctx, 1, attrOpt)
// Use floating point division here for higher precision (instead of Millisecond method).
s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt)
}()
var (
recordError = func(stage string, err error) {
span.RecordError(err)
// https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status
// Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges,
// unless there was another error (e.g., network error receiving the response body; or 3xx codes with
// max redirects exceeded), in which case status MUST be set to Error.
code := statusWriter.status
if code >= 100 && code < 500 {
span.SetStatus(codes.Error, stage)
}
attrSet := labeler.AttributeSet()
attrs := attrSet.ToSlice()
if code != 0 {
attrs = append(attrs, semconv.HTTPResponseStatusCode(code))
}
s.errors.Add(ctx, 1, metric.WithAttributes(attrs...))
}
err error
opErrContext = ogenerrors.OperationContext{
Name: ActionMapfixRequestChangesOperation,
ID: "actionMapfixRequestChanges",
}
)
params, err := decodeActionMapfixRequestChangesParams(args, argsEscaped, r)
if err != nil {
err = &ogenerrors.DecodeParamsError{
OperationContext: opErrContext,
Err: err,
}
defer recordError("DecodeParams", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
var response *ActionMapfixRequestChangesNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: ActionMapfixRequestChangesOperation,
OperationSummary: "(Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested",
OperationID: "actionMapfixRequestChanges",
Body: nil,
Params: middleware.Parameters{
{
Name: "MapfixID",
In: "path",
}: params.MapfixID,
{
Name: "ErrorMessage",
In: "query",
}: params.ErrorMessage,
},
Raw: r,
}
type (
Request = struct{}
Params = ActionMapfixRequestChangesParams
Response = *ActionMapfixRequestChangesNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackActionMapfixRequestChangesParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.ActionMapfixRequestChanges(ctx, params)
return response, err
},
)
} else {
err = s.h.ActionMapfixRequestChanges(ctx, params)
}
if err != nil {
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {
if err := encodeErrorResponse(errRes, w, span); err != nil {
defer recordError("Internal", err)
}
return
}
if errors.Is(err, ht.ErrNotImplemented) {
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil {
defer recordError("Internal", err)
}
return
}
if err := encodeActionMapfixRequestChangesResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleActionMapfixSubmittedRequest handles actionMapfixSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
@@ -281,6 +434,22 @@ func (s *Server) handleActionMapfixSubmittedRequest(args [1]string, argsEscaped
Name: "MapfixID",
In: "path",
}: params.MapfixID,
{
Name: "ModelVersion",
In: "query",
}: params.ModelVersion,
{
Name: "DisplayName",
In: "query",
}: params.DisplayName,
{
Name: "Creator",
In: "query",
}: params.Creator,
{
Name: "GameID",
In: "query",
}: params.GameID,
},
Raw: r,
}
@@ -882,9 +1051,9 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
In: "path",
}: params.SubmissionID,
{
Name: "StatusMessage",
Name: "ErrorMessage",
In: "query",
}: params.StatusMessage,
}: params.ErrorMessage,
},
Raw: r,
}
@@ -936,6 +1105,159 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap
}
}
// handleActionSubmissionRequestChangesRequest handles actionSubmissionRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
//
// POST /submissions/{SubmissionID}/status/validator-request-changes
func (s *Server) handleActionSubmissionRequestChangesRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
statusWriter := &codeRecorder{ResponseWriter: w}
w = statusWriter
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("actionSubmissionRequestChanges"),
semconv.HTTPRequestMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/validator-request-changes"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionRequestChangesOperation,
trace.WithAttributes(otelAttrs...),
serverSpanKind,
)
defer span.End()
// Add Labeler to context.
labeler := &Labeler{attrs: otelAttrs}
ctx = contextWithLabeler(ctx, labeler)
// Run stopwatch.
startTime := time.Now()
defer func() {
elapsedDuration := time.Since(startTime)
attrSet := labeler.AttributeSet()
attrs := attrSet.ToSlice()
code := statusWriter.status
if code != 0 {
codeAttr := semconv.HTTPResponseStatusCode(code)
attrs = append(attrs, codeAttr)
span.SetAttributes(codeAttr)
}
attrOpt := metric.WithAttributes(attrs...)
// Increment request counter.
s.requests.Add(ctx, 1, attrOpt)
// Use floating point division here for higher precision (instead of Millisecond method).
s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt)
}()
var (
recordError = func(stage string, err error) {
span.RecordError(err)
// https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status
// Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges,
// unless there was another error (e.g., network error receiving the response body; or 3xx codes with
// max redirects exceeded), in which case status MUST be set to Error.
code := statusWriter.status
if code >= 100 && code < 500 {
span.SetStatus(codes.Error, stage)
}
attrSet := labeler.AttributeSet()
attrs := attrSet.ToSlice()
if code != 0 {
attrs = append(attrs, semconv.HTTPResponseStatusCode(code))
}
s.errors.Add(ctx, 1, metric.WithAttributes(attrs...))
}
err error
opErrContext = ogenerrors.OperationContext{
Name: ActionSubmissionRequestChangesOperation,
ID: "actionSubmissionRequestChanges",
}
)
params, err := decodeActionSubmissionRequestChangesParams(args, argsEscaped, r)
if err != nil {
err = &ogenerrors.DecodeParamsError{
OperationContext: opErrContext,
Err: err,
}
defer recordError("DecodeParams", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
var response *ActionSubmissionRequestChangesNoContent
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: ActionSubmissionRequestChangesOperation,
OperationSummary: "(Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested",
OperationID: "actionSubmissionRequestChanges",
Body: nil,
Params: middleware.Parameters{
{
Name: "SubmissionID",
In: "path",
}: params.SubmissionID,
{
Name: "ErrorMessage",
In: "query",
}: params.ErrorMessage,
},
Raw: r,
}
type (
Request = struct{}
Params = ActionSubmissionRequestChangesParams
Response = *ActionSubmissionRequestChangesNoContent
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackActionSubmissionRequestChangesParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
err = s.h.ActionSubmissionRequestChanges(ctx, params)
return response, err
},
)
} else {
err = s.h.ActionSubmissionRequestChanges(ctx, params)
}
if err != nil {
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {
if err := encodeErrorResponse(errRes, w, span); err != nil {
defer recordError("Internal", err)
}
return
}
if errors.Is(err, ht.ErrNotImplemented) {
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil {
defer recordError("Internal", err)
}
return
}
if err := encodeActionSubmissionRequestChangesResponse(response, w, span); err != nil {
defer recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
}
return
}
}
// handleActionSubmissionSubmittedRequest handles actionSubmissionSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
@@ -1034,6 +1356,22 @@ func (s *Server) handleActionSubmissionSubmittedRequest(args [1]string, argsEsca
Name: "SubmissionID",
In: "path",
}: params.SubmissionID,
{
Name: "ModelVersion",
In: "query",
}: params.ModelVersion,
{
Name: "DisplayName",
In: "query",
}: params.DisplayName,
{
Name: "Creator",
In: "query",
}: params.Creator,
{
Name: "GameID",
In: "query",
}: params.GameID,
},
Raw: r,
}

View File

@@ -23,7 +23,7 @@ func (s *Error) Encode(e *jx.Encoder) {
func (s *Error) encodeFields(e *jx.Encoder) {
{
e.FieldStart("code")
e.UInt64(s.Code)
e.Int64(s.Code)
}
{
e.FieldStart("message")
@@ -48,8 +48,8 @@ func (s *Error) Decode(d *jx.Decoder) error {
case "code":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.UInt64()
s.Code = uint64(v)
v, err := d.Int64()
s.Code = int64(v)
if err != nil {
return err
}
@@ -136,11 +136,11 @@ func (s *MapfixCreate) Encode(e *jx.Encoder) {
func (s *MapfixCreate) encodeFields(e *jx.Encoder) {
{
e.FieldStart("OperationID")
e.UInt32(s.OperationID)
e.Int32(s.OperationID)
}
{
e.FieldStart("AssetOwner")
e.UInt64(s.AssetOwner)
e.Int64(s.AssetOwner)
}
{
e.FieldStart("DisplayName")
@@ -152,23 +152,27 @@ func (s *MapfixCreate) encodeFields(e *jx.Encoder) {
}
{
e.FieldStart("GameID")
e.UInt32(s.GameID)
e.Int32(s.GameID)
}
{
e.FieldStart("AssetID")
e.UInt64(s.AssetID)
e.Int64(s.AssetID)
}
{
e.FieldStart("AssetVersion")
e.UInt64(s.AssetVersion)
e.Int64(s.AssetVersion)
}
{
e.FieldStart("TargetAssetID")
e.UInt64(s.TargetAssetID)
e.Int64(s.TargetAssetID)
}
{
e.FieldStart("Description")
e.Str(s.Description)
}
}
var jsonFieldsNameOfMapfixCreate = [8]string{
var jsonFieldsNameOfMapfixCreate = [9]string{
0: "OperationID",
1: "AssetOwner",
2: "DisplayName",
@@ -177,6 +181,7 @@ var jsonFieldsNameOfMapfixCreate = [8]string{
5: "AssetID",
6: "AssetVersion",
7: "TargetAssetID",
8: "Description",
}
// Decode decodes MapfixCreate from json.
@@ -184,15 +189,15 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode MapfixCreate to nil")
}
var requiredBitSet [1]uint8
var requiredBitSet [2]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "OperationID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.UInt32()
s.OperationID = uint32(v)
v, err := d.Int32()
s.OperationID = int32(v)
if err != nil {
return err
}
@@ -203,8 +208,8 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
case "AssetOwner":
requiredBitSet[0] |= 1 << 1
if err := func() error {
v, err := d.UInt64()
s.AssetOwner = uint64(v)
v, err := d.Int64()
s.AssetOwner = int64(v)
if err != nil {
return err
}
@@ -239,8 +244,8 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
case "GameID":
requiredBitSet[0] |= 1 << 4
if err := func() error {
v, err := d.UInt32()
s.GameID = uint32(v)
v, err := d.Int32()
s.GameID = int32(v)
if err != nil {
return err
}
@@ -251,8 +256,8 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
case "AssetID":
requiredBitSet[0] |= 1 << 5
if err := func() error {
v, err := d.UInt64()
s.AssetID = uint64(v)
v, err := d.Int64()
s.AssetID = int64(v)
if err != nil {
return err
}
@@ -263,8 +268,8 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
case "AssetVersion":
requiredBitSet[0] |= 1 << 6
if err := func() error {
v, err := d.UInt64()
s.AssetVersion = uint64(v)
v, err := d.Int64()
s.AssetVersion = int64(v)
if err != nil {
return err
}
@@ -275,8 +280,8 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
case "TargetAssetID":
requiredBitSet[0] |= 1 << 7
if err := func() error {
v, err := d.UInt64()
s.TargetAssetID = uint64(v)
v, err := d.Int64()
s.TargetAssetID = int64(v)
if err != nil {
return err
}
@@ -284,6 +289,18 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
}(); err != nil {
return errors.Wrap(err, "decode field \"TargetAssetID\"")
}
case "Description":
requiredBitSet[1] |= 1 << 0
if err := func() error {
v, err := d.Str()
s.Description = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Description\"")
}
default:
return d.Skip()
}
@@ -293,8 +310,9 @@ func (s *MapfixCreate) Decode(d *jx.Decoder) error {
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
for i, mask := range [2]uint8{
0b11111111,
0b00000001,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
@@ -351,7 +369,7 @@ func (s *MapfixID) Encode(e *jx.Encoder) {
func (s *MapfixID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("MapfixID")
e.UInt64(s.MapfixID)
e.Int64(s.MapfixID)
}
}
@@ -371,8 +389,8 @@ func (s *MapfixID) Decode(d *jx.Decoder) error {
case "MapfixID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.UInt64()
s.MapfixID = uint64(v)
v, err := d.Int64()
s.MapfixID = int64(v)
if err != nil {
return err
}
@@ -436,37 +454,37 @@ func (s *MapfixID) UnmarshalJSON(data []byte) error {
return s.Decode(d)
}
// Encode encodes uint64 as json.
func (o OptUint64) Encode(e *jx.Encoder) {
// Encode encodes int64 as json.
func (o OptInt64) Encode(e *jx.Encoder) {
if !o.Set {
return
}
e.UInt64(uint64(o.Value))
e.Int64(int64(o.Value))
}
// Decode decodes uint64 from json.
func (o *OptUint64) Decode(d *jx.Decoder) error {
// Decode decodes int64 from json.
func (o *OptInt64) Decode(d *jx.Decoder) error {
if o == nil {
return errors.New("invalid: unable to decode OptUint64 to nil")
return errors.New("invalid: unable to decode OptInt64 to nil")
}
o.Set = true
v, err := d.UInt64()
v, err := d.Int64()
if err != nil {
return err
}
o.Value = uint64(v)
o.Value = int64(v)
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s OptUint64) MarshalJSON() ([]byte, error) {
func (s OptInt64) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *OptUint64) UnmarshalJSON(data []byte) error {
func (s *OptInt64) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
@@ -482,7 +500,7 @@ func (s *Script) Encode(e *jx.Encoder) {
func (s *Script) encodeFields(e *jx.Encoder) {
{
e.FieldStart("ID")
e.UInt64(s.ID)
e.Int64(s.ID)
}
{
e.FieldStart("Name")
@@ -498,11 +516,11 @@ func (s *Script) encodeFields(e *jx.Encoder) {
}
{
e.FieldStart("ResourceType")
e.UInt32(s.ResourceType)
e.Int32(s.ResourceType)
}
{
e.FieldStart("ResourceID")
e.UInt64(s.ResourceID)
e.Int64(s.ResourceID)
}
}
@@ -527,8 +545,8 @@ func (s *Script) Decode(d *jx.Decoder) error {
case "ID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.UInt64()
s.ID = uint64(v)
v, err := d.Int64()
s.ID = int64(v)
if err != nil {
return err
}
@@ -575,8 +593,8 @@ func (s *Script) Decode(d *jx.Decoder) error {
case "ResourceType":
requiredBitSet[0] |= 1 << 4
if err := func() error {
v, err := d.UInt32()
s.ResourceType = uint32(v)
v, err := d.Int32()
s.ResourceType = int32(v)
if err != nil {
return err
}
@@ -587,8 +605,8 @@ func (s *Script) Decode(d *jx.Decoder) error {
case "ResourceID":
requiredBitSet[0] |= 1 << 5
if err := func() error {
v, err := d.UInt64()
s.ResourceID = uint64(v)
v, err := d.Int64()
s.ResourceID = int64(v)
if err != nil {
return err
}
@@ -671,7 +689,7 @@ func (s *ScriptCreate) encodeFields(e *jx.Encoder) {
}
{
e.FieldStart("ResourceType")
e.UInt32(s.ResourceType)
e.Int32(s.ResourceType)
}
{
if s.ResourceID.Set {
@@ -724,8 +742,8 @@ func (s *ScriptCreate) Decode(d *jx.Decoder) error {
case "ResourceType":
requiredBitSet[0] |= 1 << 2
if err := func() error {
v, err := d.UInt32()
s.ResourceType = uint32(v)
v, err := d.Int32()
s.ResourceType = int32(v)
if err != nil {
return err
}
@@ -810,7 +828,7 @@ func (s *ScriptID) Encode(e *jx.Encoder) {
func (s *ScriptID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("ScriptID")
e.UInt64(s.ScriptID)
e.Int64(s.ScriptID)
}
}
@@ -830,8 +848,8 @@ func (s *ScriptID) Decode(d *jx.Decoder) error {
case "ScriptID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.UInt64()
s.ScriptID = uint64(v)
v, err := d.Int64()
s.ScriptID = int64(v)
if err != nil {
return err
}
@@ -906,7 +924,7 @@ func (s *ScriptPolicy) Encode(e *jx.Encoder) {
func (s *ScriptPolicy) encodeFields(e *jx.Encoder) {
{
e.FieldStart("ID")
e.UInt64(s.ID)
e.Int64(s.ID)
}
{
e.FieldStart("FromScriptHash")
@@ -914,11 +932,11 @@ func (s *ScriptPolicy) encodeFields(e *jx.Encoder) {
}
{
e.FieldStart("ToScriptID")
e.UInt64(s.ToScriptID)
e.Int64(s.ToScriptID)
}
{
e.FieldStart("Policy")
e.UInt32(s.Policy)
e.Int32(s.Policy)
}
}
@@ -941,8 +959,8 @@ func (s *ScriptPolicy) Decode(d *jx.Decoder) error {
case "ID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.UInt64()
s.ID = uint64(v)
v, err := d.Int64()
s.ID = int64(v)
if err != nil {
return err
}
@@ -965,8 +983,8 @@ func (s *ScriptPolicy) Decode(d *jx.Decoder) error {
case "ToScriptID":
requiredBitSet[0] |= 1 << 2
if err := func() error {
v, err := d.UInt64()
s.ToScriptID = uint64(v)
v, err := d.Int64()
s.ToScriptID = int64(v)
if err != nil {
return err
}
@@ -977,8 +995,8 @@ func (s *ScriptPolicy) Decode(d *jx.Decoder) error {
case "Policy":
requiredBitSet[0] |= 1 << 3
if err := func() error {
v, err := d.UInt32()
s.Policy = uint32(v)
v, err := d.Int32()
s.Policy = int32(v)
if err != nil {
return err
}
@@ -1053,15 +1071,15 @@ func (s *ScriptPolicyCreate) Encode(e *jx.Encoder) {
func (s *ScriptPolicyCreate) encodeFields(e *jx.Encoder) {
{
e.FieldStart("FromScriptID")
e.UInt64(s.FromScriptID)
e.Int64(s.FromScriptID)
}
{
e.FieldStart("ToScriptID")
e.UInt64(s.ToScriptID)
e.Int64(s.ToScriptID)
}
{
e.FieldStart("Policy")
e.UInt32(s.Policy)
e.Int32(s.Policy)
}
}
@@ -1083,8 +1101,8 @@ func (s *ScriptPolicyCreate) Decode(d *jx.Decoder) error {
case "FromScriptID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.UInt64()
s.FromScriptID = uint64(v)
v, err := d.Int64()
s.FromScriptID = int64(v)
if err != nil {
return err
}
@@ -1095,8 +1113,8 @@ func (s *ScriptPolicyCreate) Decode(d *jx.Decoder) error {
case "ToScriptID":
requiredBitSet[0] |= 1 << 1
if err := func() error {
v, err := d.UInt64()
s.ToScriptID = uint64(v)
v, err := d.Int64()
s.ToScriptID = int64(v)
if err != nil {
return err
}
@@ -1107,8 +1125,8 @@ func (s *ScriptPolicyCreate) Decode(d *jx.Decoder) error {
case "Policy":
requiredBitSet[0] |= 1 << 2
if err := func() error {
v, err := d.UInt32()
s.Policy = uint32(v)
v, err := d.Int32()
s.Policy = int32(v)
if err != nil {
return err
}
@@ -1183,7 +1201,7 @@ func (s *ScriptPolicyID) Encode(e *jx.Encoder) {
func (s *ScriptPolicyID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("ScriptPolicyID")
e.UInt64(s.ScriptPolicyID)
e.Int64(s.ScriptPolicyID)
}
}
@@ -1203,8 +1221,8 @@ func (s *ScriptPolicyID) Decode(d *jx.Decoder) error {
case "ScriptPolicyID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.UInt64()
s.ScriptPolicyID = uint64(v)
v, err := d.Int64()
s.ScriptPolicyID = int64(v)
if err != nil {
return err
}
@@ -1279,11 +1297,11 @@ func (s *SubmissionCreate) Encode(e *jx.Encoder) {
func (s *SubmissionCreate) encodeFields(e *jx.Encoder) {
{
e.FieldStart("OperationID")
e.UInt32(s.OperationID)
e.Int32(s.OperationID)
}
{
e.FieldStart("AssetOwner")
e.UInt64(s.AssetOwner)
e.Int64(s.AssetOwner)
}
{
e.FieldStart("DisplayName")
@@ -1295,19 +1313,27 @@ func (s *SubmissionCreate) encodeFields(e *jx.Encoder) {
}
{
e.FieldStart("GameID")
e.UInt32(s.GameID)
e.Int32(s.GameID)
}
{
e.FieldStart("AssetID")
e.UInt64(s.AssetID)
e.Int64(s.AssetID)
}
{
e.FieldStart("AssetVersion")
e.UInt64(s.AssetVersion)
e.Int64(s.AssetVersion)
}
{
e.FieldStart("Status")
e.UInt32(s.Status)
}
{
e.FieldStart("Roles")
e.UInt32(s.Roles)
}
}
var jsonFieldsNameOfSubmissionCreate = [7]string{
var jsonFieldsNameOfSubmissionCreate = [9]string{
0: "OperationID",
1: "AssetOwner",
2: "DisplayName",
@@ -1315,6 +1341,8 @@ var jsonFieldsNameOfSubmissionCreate = [7]string{
4: "GameID",
5: "AssetID",
6: "AssetVersion",
7: "Status",
8: "Roles",
}
// Decode decodes SubmissionCreate from json.
@@ -1322,15 +1350,15 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode SubmissionCreate to nil")
}
var requiredBitSet [1]uint8
var requiredBitSet [2]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "OperationID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.UInt32()
s.OperationID = uint32(v)
v, err := d.Int32()
s.OperationID = int32(v)
if err != nil {
return err
}
@@ -1341,8 +1369,8 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
case "AssetOwner":
requiredBitSet[0] |= 1 << 1
if err := func() error {
v, err := d.UInt64()
s.AssetOwner = uint64(v)
v, err := d.Int64()
s.AssetOwner = int64(v)
if err != nil {
return err
}
@@ -1377,8 +1405,8 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
case "GameID":
requiredBitSet[0] |= 1 << 4
if err := func() error {
v, err := d.UInt32()
s.GameID = uint32(v)
v, err := d.Int32()
s.GameID = int32(v)
if err != nil {
return err
}
@@ -1389,8 +1417,8 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
case "AssetID":
requiredBitSet[0] |= 1 << 5
if err := func() error {
v, err := d.UInt64()
s.AssetID = uint64(v)
v, err := d.Int64()
s.AssetID = int64(v)
if err != nil {
return err
}
@@ -1401,8 +1429,8 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
case "AssetVersion":
requiredBitSet[0] |= 1 << 6
if err := func() error {
v, err := d.UInt64()
s.AssetVersion = uint64(v)
v, err := d.Int64()
s.AssetVersion = int64(v)
if err != nil {
return err
}
@@ -1410,6 +1438,30 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
}(); err != nil {
return errors.Wrap(err, "decode field \"AssetVersion\"")
}
case "Status":
requiredBitSet[0] |= 1 << 7
if err := func() error {
v, err := d.UInt32()
s.Status = uint32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Status\"")
}
case "Roles":
requiredBitSet[1] |= 1 << 0
if err := func() error {
v, err := d.UInt32()
s.Roles = uint32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Roles\"")
}
default:
return d.Skip()
}
@@ -1419,8 +1471,9 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b01111111,
for i, mask := range [2]uint8{
0b11111111,
0b00000001,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
@@ -1477,7 +1530,7 @@ func (s *SubmissionID) Encode(e *jx.Encoder) {
func (s *SubmissionID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("SubmissionID")
e.UInt64(s.SubmissionID)
e.Int64(s.SubmissionID)
}
}
@@ -1497,8 +1550,8 @@ func (s *SubmissionID) Decode(d *jx.Decoder) error {
case "SubmissionID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.UInt64()
s.SubmissionID = uint64(v)
v, err := d.Int64()
s.SubmissionID = int64(v)
if err != nil {
return err
}

View File

@@ -7,11 +7,13 @@ 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"

File diff suppressed because it is too large Load Diff

View File

@@ -75,6 +75,66 @@ func decodeActionMapfixAcceptedResponse(resp *http.Response) (res *ActionMapfixA
return res, errors.Wrap(defRes, "error")
}
func decodeActionMapfixRequestChangesResponse(resp *http.Response) (res *ActionMapfixRequestChangesNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionMapfixRequestChangesNoContent{}, 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 decodeActionMapfixSubmittedResponse(resp *http.Response) (res *ActionMapfixSubmittedNoContent, _ error) {
switch resp.StatusCode {
case 204:
@@ -375,6 +435,66 @@ func decodeActionSubmissionAcceptedResponse(resp *http.Response) (res *ActionSub
return res, errors.Wrap(defRes, "error")
}
func decodeActionSubmissionRequestChangesResponse(resp *http.Response) (res *ActionSubmissionRequestChangesNoContent, _ error) {
switch resp.StatusCode {
case 204:
// Code 204.
return &ActionSubmissionRequestChangesNoContent{}, 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 decodeActionSubmissionSubmittedResponse(resp *http.Response) (res *ActionSubmissionSubmittedNoContent, _ error) {
switch resp.StatusCode {
case 204:

View File

@@ -20,6 +20,13 @@ func encodeActionMapfixAcceptedResponse(response *ActionMapfixAcceptedNoContent,
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))
@@ -55,6 +62,13 @@ func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNo
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))

View File

@@ -147,6 +147,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
case 'r': // Prefix: "request-changes"
if l := len("request-changes"); len(elem) >= l && elem[0:l] == "request-changes" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionMapfixRequestChangesRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 's': // Prefix: "submitted"
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
@@ -476,6 +498,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
case 'r': // Prefix: "request-changes"
if l := len("request-changes"); len(elem) >= l && elem[0:l] == "request-changes" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "POST":
s.handleActionSubmissionRequestChangesRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
return
}
case 's': // Prefix: "submitted"
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
@@ -760,6 +804,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
}
case 'r': // Prefix: "request-changes"
if l := len("request-changes"); len(elem) >= l && elem[0:l] == "request-changes" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionMapfixRequestChangesOperation
r.summary = "(Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested"
r.operationID = "actionMapfixRequestChanges"
r.pathPattern = "/mapfixes/{MapfixID}/status/validator-request-changes"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 's': // Prefix: "submitted"
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {
@@ -1127,6 +1195,30 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
}
}
case 'r': // Prefix: "request-changes"
if l := len("request-changes"); len(elem) >= l && elem[0:l] == "request-changes" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
// Leaf node.
switch method {
case "POST":
r.name = ActionSubmissionRequestChangesOperation
r.summary = "(Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested"
r.operationID = "actionSubmissionRequestChanges"
r.pathPattern = "/submissions/{SubmissionID}/status/validator-request-changes"
r.args = args
r.count = 1
return r, true
default:
return
}
}
case 's': // Prefix: "submitted"
if l := len("submitted"); len(elem) >= l && elem[0:l] == "submitted" {

View File

@@ -13,6 +13,9 @@ func (s *ErrorStatusCode) Error() string {
// 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{}
@@ -28,6 +31,9 @@ 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{}
@@ -40,12 +46,12 @@ type ActionSubmissionValidatedNoContent struct{}
// Represents error object.
// Ref: #/components/schemas/Error
type Error struct {
Code uint64 `json:"code"`
Code int64 `json:"code"`
Message string `json:"message"`
}
// GetCode returns the value of Code.
func (s *Error) GetCode() uint64 {
func (s *Error) GetCode() int64 {
return s.Code
}
@@ -55,7 +61,7 @@ func (s *Error) GetMessage() string {
}
// SetCode sets the value of Code.
func (s *Error) SetCode(val uint64) {
func (s *Error) SetCode(val int64) {
s.Code = val
}
@@ -92,23 +98,24 @@ func (s *ErrorStatusCode) SetResponse(val Error) {
// Ref: #/components/schemas/MapfixCreate
type MapfixCreate struct {
OperationID uint32 `json:"OperationID"`
AssetOwner uint64 `json:"AssetOwner"`
OperationID int32 `json:"OperationID"`
AssetOwner int64 `json:"AssetOwner"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID uint32 `json:"GameID"`
AssetID uint64 `json:"AssetID"`
AssetVersion uint64 `json:"AssetVersion"`
TargetAssetID uint64 `json:"TargetAssetID"`
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() uint32 {
func (s *MapfixCreate) GetOperationID() int32 {
return s.OperationID
}
// GetAssetOwner returns the value of AssetOwner.
func (s *MapfixCreate) GetAssetOwner() uint64 {
func (s *MapfixCreate) GetAssetOwner() int64 {
return s.AssetOwner
}
@@ -123,32 +130,37 @@ func (s *MapfixCreate) GetCreator() string {
}
// GetGameID returns the value of GameID.
func (s *MapfixCreate) GetGameID() uint32 {
func (s *MapfixCreate) GetGameID() int32 {
return s.GameID
}
// GetAssetID returns the value of AssetID.
func (s *MapfixCreate) GetAssetID() uint64 {
func (s *MapfixCreate) GetAssetID() int64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *MapfixCreate) GetAssetVersion() uint64 {
func (s *MapfixCreate) GetAssetVersion() int64 {
return s.AssetVersion
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *MapfixCreate) GetTargetAssetID() uint64 {
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 uint32) {
func (s *MapfixCreate) SetOperationID(val int32) {
s.OperationID = val
}
// SetAssetOwner sets the value of AssetOwner.
func (s *MapfixCreate) SetAssetOwner(val uint64) {
func (s *MapfixCreate) SetAssetOwner(val int64) {
s.AssetOwner = val
}
@@ -163,40 +175,137 @@ func (s *MapfixCreate) SetCreator(val string) {
}
// SetGameID sets the value of GameID.
func (s *MapfixCreate) SetGameID(val uint32) {
func (s *MapfixCreate) SetGameID(val int32) {
s.GameID = val
}
// SetAssetID sets the value of AssetID.
func (s *MapfixCreate) SetAssetID(val uint64) {
func (s *MapfixCreate) SetAssetID(val int64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *MapfixCreate) SetAssetVersion(val uint64) {
func (s *MapfixCreate) SetAssetVersion(val int64) {
s.AssetVersion = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *MapfixCreate) SetTargetAssetID(val uint64) {
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 uint64 `json:"MapfixID"`
MapfixID int64 `json:"MapfixID"`
}
// GetMapfixID returns the value of MapfixID.
func (s *MapfixID) GetMapfixID() uint64 {
func (s *MapfixID) GetMapfixID() int64 {
return s.MapfixID
}
// SetMapfixID sets the value of MapfixID.
func (s *MapfixID) SetMapfixID(val uint64) {
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{
@@ -243,110 +352,18 @@ func (o OptString) Or(d string) string {
return d
}
// NewOptUint32 returns new OptUint32 with value set to v.
func NewOptUint32(v uint32) OptUint32 {
return OptUint32{
Value: v,
Set: true,
}
}
// OptUint32 is optional uint32.
type OptUint32 struct {
Value uint32
Set bool
}
// IsSet returns true if OptUint32 was set.
func (o OptUint32) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptUint32) Reset() {
var v uint32
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptUint32) SetTo(v uint32) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptUint32) Get() (v uint32, 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 OptUint32) Or(d uint32) uint32 {
if v, ok := o.Get(); ok {
return v
}
return d
}
// NewOptUint64 returns new OptUint64 with value set to v.
func NewOptUint64(v uint64) OptUint64 {
return OptUint64{
Value: v,
Set: true,
}
}
// OptUint64 is optional uint64.
type OptUint64 struct {
Value uint64
Set bool
}
// IsSet returns true if OptUint64 was set.
func (o OptUint64) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptUint64) Reset() {
var v uint64
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptUint64) SetTo(v uint64) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptUint64) Get() (v uint64, 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 OptUint64) Or(d uint64) uint64 {
if v, ok := o.Get(); ok {
return v
}
return d
}
// Ref: #/components/schemas/Script
type Script struct {
ID uint64 `json:"ID"`
ID int64 `json:"ID"`
Name string `json:"Name"`
Hash string `json:"Hash"`
Source string `json:"Source"`
ResourceType uint32 `json:"ResourceType"`
ResourceID uint64 `json:"ResourceID"`
ResourceType int32 `json:"ResourceType"`
ResourceID int64 `json:"ResourceID"`
}
// GetID returns the value of ID.
func (s *Script) GetID() uint64 {
func (s *Script) GetID() int64 {
return s.ID
}
@@ -366,17 +383,17 @@ func (s *Script) GetSource() string {
}
// GetResourceType returns the value of ResourceType.
func (s *Script) GetResourceType() uint32 {
func (s *Script) GetResourceType() int32 {
return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *Script) GetResourceID() uint64 {
func (s *Script) GetResourceID() int64 {
return s.ResourceID
}
// SetID sets the value of ID.
func (s *Script) SetID(val uint64) {
func (s *Script) SetID(val int64) {
s.ID = val
}
@@ -396,21 +413,21 @@ func (s *Script) SetSource(val string) {
}
// SetResourceType sets the value of ResourceType.
func (s *Script) SetResourceType(val uint32) {
func (s *Script) SetResourceType(val int32) {
s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *Script) SetResourceID(val uint64) {
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 uint32 `json:"ResourceType"`
ResourceID OptUint64 `json:"ResourceID"`
Name string `json:"Name"`
Source string `json:"Source"`
ResourceType int32 `json:"ResourceType"`
ResourceID OptInt64 `json:"ResourceID"`
}
// GetName returns the value of Name.
@@ -424,12 +441,12 @@ func (s *ScriptCreate) GetSource() string {
}
// GetResourceType returns the value of ResourceType.
func (s *ScriptCreate) GetResourceType() uint32 {
func (s *ScriptCreate) GetResourceType() int32 {
return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *ScriptCreate) GetResourceID() OptUint64 {
func (s *ScriptCreate) GetResourceID() OptInt64 {
return s.ResourceID
}
@@ -444,40 +461,40 @@ func (s *ScriptCreate) SetSource(val string) {
}
// SetResourceType sets the value of ResourceType.
func (s *ScriptCreate) SetResourceType(val uint32) {
func (s *ScriptCreate) SetResourceType(val int32) {
s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *ScriptCreate) SetResourceID(val OptUint64) {
func (s *ScriptCreate) SetResourceID(val OptInt64) {
s.ResourceID = val
}
// Ref: #/components/schemas/ScriptID
type ScriptID struct {
ScriptID uint64 `json:"ScriptID"`
ScriptID int64 `json:"ScriptID"`
}
// GetScriptID returns the value of ScriptID.
func (s *ScriptID) GetScriptID() uint64 {
func (s *ScriptID) GetScriptID() int64 {
return s.ScriptID
}
// SetScriptID sets the value of ScriptID.
func (s *ScriptID) SetScriptID(val uint64) {
func (s *ScriptID) SetScriptID(val int64) {
s.ScriptID = val
}
// Ref: #/components/schemas/ScriptPolicy
type ScriptPolicy struct {
ID uint64 `json:"ID"`
ID int64 `json:"ID"`
FromScriptHash string `json:"FromScriptHash"`
ToScriptID uint64 `json:"ToScriptID"`
Policy uint32 `json:"Policy"`
ToScriptID int64 `json:"ToScriptID"`
Policy int32 `json:"Policy"`
}
// GetID returns the value of ID.
func (s *ScriptPolicy) GetID() uint64 {
func (s *ScriptPolicy) GetID() int64 {
return s.ID
}
@@ -487,17 +504,17 @@ func (s *ScriptPolicy) GetFromScriptHash() string {
}
// GetToScriptID returns the value of ToScriptID.
func (s *ScriptPolicy) GetToScriptID() uint64 {
func (s *ScriptPolicy) GetToScriptID() int64 {
return s.ToScriptID
}
// GetPolicy returns the value of Policy.
func (s *ScriptPolicy) GetPolicy() uint32 {
func (s *ScriptPolicy) GetPolicy() int32 {
return s.Policy
}
// SetID sets the value of ID.
func (s *ScriptPolicy) SetID(val uint64) {
func (s *ScriptPolicy) SetID(val int64) {
s.ID = val
}
@@ -507,85 +524,87 @@ func (s *ScriptPolicy) SetFromScriptHash(val string) {
}
// SetToScriptID sets the value of ToScriptID.
func (s *ScriptPolicy) SetToScriptID(val uint64) {
func (s *ScriptPolicy) SetToScriptID(val int64) {
s.ToScriptID = val
}
// SetPolicy sets the value of Policy.
func (s *ScriptPolicy) SetPolicy(val uint32) {
func (s *ScriptPolicy) SetPolicy(val int32) {
s.Policy = val
}
// Ref: #/components/schemas/ScriptPolicyCreate
type ScriptPolicyCreate struct {
FromScriptID uint64 `json:"FromScriptID"`
ToScriptID uint64 `json:"ToScriptID"`
Policy uint32 `json:"Policy"`
FromScriptID int64 `json:"FromScriptID"`
ToScriptID int64 `json:"ToScriptID"`
Policy int32 `json:"Policy"`
}
// GetFromScriptID returns the value of FromScriptID.
func (s *ScriptPolicyCreate) GetFromScriptID() uint64 {
func (s *ScriptPolicyCreate) GetFromScriptID() int64 {
return s.FromScriptID
}
// GetToScriptID returns the value of ToScriptID.
func (s *ScriptPolicyCreate) GetToScriptID() uint64 {
func (s *ScriptPolicyCreate) GetToScriptID() int64 {
return s.ToScriptID
}
// GetPolicy returns the value of Policy.
func (s *ScriptPolicyCreate) GetPolicy() uint32 {
func (s *ScriptPolicyCreate) GetPolicy() int32 {
return s.Policy
}
// SetFromScriptID sets the value of FromScriptID.
func (s *ScriptPolicyCreate) SetFromScriptID(val uint64) {
func (s *ScriptPolicyCreate) SetFromScriptID(val int64) {
s.FromScriptID = val
}
// SetToScriptID sets the value of ToScriptID.
func (s *ScriptPolicyCreate) SetToScriptID(val uint64) {
func (s *ScriptPolicyCreate) SetToScriptID(val int64) {
s.ToScriptID = val
}
// SetPolicy sets the value of Policy.
func (s *ScriptPolicyCreate) SetPolicy(val uint32) {
func (s *ScriptPolicyCreate) SetPolicy(val int32) {
s.Policy = val
}
// Ref: #/components/schemas/ScriptPolicyID
type ScriptPolicyID struct {
ScriptPolicyID uint64 `json:"ScriptPolicyID"`
ScriptPolicyID int64 `json:"ScriptPolicyID"`
}
// GetScriptPolicyID returns the value of ScriptPolicyID.
func (s *ScriptPolicyID) GetScriptPolicyID() uint64 {
func (s *ScriptPolicyID) GetScriptPolicyID() int64 {
return s.ScriptPolicyID
}
// SetScriptPolicyID sets the value of ScriptPolicyID.
func (s *ScriptPolicyID) SetScriptPolicyID(val uint64) {
func (s *ScriptPolicyID) SetScriptPolicyID(val int64) {
s.ScriptPolicyID = val
}
// Ref: #/components/schemas/SubmissionCreate
type SubmissionCreate struct {
OperationID uint32 `json:"OperationID"`
AssetOwner uint64 `json:"AssetOwner"`
OperationID int32 `json:"OperationID"`
AssetOwner int64 `json:"AssetOwner"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID uint32 `json:"GameID"`
AssetID uint64 `json:"AssetID"`
AssetVersion uint64 `json:"AssetVersion"`
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() uint32 {
func (s *SubmissionCreate) GetOperationID() int32 {
return s.OperationID
}
// GetAssetOwner returns the value of AssetOwner.
func (s *SubmissionCreate) GetAssetOwner() uint64 {
func (s *SubmissionCreate) GetAssetOwner() int64 {
return s.AssetOwner
}
@@ -600,27 +619,37 @@ func (s *SubmissionCreate) GetCreator() string {
}
// GetGameID returns the value of GameID.
func (s *SubmissionCreate) GetGameID() uint32 {
func (s *SubmissionCreate) GetGameID() int32 {
return s.GameID
}
// GetAssetID returns the value of AssetID.
func (s *SubmissionCreate) GetAssetID() uint64 {
func (s *SubmissionCreate) GetAssetID() int64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *SubmissionCreate) GetAssetVersion() uint64 {
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 uint32) {
func (s *SubmissionCreate) SetOperationID(val int32) {
s.OperationID = val
}
// SetAssetOwner sets the value of AssetOwner.
func (s *SubmissionCreate) SetAssetOwner(val uint64) {
func (s *SubmissionCreate) SetAssetOwner(val int64) {
s.AssetOwner = val
}
@@ -635,32 +664,42 @@ func (s *SubmissionCreate) SetCreator(val string) {
}
// SetGameID sets the value of GameID.
func (s *SubmissionCreate) SetGameID(val uint32) {
func (s *SubmissionCreate) SetGameID(val int32) {
s.GameID = val
}
// SetAssetID sets the value of AssetID.
func (s *SubmissionCreate) SetAssetID(val uint64) {
func (s *SubmissionCreate) SetAssetID(val int64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *SubmissionCreate) SetAssetVersion(val uint64) {
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 uint64 `json:"SubmissionID"`
SubmissionID int64 `json:"SubmissionID"`
}
// GetSubmissionID returns the value of SubmissionID.
func (s *SubmissionID) GetSubmissionID() uint64 {
func (s *SubmissionID) GetSubmissionID() int64 {
return s.SubmissionID
}
// SetSubmissionID sets the value of SubmissionID.
func (s *SubmissionID) SetSubmissionID(val uint64) {
func (s *SubmissionID) SetSubmissionID(val int64) {
s.SubmissionID = val
}

View File

@@ -14,6 +14,12 @@ type Handler interface {
//
// 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.
@@ -44,6 +50,12 @@ type Handler interface {
//
// 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.

View File

@@ -22,6 +22,15 @@ func (UnimplementedHandler) ActionMapfixAccepted(ctx context.Context, params Act
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.
@@ -67,6 +76,15 @@ func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params
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.

View File

@@ -227,6 +227,25 @@ func (s *MapfixCreate) Validate() error {
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 256,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.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}
}
@@ -862,6 +881,26 @@ func (s *SubmissionCreate) Validate() error {
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}
}

View File

@@ -5,7 +5,7 @@ import (
"time"
)
type AuditEventType uint32
type AuditEventType int32
// User clicked "Submit", "Accept" etc
const AuditEventTypeAction AuditEventType = 0
@@ -42,12 +42,18 @@ type AuditEventDataChangeName struct {
NewName string `json:"new_name"`
}
// Validator had an error
const AuditEventTypeError AuditEventType = 6
type AuditEventDataError struct {
Error string `json:"error"`
}
type AuditEvent struct {
ID uint64 `gorm:"primaryKey"`
ID int64 `gorm:"primaryKey"`
CreatedAt time.Time
User uint64
ResourceType ResourceType // is this a submission or is it a mapfix
ResourceID uint64 // submission / mapfix / map ID
ResourceID int64 // submission / mapfix / map ID
EventType AuditEventType
EventData json.RawMessage `gorm:"type:jsonb"`
}

View File

@@ -2,7 +2,7 @@ package model
import "time"
type MapfixStatus uint32
type MapfixStatus int32
const (
// Phase: Creation
@@ -25,7 +25,7 @@ const (
)
type Mapfix struct {
ID uint64 `gorm:"primaryKey"`
ID int64 `gorm:"primaryKey"`
DisplayName string
Creator string
GameID uint32
@@ -39,5 +39,5 @@ type Mapfix struct {
Completed bool // Has this version of the map been completed at least once on maptest
TargetAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
StatusID MapfixStatus
StatusMessage string
Description string // mapfix description
}

View File

@@ -7,26 +7,43 @@ package model
type CreateSubmissionRequest struct {
// operation_id is passed back in the response message
OperationID uint32
OperationID int32
ModelID uint64
DisplayName string
Creator string
GameID uint32
Status uint32
Roles uint32
}
type CreateMapfixRequest struct {
OperationID uint32
OperationID int32
ModelID uint64
TargetAssetID uint64
Description string
}
type CheckSubmissionRequest struct{
SubmissionID int64
ModelID uint64
}
type CheckMapfixRequest struct{
MapfixID int64
ModelID uint64
}
type ValidateSubmissionRequest struct {
// submission_id is passed back in the response message
SubmissionID uint64
SubmissionID int64
ModelID uint64
ModelVersion uint64
ValidatedModelID *uint64 // optional value
}
type ValidateMapfixRequest struct {
MapfixID uint64
MapfixID int64
ModelID uint64
ModelVersion uint64
ValidatedModelID *uint64 // optional value
@@ -34,14 +51,14 @@ type ValidateMapfixRequest struct {
// Create a new map
type UploadSubmissionRequest struct {
SubmissionID uint64
SubmissionID int64
ModelID uint64
ModelVersion uint64
ModelName string
}
type UploadMapfixRequest struct {
MapfixID uint64
MapfixID int64
ModelID uint64
ModelVersion uint64
TargetAssetID uint64

View File

@@ -2,7 +2,7 @@ package model
import "time"
type OperationStatus uint32
type OperationStatus int32
const (
OperationStatusCreated OperationStatus = 0
OperationStatusCompleted OperationStatus = 1
@@ -10,7 +10,7 @@ const (
)
type Operation struct {
ID uint32 `gorm:"primaryKey"`
ID int32 `gorm:"primaryKey"`
CreatedAt time.Time
Owner uint64 // UserID
StatusID OperationStatus

View File

@@ -1,6 +1,6 @@
package model
type Page struct {
Number uint32
Size uint32
Number int32
Size int32
}

View File

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

View File

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

View File

@@ -2,7 +2,7 @@ package model
import "time"
type SubmissionStatus uint32
type SubmissionStatus int32
const (
// Phase: Creation
@@ -26,7 +26,7 @@ const (
)
type Submission struct {
ID uint64 `gorm:"primaryKey"`
ID int64 `gorm:"primaryKey"`
DisplayName string
Creator string
GameID uint32
@@ -40,5 +40,4 @@ type Submission struct {
Completed bool // Has this version of the map been completed at least once on maptest
UploadedAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
StatusID SubmissionStatus
StatusMessage string
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"git.itzana.me/strafesnet/go-grpc/users"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
@@ -72,8 +73,8 @@ func (svc *Service) CreateMapfixAuditComment(ctx context.Context, req api.Create
func (svc *Service) ListMapfixAuditEvents(ctx context.Context, params api.ListMapfixAuditEventsParams) ([]api.AuditEvent, error) {
filter := datastore.Optional()
filter.AddPostgresInt32("resource_type", uint32(model.ResourceMapfix))
filter.AddPostgresInt64("resource_id", params.MapfixID)
filter.Add("resource_type", model.ResourceMapfix)
filter.Add("resource_id", params.MapfixID)
items, err := svc.DB.AuditEvents().List(ctx, filter, model.Page{
Number: params.Page,
@@ -83,6 +84,27 @@ func (svc *Service) ListMapfixAuditEvents(ctx context.Context, params api.ListMa
return nil, 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{}
@@ -90,13 +112,18 @@ func (svc *Service) ListMapfixAuditEvents(ctx context.Context, params api.ListMa
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: item.User,
ResourceType: uint32(item.ResourceType),
User: int64(item.User),
Username: username,
ResourceType: int32(item.ResourceType),
ResourceID: item.ResourceID,
EventType: uint32(item.EventType),
EventType: int32(item.EventType),
EventData: EventData,
})
}
@@ -167,8 +194,8 @@ func (svc *Service) CreateSubmissionAuditComment(ctx context.Context, req api.Cr
func (svc *Service) ListSubmissionAuditEvents(ctx context.Context, params api.ListSubmissionAuditEventsParams) ([]api.AuditEvent, error) {
filter := datastore.Optional()
filter.AddPostgresInt32("resource_type", uint32(model.ResourceSubmission))
filter.AddPostgresInt64("resource_id", params.SubmissionID)
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,
@@ -178,6 +205,27 @@ func (svc *Service) ListSubmissionAuditEvents(ctx context.Context, params api.Li
return nil, 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{}
@@ -185,13 +233,18 @@ func (svc *Service) ListSubmissionAuditEvents(ctx context.Context, params api.Li
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: item.User,
ResourceType: uint32(item.ResourceType),
User: int64(item.User),
Username: username,
ResourceType: int32(item.ResourceType),
ResourceID: item.ResourceID,
EventType: uint32(item.EventType),
EventType: int32(item.EventType),
EventData: EventData,
})
}

View File

@@ -65,7 +65,7 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
filter.Add("status_id", CreationPhaseMapfixStatuses)
creation_mapfixes, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
Number: 1,
Size: uint32(CreationPhaseMapfixesLimit),
Size: int32(CreationPhaseMapfixesLimit),
},datastore.ListSortDisabled)
if err != nil {
return nil, err
@@ -78,7 +78,7 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
// Check if TargetAssetID actually exists
{
_, err := svc.Client.Get(ctx, &maps.IdMessage{
_, err := svc.Maps.Get(ctx, &maps.IdMessage{
ID: request.TargetAssetID,
})
if err != nil {
@@ -114,6 +114,7 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
OperationID: operation.ID,
ModelID: ModelID,
TargetAssetID: TargetAssetID,
Description: request.Description,
}
j, err := json.Marshal(create_request)
@@ -137,7 +138,7 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
//
// GET /mapfixes/{MapfixID}
func (svc *Service) GetMapfix(ctx context.Context, params api.GetMapfixParams) (*api.Mapfix, error) {
mapfix, err := svc.DB.Mapfixes().Get(ctx, int64(params.MapfixID))
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
if err != nil {
return nil, err
}
@@ -145,16 +146,16 @@ func (svc *Service) GetMapfix(ctx context.Context, params api.GetMapfixParams) (
ID: mapfix.ID,
DisplayName: mapfix.DisplayName,
Creator: mapfix.Creator,
GameID: uint32(mapfix.GameID),
GameID: int32(mapfix.GameID),
CreatedAt: mapfix.CreatedAt.Unix(),
UpdatedAt: mapfix.UpdatedAt.Unix(),
Submitter: mapfix.Submitter,
AssetID: mapfix.AssetID,
AssetVersion: mapfix.AssetVersion,
Submitter: int64(mapfix.Submitter),
AssetID: int64(mapfix.AssetID),
AssetVersion: int64(mapfix.AssetVersion),
Completed: mapfix.Completed,
TargetAssetID: mapfix.TargetAssetID,
StatusID: uint32(mapfix.StatusID),
StatusMessage: mapfix.StatusMessage,
TargetAssetID: int64(mapfix.TargetAssetID),
StatusID: int32(mapfix.StatusID),
Description: mapfix.Description,
}, nil
}
@@ -167,28 +168,28 @@ func (svc *Service) ListMapfixes(ctx context.Context, params api.ListMapfixesPar
filter := datastore.Optional()
if params.DisplayName.IsSet(){
filter.Add2("display_name", params.DisplayName.Value)
filter.Add("display_name", params.DisplayName.Value)
}
if params.Creator.IsSet(){
filter.Add2("creator", params.Creator.Value)
filter.Add("creator", params.Creator.Value)
}
if params.GameID.IsSet(){
filter.AddPostgresInt32("game_id", params.GameID.Value)
filter.Add("game_id", params.GameID.Value)
}
if params.Submitter.IsSet(){
filter.AddPostgresInt64("submitter", params.Submitter.Value)
filter.Add("submitter", params.Submitter.Value)
}
if params.AssetID.IsSet(){
filter.AddPostgresInt64("asset_id", params.AssetID.Value)
filter.Add("asset_id", params.AssetID.Value)
}
if params.TargetAssetID.IsSet(){
filter.AddPostgresInt64("target_asset_id", params.TargetAssetID.Value)
filter.Add("target_asset_id", params.TargetAssetID.Value)
}
if params.StatusID.IsSet(){
filter.AddPostgresInt32("status_id", params.StatusID.Value)
filter.Add("status_id", params.StatusID.Value)
}
sort := datastore.ListSort(params.Sort.Or(uint32(datastore.ListSortDisabled)))
sort := datastore.ListSort(params.Sort.Or(int32(datastore.ListSortDisabled)))
total, items, err := svc.DB.Mapfixes().ListWithTotal(ctx, filter, model.Page{
Number: params.Page,
@@ -205,15 +206,16 @@ func (svc *Service) ListMapfixes(ctx context.Context, params api.ListMapfixesPar
ID: item.ID,
DisplayName: item.DisplayName,
Creator: item.Creator,
GameID: item.GameID,
GameID: int32(item.GameID),
CreatedAt: item.CreatedAt.Unix(),
UpdatedAt: item.UpdatedAt.Unix(),
Submitter: item.Submitter,
AssetID: item.AssetID,
AssetVersion: item.AssetVersion,
Submitter: int64(item.Submitter),
AssetID: int64(item.AssetID),
AssetVersion: int64(item.AssetVersion),
Completed: item.Completed,
TargetAssetID: item.TargetAssetID,
StatusID: uint32(item.StatusID),
TargetAssetID: int64(item.TargetAssetID),
StatusID: int32(item.StatusID),
Description: item.Description,
})
}
@@ -490,7 +492,7 @@ func (svc *Service) ActionMapfixTriggerSubmit(ctx context.Context, params api.Ac
}
// transaction
target_status := model.MapfixStatusSubmitted
target_status := model.MapfixStatusSubmitting
smap := datastore.Optional()
smap.Add("status_id", target_status)
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUnderConstruction, model.MapfixStatusChangesRequested}, smap)
@@ -498,6 +500,21 @@ func (svc *Service) ActionMapfixTriggerSubmit(ctx context.Context, params api.Ac
return err
}
validate_request := model.CheckMapfixRequest{
MapfixID: mapfix.ID,
ModelID: mapfix.AssetID,
}
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
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
@@ -558,7 +575,6 @@ func (svc *Service) ActionMapfixResetSubmitting(ctx context.Context, params api.
target_status := model.MapfixStatusUnderConstruction
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("status_message", "Manually forced reset")
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap)
if err != nil {
return err
@@ -960,7 +976,6 @@ func (svc *Service) ActionMapfixAccepted(ctx context.Context, params api.ActionM
target_status := model.MapfixStatusAcceptedUnvalidated
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("status_message", "Manually forced reset")
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
if err != nil {
return err

View File

@@ -25,7 +25,7 @@ func (svc *Service) ListMaps(ctx context.Context, params api.ListMapsParams) ([]
filter.GameID = &params.GameID.Value
}
mapList, err := svc.Client.List(ctx, &maps.ListRequest{
mapList, err := svc.Maps.List(ctx, &maps.ListRequest{
Filter: &filter,
Page: &maps.Pagination{
Size: params.Limit,
@@ -56,7 +56,7 @@ func (svc *Service) ListMaps(ctx context.Context, params api.ListMapsParams) ([]
//
// GET /maps/{MapID}
func (svc *Service) GetMap(ctx context.Context, params api.GetMapParams) (*api.Map, error) {
mapResponse, err := svc.Client.Get(ctx, &maps.IdMessage{
mapResponse, err := svc.Maps.Get(ctx, &maps.IdMessage{
ID: params.MapID,
})
if err != nil {

View File

@@ -6,6 +6,7 @@ import (
"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"
@@ -32,7 +33,8 @@ var (
type Service struct {
DB datastore.Datastore
Nats nats.JetStreamContext
Client maps.MapsServiceClient
Maps maps.MapsServiceClient
Users users.UsersServiceClient
}
// NewError creates *ErrorStatusCode from error returned by handler.

View File

@@ -101,8 +101,89 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
}
create_request := model.CreateSubmissionRequest{
OperationID: operation.ID,
ModelID: ModelID,
OperationID: operation.ID,
ModelID: ModelID,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: uint32(request.GameID),
Status: uint32(model.SubmissionStatusUnderConstruction),
Roles: uint32(RolesEmpty),
}
j, err := json.Marshal(create_request)
if err != nil {
return nil, err
}
_, err = svc.Nats.Publish("maptest.submissions.create", []byte(j))
if err != nil {
return nil, err
}
return &api.OperationID{
OperationID: operation.ID,
}, nil
}
// POST /submissions-admin
func (svc *Service) CreateSubmissionAdmin(ctx context.Context, request *api.SubmissionTriggerCreate) (*api.OperationID, error) {
// sanitization
if request.AssetID<0{
return nil, ErrNegativeID
}
var ModelID=uint64(request.AssetID);
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
userId, err := userInfo.GetUserID()
if err != nil {
return nil, err
}
roles, err := userInfo.GetRoles()
if err != nil {
return nil, err
}
// check if caller has required role
has_role := roles & RolesSubmissionReview == RolesSubmissionReview
if !has_role {
return nil, ErrPermissionDeniedNeedRoleSubmissionReview
}
// Check if too many operations have been created recently
{
count, err := svc.DB.Operations().CountSince(ctx,
int64(userId),
time.Now().Add(-CreateSubmissionRecencyWindow),
)
if err != nil {
return nil, err
}
if CreateSubmissionRateLimit < count {
return nil, ErrCreateSubmissionRateLimit
}
}
operation, err := svc.DB.Operations().Create(ctx, model.Operation{
Owner: userId,
StatusID: model.OperationStatusCreated,
})
if err != nil {
return nil, err
}
create_request := model.CreateSubmissionRequest{
OperationID: operation.ID,
ModelID: ModelID,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: uint32(request.GameID),
Status: uint32(model.SubmissionStatusChangesRequested),
Roles: uint32(roles),
}
j, err := json.Marshal(create_request)
@@ -143,7 +224,6 @@ func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionP
Completed: submission.Completed,
UploadedAssetID: api.NewOptInt64(int64(submission.UploadedAssetID)),
StatusID: int32(submission.StatusID),
StatusMessage: submission.StatusMessage,
}, nil
}
@@ -504,14 +584,20 @@ func (svc *Service) ActionSubmissionTriggerSubmit(ctx context.Context, params ap
return err
}
has_submission_review, err := userInfo.HasRoleSubmissionReview()
if err != nil {
return err
}
// check if caller is the submitter
has_role := userId == submission.Submitter
if !has_role {
is_submitter := userId == submission.Submitter
// neither = deny
if !is_submitter && !has_submission_review {
return ErrPermissionDeniedNotSubmitter
}
// transaction
target_status := model.SubmissionStatusSubmitted
target_status := model.SubmissionStatusSubmitting
smap := datastore.Optional()
smap.Add("status_id", target_status)
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUnderConstruction, model.SubmissionStatusChangesRequested}, smap)
@@ -519,6 +605,21 @@ func (svc *Service) ActionSubmissionTriggerSubmit(ctx context.Context, params ap
return err
}
validate_request := model.CheckSubmissionRequest{
SubmissionID: submission.ID,
ModelID: submission.AssetID,
}
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
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
@@ -579,7 +680,6 @@ func (svc *Service) ActionSubmissionResetSubmitting(ctx context.Context, params
target_status := model.SubmissionStatusUnderConstruction
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("status_message", "Manually forced reset")
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap)
if err != nil {
return err
@@ -971,7 +1071,6 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params api.Act
target_status := model.SubmissionStatusAcceptedUnvalidated
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("status_message", "Manually forced reset")
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
if err != nil {
return err
@@ -1046,7 +1145,7 @@ func (svc *Service) ReleaseSubmissions(ctx context.Context, request []api.Releas
date := request[i].Date.Unix()
var GameID = int32(submission.GameID)
// create each map with go-grpc
_, err := svc.Client.Create(ctx, &maps.MapRequest{
_, err := svc.Maps.Create(ctx, &maps.MapRequest{
ID: int64(submission.UploadedAssetID),
DisplayName: &submission.DisplayName,
Creator: &submission.Creator,

View File

@@ -74,7 +74,7 @@ func (svc *Service) UpdateMapfixValidatedModel(ctx context.Context, params inter
return nil
}
// ActionMapfixValidate invokes actionMapfixValidate operation.
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
//
// Role Validator changes status from Submitting -> Submitted.
//
@@ -84,6 +84,10 @@ func (svc *Service) ActionMapfixSubmitted(ctx context.Context, params internal.A
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
@@ -113,6 +117,68 @@ func (svc *Service) ActionMapfixSubmitted(ctx context.Context, params internal.A
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.
@@ -135,12 +201,36 @@ func (svc *Service) ActionMapfixAccepted(ctx context.Context, params internal.Ac
target_status := model.MapfixStatusAcceptedUnvalidated
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("status_message", params.StatusMessage)
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),
}
@@ -260,6 +350,7 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *internal.MapfixCr
Completed: false,
TargetAssetID: TargetAssetID,
StatusID: model.MapfixStatusUnderConstruction,
Description: request.Description,
})
if err != nil {
return nil, err

View File

@@ -9,6 +9,7 @@ import (
"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(
@@ -73,7 +74,7 @@ func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params i
return nil
}
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
//
// Role Validator changes status from Submitting -> Submitted.
//
@@ -83,6 +84,10 @@ func (svc *Service) ActionSubmissionSubmitted(ctx context.Context, params intern
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
@@ -112,6 +117,70 @@ func (svc *Service) ActionSubmissionSubmitted(ctx context.Context, params intern
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.
@@ -161,12 +230,37 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params interna
target_status := model.SubmissionStatusAcceptedUnvalidated
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("status_message", params.StatusMessage)
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),
}
@@ -244,6 +338,8 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *internal.Subm
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
{
@@ -269,8 +365,11 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *internal.Subm
}
// check if user owns asset
// TODO: allow bypass by admin
if operation.Owner != Submitter {
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
}
@@ -283,7 +382,7 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *internal.Subm
AssetID: AssetID,
AssetVersion: AssetVersion,
Completed: false,
StatusID: model.SubmissionStatusUnderConstruction,
StatusID: Status,
})
if err != nil {
return nil, err

View File

@@ -7,7 +7,7 @@ edition = "2021"
submissions-api = { path = "api", features = ["internal"], default-features = false, registry = "strafesnet" }
async-nats = "0.40.0"
futures = "0.3.31"
rbx_asset = { version = "0.4.3", registry = "strafesnet" }
rbx_asset = { version = "0.4.4-pre2", registry = "strafesnet" }
rbx_binary = { version = "0.7.4", registry = "strafesnet"}
rbx_dom_weak = { version = "2.9.0", registry = "strafesnet"}
rbx_reflection_database = { version = "0.2.12", registry = "strafesnet"}
@@ -16,3 +16,5 @@ serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.133"
siphasher = "1.0.1"
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread", "signal"] }
heck = "0.5.0"
lazy-regex = "3.4.1"

View File

@@ -162,6 +162,15 @@ impl Context{
.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())
@@ -171,7 +180,7 @@ impl Context{
("UploadedAssetID",config.UploadedAssetID.to_string().as_str())
);
action!("submissions",action_submission_accepted,config,ActionSubmissionAcceptedRequest,"status/validator-failed",config.SubmissionID,
("StatusMessage",config.StatusMessage.as_str())
("ErrorMessage",config.ErrorMessage.as_str())
);
pub async fn create_mapfix<'a>(&self,config:CreateMapfixRequest<'a>)->Result<MapfixIDResponse,Error>{
let url_raw=format!("{}/mapfixes",self.0.base_url);
@@ -185,6 +194,15 @@ impl Context{
.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())
@@ -192,7 +210,7 @@ impl Context{
);
action!("mapfixes",action_mapfix_uploaded,config,ActionMapfixUploadedRequest,"status/validator-uploaded",config.MapfixID,);
action!("mapfixes",action_mapfix_accepted,config,ActionMapfixAcceptedRequest,"status/validator-failed",config.MapfixID,
("StatusMessage",config.StatusMessage.as_str())
("ErrorMessage",config.ErrorMessage.as_str())
);
// simple operation endpoint
action!("operations",action_operation_failed,config,ActionOperationFailedRequest,"status/operation-failed",config.OperationID,

View File

@@ -73,6 +73,7 @@ pub struct CreateMapfixRequest<'a>{
pub AssetID:u64,
pub AssetVersion:u64,
pub TargetAssetID:u64,
pub Description:&'a str,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
@@ -90,6 +91,8 @@ pub struct CreateSubmissionRequest<'a>{
pub GameID:i32,
pub AssetID:u64,
pub AssetVersion:u64,
pub Status:u32,
pub Roles:u32,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
@@ -222,6 +225,23 @@ pub struct UpdateSubmissionModelRequest{
pub ModelVersion:u64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionSubmittedRequest{
pub SubmissionID:i64,
pub ModelVersion:u64,
pub DisplayName:String,
pub Creator:String,
pub GameID:u32,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionRequestChangesRequest{
pub SubmissionID:i64,
pub ErrorMessage:String,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionUploadedRequest{
@@ -233,7 +253,7 @@ pub struct ActionSubmissionUploadedRequest{
#[derive(Clone,Debug)]
pub struct ActionSubmissionAcceptedRequest{
pub SubmissionID:i64,
pub StatusMessage:String,
pub ErrorMessage:String,
}
#[derive(Clone,Copy,Debug,serde::Deserialize)]
@@ -247,6 +267,23 @@ pub struct UpdateMapfixModelRequest{
pub ModelVersion:u64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixSubmittedRequest{
pub MapfixID:i64,
pub ModelVersion:u64,
pub DisplayName:String,
pub Creator:String,
pub GameID:u32,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixRequestChangesRequest{
pub MapfixID:i64,
pub ErrorMessage:String,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixUploadedRequest{
@@ -257,7 +294,7 @@ pub struct ActionMapfixUploadedRequest{
#[derive(Clone,Debug)]
pub struct ActionMapfixAcceptedRequest{
pub MapfixID:i64,
pub StatusMessage:String,
pub ErrorMessage:String,
}
#[derive(Clone,Copy,Debug,serde::Deserialize)]

645
validation/src/check.rs Normal file
View File

@@ -0,0 +1,645 @@
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 heck::{ToSnakeCase,ToTitleCase};
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
ModelInfoDownload(rbx_asset::cloud::GetError),
CreatorTypeMustBeUser,
Download(crate::download::Error),
ModelFileDecode(ReadDomError),
GetRootInstance(GetRootInstanceError),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
#[allow(nonstandard_style)]
pub struct CheckRequest{
pub ModelID:u64,
}
impl From<crate::nats_types::CheckMapfixRequest> for CheckRequest{
fn from(value:crate::nats_types::CheckMapfixRequest)->Self{
Self{
ModelID:value.ModelID,
}
}
}
impl From<crate::nats_types::CheckSubmissionRequest> for CheckRequest{
fn from(value:crate::nats_types::CheckSubmissionRequest)->Self{
Self{
ModelID:value.ModelID,
}
}
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
struct ModeID(u64);
impl ModeID{
const MAIN:Self=Self(0);
const BONUS:Self=Self(1);
}
enum Zone{
Start(ModeID),
Finish(ModeID),
Anticheat(ModeID),
}
#[allow(dead_code)]
pub enum IDParseError{
NoCaptures,
ParseInt(core::num::ParseIntError)
}
impl std::str::FromStr for Zone{
type Err=IDParseError;
fn from_str(s:&str)->Result<Self,Self::Err>{
match s{
"MapStart"=>Ok(Self::Start(ModeID::MAIN)),
"MapFinish"=>Ok(Self::Finish(ModeID::MAIN)),
"MapAnticheat"=>Ok(Self::Anticheat(ModeID::MAIN)),
"BonusStart"=>Ok(Self::Start(ModeID::BONUS)),
"BonusFinish"=>Ok(Self::Finish(ModeID::BONUS)),
"BonusAnticheat"=>Ok(Self::Anticheat(ModeID::BONUS)),
other=>{
let bonus_start_pattern=lazy_regex::lazy_regex!(r"^Bonus(\d+)Start$|^BonusStart(\d+)$");
if let Some(captures)=bonus_start_pattern.captures(other){
return Ok(Self::Start(ModeID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
}
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^Bonus(\d+)Finish$|^BonusFinish(\d+)$");
if let Some(captures)=bonus_finish_pattern.captures(other){
return Ok(Self::Finish(ModeID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
}
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^Bonus(\d+)Anticheat$|^BonusAnticheat(\d+)$");
if let Some(captures)=bonus_finish_pattern.captures(other){
return Ok(Self::Anticheat(ModeID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
}
Err(IDParseError::NoCaptures)
}
}
}
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
struct SpawnID(u64);
impl SpawnID{
const FIRST:Self=Self(1);
}
enum SpawnTeleport{
Teleport(SpawnID),
Spawn(SpawnID),
}
impl std::str::FromStr for SpawnTeleport{
type Err=IDParseError;
fn from_str(s:&str)->Result<Self,Self::Err>{
// Trigger ForceTrigger Teleport ForceTeleport SpawnAt ForceSpawnAt
let bonus_start_pattern=lazy_regex::lazy_regex!(r"^(?:Force)?(Teleport|SpawnAt|Trigger)(\d+)$");
if let Some(captures)=bonus_start_pattern.captures(s){
return Ok(Self::Teleport(SpawnID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
}
// Spawn
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^Spawn(\d+)$");
if let Some(captures)=bonus_finish_pattern.captures(s){
return Ok(Self::Spawn(SpawnID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
}
Err(IDParseError::NoCaptures)
}
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
struct WormholeID(u64);
enum Wormhole{
In(WormholeID),
Out(WormholeID),
}
impl std::str::FromStr for Wormhole{
type Err=IDParseError;
fn from_str(s:&str)->Result<Self,Self::Err>{
let bonus_start_pattern=lazy_regex::lazy_regex!(r"^WormholeIn(\d+)$");
if let Some(captures)=bonus_start_pattern.captures(s){
return Ok(Self::In(WormholeID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
}
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^WormholeOut(\d+)$");
if let Some(captures)=bonus_finish_pattern.captures(s){
return Ok(Self::Out(WormholeID(captures[1].parse().map_err(IDParseError::ParseInt)?)));
}
Err(IDParseError::NoCaptures)
}
}
#[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<SpawnID,Vec<&'a str>>,
spawn_counts:HashMap<SpawnID,u64>,
wormhole_in_counts:HashMap<WormholeID,u64>,
wormhole_out_counts:HashMap<WormholeID,u64>,
}
pub struct ModelInfo<'a>{
model_class:&'a str,
model_name:&'a str,
map_info:MapInfo<'a>,
counts:Counts<'a>,
}
pub fn get_model_info<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&'a rbx_dom_weak::Instance)->ModelInfo<'a>{
// extract model info
let map_info=get_mapinfo(&dom,model_instance);
// 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(Zone::Start(mode_id))=>counts.mode_start_counts.entry(mode_id).or_default().push(instance.name.as_str()),
Ok(Zone::Finish(mode_id))=>counts.mode_finish_counts.entry(mode_id).or_default().push(instance.name.as_str()),
Ok(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(SpawnTeleport::Teleport(spawn_id))=>counts.teleport_counts.entry(spawn_id).or_default().push(instance.name.as_str()),
Ok(SpawnTeleport::Spawn(spawn_id))=>*counts.spawn_counts.entry(spawn_id).or_insert(0)+=1,
Err(_)=>(),
}
// Wormholes
match instance.name.parse(){
Ok(Wormhole::In(wormhole_id))=>*counts.wormhole_in_counts.entry(wormhole_id).or_insert(0)+=1,
Ok(Wormhole::Out(wormhole_id))=>*counts.wormhole_out_counts.entry(wormhole_id).or_insert(0)+=1,
Err(_)=>(),
}
}
}
ModelInfo{
model_class:model_instance.class.as_str(),
model_name:model_instance.name.as_str(),
map_info,
counts,
}
}
// check if an observed string matches an expected string
pub struct StringCheck<'a,T,Str>(Result<T,StringCheckContext<'a,Str>>);
pub struct StringCheckContext<'a,Str>{
observed:&'a str,
expected:Str,
}
impl<'a,Str> StringCheckContext<'a,Str>
where
&'a str:PartialEq<Str>,
{
fn check<T>(self,value:T)->StringCheck<'a,T,Str>{
if self.observed==self.expected{
StringCheck(Ok(value))
}else{
StringCheck(Err(self))
}
}
}
impl<'a,Str:std::fmt::Display> std::fmt::Display for StringCheckContext<'a,Str>{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"expected: {}, observed: {}",self.expected,self.observed)
}
}
// check if a string is empty
pub struct StringEmpty;
impl std::fmt::Display for StringEmpty{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"Empty string")
}
}
fn check_empty(value:&str)->Result<&str,StringEmpty>{
(!value.is_empty()).then_some(value).ok_or(StringEmpty)
}
// check for duplicate objects
pub struct DuplicateCheckContext<ID,T>(HashMap<ID,T>);
pub struct DuplicateCheck<ID,T>(Result<(),DuplicateCheckContext<ID,T>>);
impl<ID,T> DuplicateCheckContext<ID,T>{
fn check(self,f:impl Fn(&T)->bool)->DuplicateCheck<ID,T>{
let Self(mut set)=self;
// remove correct entries
set.retain(|_,c|f(c));
// if any entries remain, they are incorrect
if set.is_empty(){
DuplicateCheck(Ok(()))
}else{
DuplicateCheck(Err(Self(set)))
}
}
}
// check that there is at least one matching item for each item in a reference set, and no extra items
pub struct SetDifferenceCheckContextAllowNone<ID,T>{
extra:HashMap<ID,T>,
}
pub struct SetDifferenceCheckContextAtLeastOne<ID,T>{
extra:HashMap<ID,T>,
missing:HashSet<ID>,
}
pub struct SetDifferenceCheck<Context>(Result<(),Context>);
impl<ID,T> SetDifferenceCheckContextAllowNone<ID,T>{
fn new(initial_set:HashMap<ID,T>)->Self{
Self{
extra:initial_set,
}
}
}
impl<ID:Eq+std::hash::Hash,T> SetDifferenceCheckContextAllowNone<ID,T>{
fn check<U>(mut self,reference_set:&HashMap<ID,U>)->SetDifferenceCheck<Self>{
// remove correct entries
for (id,_) in reference_set{
self.extra.remove(id);
}
// if any entries remain, they are incorrect
if self.extra.is_empty(){
SetDifferenceCheck(Ok(()))
}else{
SetDifferenceCheck(Err(self))
}
}
}
impl<ID,T> SetDifferenceCheckContextAtLeastOne<ID,T>{
fn new(initial_set:HashMap<ID,T>)->Self{
Self{
extra:initial_set,
missing:HashSet::new(),
}
}
}
impl<ID:Copy+Eq+std::hash::Hash,T> SetDifferenceCheckContextAtLeastOne<ID,T>{
fn check<U>(mut self,reference_set:&HashMap<ID,U>)->SetDifferenceCheck<Self>{
// remove correct entries
for (id,_) in reference_set{
if self.extra.remove(id).is_none(){
// the set did not contain a required item. This is a fail
self.missing.insert(*id);
}
}
// if any entries remain, they are incorrect
if self.extra.is_empty()&&self.missing.is_empty(){
SetDifferenceCheck(Ok(()))
}else{
SetDifferenceCheck(Err(self))
}
}
}
pub struct MapInfoOwned{
pub display_name:String,
pub creator:String,
pub game_id:GameID,
}
// crazy!
pub struct MapCheck<'a>{
// === METADATA CHECKS ===
// The root must be of class Model
model_class:StringCheck<'a,(),&'static str>,
// Model's name must be in snake case
model_name:StringCheck<'a,(),String>,
// Map must have a StringValue named DisplayName.
// Value must not be empty, must be in title case.
display_name:Result<Result<StringCheck<'a,&'a str,String>,StringEmpty>,StringValueError>,
// Map must have a StringValue named Creator.
// Value must not be empty.
creator:Result<Result<&'a str,StringEmpty>,StringValueError>,
// The prefix of the model's name must match the game it was submitted for.
// bhop_ for bhop, and surf_ for surf
game_id:Result<GameID,ParseGameIDError>,
// === MODE CHECKS ===
// MapStart must exist
mapstart:Result<(),()>,
// No duplicate map starts (including bonuses)
mode_start_counts:DuplicateCheck<ModeID,Vec<&'a str>>,
// At least one finish zone for each start zone, and no finishes with no start
mode_finish_counts:SetDifferenceCheck<SetDifferenceCheckContextAtLeastOne<ModeID,Vec<&'a str>>>,
// Check for dangling MapAnticheat zones (no associated MapStart)
mode_anticheat_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<ModeID,Vec<&'a str>>>,
// Spawn1 must exist
spawn1:Result<(),()>,
// Check for dangling Teleport# (no associated Spawn#)
teleport_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<SpawnID,Vec<&'a str>>>,
// No duplicate Spawn#
spawn_counts:DuplicateCheck<SpawnID,u64>,
// Check for dangling WormholeIn# (no associated WormholeOut#)
wormhole_in_counts:SetDifferenceCheck<SetDifferenceCheckContextAtLeastOne<WormholeID,u64>>,
// No duplicate WormholeOut# (duplicate WormholeIn# ok)
// No dangling WormholeOut#
wormhole_out_counts:DuplicateCheck<WormholeID,u64>,
}
impl<'a> ModelInfo<'a>{
fn check(self)->MapCheck<'a>{
// Check class is exactly "Model"
let model_class=StringCheckContext{
observed:self.model_class,
expected:"Model",
}.check(());
// Check model name is snake case
let model_name=StringCheckContext{
observed:self.model_name,
expected:self.model_name.to_snake_case(),
}.check(());
// Check display name is not empty and has title case
let display_name=self.map_info.display_name.map(|display_name|{
check_empty(display_name).map(|display_name|StringCheckContext{
observed:display_name,
expected:display_name.to_title_case(),
}.check(display_name))
});
// Check Creator is not empty
let creator=self.map_info.creator.map(check_empty);
// Check GameID (model name was prefixed with bhop_ surf_ etc)
let game_id=self.map_info.game_id;
// MapStart must exist
let mapstart=if self.counts.mode_start_counts.get(&ModeID::MAIN).is_some(){
Ok(())
}else{
Err(())
};
// Spawn1 must exist
let spawn1=if self.counts.spawn_counts.get(&SpawnID::FIRST).is_some(){
Ok(())
}else{
Err(())
};
// Check that at least one finish zone exists for each start zone.
// This also checks that there are no finish zones without a corresponding start zone.
let mode_finish_counts=SetDifferenceCheckContextAtLeastOne::new(self.counts.mode_finish_counts)
.check(&self.counts.mode_start_counts);
// Check that there are no anticheat zones without a corresponding start zone.
// Modes are allowed to have 0 anticheat zones.
let mode_anticheat_counts=SetDifferenceCheckContextAllowNone::new(self.counts.mode_anticheat_counts)
.check(&self.counts.mode_start_counts);
// There must be exactly one start zone for every mode in the map.
let mode_start_counts=DuplicateCheckContext(self.counts.mode_start_counts).check(|c|1<c.len());
// Check that there are no Teleports without a corresponding Spawn.
// Spawns are allowed to have 0 Teleports.
let teleport_counts=SetDifferenceCheckContextAllowNone::new(self.counts.teleport_counts)
.check(&self.counts.spawn_counts);
// There must be exactly one of any perticular spawn id in the map.
let spawn_counts=DuplicateCheckContext(self.counts.spawn_counts).check(|&c|1<c);
// Check that at least one WormholeIn exists for each WormholeOut.
// This also checks that there are no WormholeIn without a corresponding WormholeOut.
let wormhole_in_counts=SetDifferenceCheckContextAtLeastOne::new(self.counts.wormhole_in_counts)
.check(&self.counts.wormhole_out_counts);
// 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);
MapCheck{
model_class,
model_name,
display_name,
creator,
game_id,
mapstart,
mode_start_counts,
mode_finish_counts,
mode_anticheat_counts,
spawn1,
teleport_counts,
spawn_counts,
wormhole_in_counts,
wormhole_out_counts,
}
}
}
impl<'a> MapCheck<'a>{
fn pass(self)->Result<MapInfoOwned,Self>{
match self{
MapCheck{
model_class:StringCheck(Ok(())),
model_name:StringCheck(Ok(())),
display_name:Ok(Ok(StringCheck(Ok(display_name)))),
creator:Ok(Ok(creator)),
game_id:Ok(game_id),
mapstart:Ok(()),
mode_start_counts:DuplicateCheck(Ok(())),
mode_finish_counts:SetDifferenceCheck(Ok(())),
mode_anticheat_counts:SetDifferenceCheck(Ok(())),
spawn1:Ok(()),
teleport_counts:SetDifferenceCheck(Ok(())),
spawn_counts:DuplicateCheck(Ok(())),
wormhole_in_counts:SetDifferenceCheck(Ok(())),
wormhole_out_counts:DuplicateCheck(Ok(())),
}=>{
Ok(MapInfoOwned{
display_name:display_name.to_owned(),
creator:creator.to_owned(),
game_id,
})
},
other=>Err(other),
}
}
}
fn write_comma_separated<T>(
f:&mut std::fmt::Formatter<'_>,
mut it:impl Iterator<Item=T>,
custom_write:impl Fn(&mut std::fmt::Formatter<'_>,T)->std::fmt::Result
)->std::fmt::Result{
if let Some(t)=it.next(){
custom_write(f,t)?;
for t in it{
write!(f,", ")?;
custom_write(f,t)?;
}
}
Ok(())
}
macro_rules! write_zone{
($f:expr,$mode:expr,$zone:expr)=>{
match $mode{
ModeID(0)=>write!($f,concat!("Map",$zone)),
ModeID(1)=>write!($f,concat!("Bonus",$zone)),
ModeID(other)=>write!($f,concat!("Bonus{}",$zone),other),
}
};
}
impl<'a> std::fmt::Display for MapCheck<'a>{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
if let StringCheck(Err(context))=&self.model_class{
writeln!(f,"Invalid model class: {context}")?;
}
if let StringCheck(Err(context))=&self.model_name{
writeln!(f,"Model name must have snake_case: {context}")?;
}
match &self.display_name{
Ok(Ok(StringCheck(Ok(_))))=>(),
Ok(Ok(StringCheck(Err(context))))=>writeln!(f,"DisplayName must have Title Case: {context}")?,
Ok(Err(context))=>writeln!(f,"Invalid DisplayName: {context}")?,
Err(StringValueError::ObjectNotFound)=>writeln!(f,"Missing DisplayName StringValue")?,
Err(StringValueError::ValueNotSet)=>writeln!(f,"DisplayName Value not set")?,
Err(StringValueError::NonStringValue)=>writeln!(f,"DisplayName Value is not a String")?,
}
match &self.creator{
Ok(Ok(_))=>(),
Ok(Err(context))=>writeln!(f,"Invalid Creator: {context}")?,
Err(StringValueError::ObjectNotFound)=>writeln!(f,"Missing Creator StringValue")?,
Err(StringValueError::ValueNotSet)=>writeln!(f,"Creator Value not set")?,
Err(StringValueError::NonStringValue)=>writeln!(f,"Creator Value is not a String")?,
}
if let Err(_parse_game_id_error)=&self.game_id{
writeln!(f,"Model name must be prefixed with bhop_ surf_ or flytrials_")?;
}
if let Err(())=&self.mapstart{
writeln!(f,"Model has no MapStart")?;
}
if let DuplicateCheck(Err(DuplicateCheckContext(context)))=&self.mode_start_counts{
write!(f,"Duplicate start zones: ")?;
write_comma_separated(f,context.iter(),|f,(mode_id,names)|{
write_zone!(f,mode_id,"Start")?;
write!(f," ({} duplicates)",names.len())?;
Ok(())
})?;
writeln!(f,"")?;
}
if let SetDifferenceCheck(Err(context))=&self.mode_finish_counts{
if !context.extra.is_empty(){
let plural=if context.extra.len()==1{"zone"}else{"zones"};
write!(f,"No matching start zone for finish {plural}: ")?;
write_comma_separated(f,context.extra.iter(),|f,(mode_id,_names)|
write_zone!(f,mode_id,"Finish")
)?;
writeln!(f,"")?;
}
if !context.missing.is_empty(){
let plural=if context.missing.len()==1{"zone"}else{"zones"};
write!(f,"Missing finish {plural}: ")?;
write_comma_separated(f,context.missing.iter(),|f,mode_id|
write_zone!(f,mode_id,"Finish")
)?;
writeln!(f,"")?;
}
}
if let SetDifferenceCheck(Err(context))=&self.mode_anticheat_counts{
if !context.extra.is_empty(){
let plural=if context.extra.len()==1{"zone"}else{"zones"};
write!(f,"No matching start zone for anticheat {plural}: ")?;
write_comma_separated(f,context.extra.iter(),|f,(mode_id,_names)|
write_zone!(f,mode_id,"Anticheat")
)?;
writeln!(f,"")?;
}
}
if let Err(())=&self.spawn1{
writeln!(f,"Model has no Spawn1")?;
}
if let SetDifferenceCheck(Err(context))=&self.teleport_counts{
for (_,names) in &context.extra{
let plural=if names.len()==1{"object"}else{"objects"};
write!(f,"No matching Spawn for {plural}: ")?;
write_comma_separated(f,names.iter(),|f,&name|{
write!(f,"{name}")
})?;
writeln!(f,"")?;
}
}
if let DuplicateCheck(Err(DuplicateCheckContext(context)))=&self.spawn_counts{
write!(f,"Duplicate Spawn: ")?;
write_comma_separated(f,context.iter(),|f,(SpawnID(spawn_id),count)|
write!(f,"Spawn{spawn_id} ({count} duplicates)")
)?;
writeln!(f,"")?;
}
if let SetDifferenceCheck(Err(context))=&self.wormhole_in_counts{
if !context.extra.is_empty(){
write!(f,"WormholeIn with no matching WormholeOut: ")?;
write_comma_separated(f,context.extra.iter(),|f,(WormholeID(wormhole_id),_count)|
write!(f,"WormholeIn{wormhole_id}")
)?;
writeln!(f,"")?;
}
if !context.missing.is_empty(){
// This counts WormholeIn objects, but
// flipped logic is easier to understand
write!(f,"WormholeOut with no matching WormholeIn: ")?;
write_comma_separated(f,context.missing.iter(),|f,WormholeID(wormhole_id)|
write!(f,"WormholeOut{wormhole_id}")
)?;
writeln!(f,"")?;
}
}
if let DuplicateCheck(Err(DuplicateCheckContext(context)))=&self.wormhole_out_counts{
write!(f,"Duplicate WormholeOut: ")?;
write_comma_separated(f,context.iter(),|f,(WormholeID(wormhole_id),count)|
write!(f,"WormholeOut{wormhole_id} ({count} duplicates)")
)?;
writeln!(f,"")?;
}
Ok(())
}
}
pub struct CheckReportAndVersion{
pub status:Result<MapInfoOwned,String>,
pub version:u64,
}
impl crate::message_handler::MessageHandler{
pub async fn check_inner(&self,check_info:CheckRequest)->Result<CheckReportAndVersion,Error>{
// discover asset creator and latest version
let info=self.cloud_context.get_asset_info(
rbx_asset::cloud::GetAssetLatestRequest{asset_id:check_info.ModelID}
).await.map_err(Error::ModelInfoDownload)?;
// reject models created by a group
let rbx_asset::cloud::Creator::userId(_user_id)=info.creationContext.creator else{
return Err(Error::CreatorTypeMustBeUser);
};
// parse model version string
let version=info.revisionId;
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
asset_id:check_info.ModelID,
version,
}).await.map_err(Error::Download)?;
// decode dom (slow!)
let dom=maybe_gzip.read_with(read_dom,read_dom).map_err(Error::ModelFileDecode)?;
// extract the root instance
let model_instance=get_root_instance(&dom).map_err(Error::GetRootInstance)?;
// extract information from the model
let model_info=get_model_info(&dom,model_instance);
// convert the model information into a structured report
let map_check=model_info.check();
// check the report, generate an error message if it fails the check
let status=map_check.pass().map_err(|e|e.to_string());
Ok(CheckReportAndVersion{status,version})
}
}

View File

@@ -0,0 +1,52 @@
use crate::check::CheckReportAndVersion;
use crate::nats_types::CheckMapfixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Check(crate::check::Error),
ApiActionMapfixCheck(submissions_api::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl crate::message_handler::MessageHandler{
pub async fn check_mapfix(&self,check_info:CheckMapfixRequest)->Result<(),Error>{
let mapfix_id=check_info.MapfixID;
let check_result=self.check_inner(check_info.into()).await;
// 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)?,
// 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,
}
).await.map_err(Error::ApiActionMapfixCheck)?,
// TODO: report the error
// update the mapfix model status to request changes
Err(e)=>self.api.action_mapfix_request_changes(
submissions_api::types::ActionMapfixRequestChangesRequest{
MapfixID:mapfix_id,
ErrorMessage:e.to_string(),
}
).await.map_err(Error::ApiActionMapfixCheck)?,
}
Ok(())
}
}

View File

@@ -0,0 +1,53 @@
use crate::check::CheckReportAndVersion;
use crate::nats_types::CheckSubmissionRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Check(crate::check::Error),
ApiActionSubmissionCheck(submissions_api::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl crate::message_handler::MessageHandler{
pub async fn check_submission(&self,check_info:CheckSubmissionRequest)->Result<(),Error>{
let submission_id=check_info.SubmissionID;
let check_result=self.check_inner(check_info.into()).await;
// 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)?,
// 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,
}
).await.map_err(Error::ApiActionSubmissionCheck)?,
// TODO: report the error
// update the submission model status to request changes
Err(e)=>self.api.action_submission_request_changes(
submissions_api::types::ActionSubmissionRequestChangesRequest{
SubmissionID:submission_id,
ErrorMessage:e.to_string(),
}
).await.map_err(Error::ApiActionSubmissionCheck)?,
}
Ok(())
}
}

View File

@@ -1,18 +1,14 @@
use crate::rbx_util::{get_mapinfo,read_dom,MapInfo,ReadDomError,GetMapInfoError,ParseGameIDError};
use crate::download::download_asset_version;
use crate::rbx_util::{get_root_instance,get_mapinfo,read_dom,MapInfo,ReadDomError,GetRootInstanceError,GameID};
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
CreatorTypeMustBeUser,
ModelInfoDownload(rbx_asset::cloud::GetError),
ModelLocationDownload(rbx_asset::cloud::GetError),
NonFreeModel,
ModelFileDownload(rbx_asset::cloud::GetError),
ParseUserID(core::num::ParseIntError),
ParseModelVersion(core::num::ParseIntError),
Download(crate::download::Error),
ModelFileDecode(ReadDomError),
GetMapInfo(GetMapInfoError),
ParseGameID(ParseGameIDError),
GetRootInstance(GetRootInstanceError),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@@ -27,10 +23,10 @@ pub struct CreateRequest{
}
#[allow(nonstandard_style)]
pub struct CreateResult{
pub AssetOwner:i64,
pub DisplayName:String,
pub Creator:String,
pub GameID:i32,
pub AssetOwner:u64,
pub DisplayName:Option<String>,
pub Creator:Option<String>,
pub GameID:Option<GameID>,
pub AssetVersion:u64,
}
impl crate::message_handler::MessageHandler{
@@ -41,45 +37,36 @@ impl crate::message_handler::MessageHandler{
).await.map_err(Error::ModelInfoDownload)?;
// reject models created by a group
let rbx_asset::cloud::Creator::userId(user_id_string)=info.creationContext.creator else{
let rbx_asset::cloud::Creator::userId(user_id)=info.creationContext.creator else{
return Err(Error::CreatorTypeMustBeUser);
};
// parse user string and model version string
let user_id:u64=user_id_string.parse().map_err(Error::ParseUserID)?;
let asset_version=info.revisionId.parse().map_err(Error::ParseModelVersion)?;
// download the location of the map model
let location=self.cloud_context.get_asset_version_location(rbx_asset::cloud::GetAssetVersionRequest{
asset_id:create_info.ModelID,
version:asset_version,
}).await.map_err(Error::ModelLocationDownload)?;
// if the location does not exist, you are not allowed to donwload it
let Some(location)=location.location else{
return Err(Error::NonFreeModel);
};
let asset_version=info.revisionId;
// download the map model
let model_data=self.cloud_context.get_asset(&location).await.map_err(Error::ModelFileDownload)?;
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
asset_id:create_info.ModelID,
version:asset_version,
}).await.map_err(Error::Download)?;
// decode dom (slow!)
let dom=read_dom(std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?;
let dom=maybe_gzip.read_with(read_dom,read_dom).map_err(Error::ModelFileDecode)?;
// extract the root instance
let model_instance=get_root_instance(&dom).map_err(Error::GetRootInstance)?;
// parse create fields out of asset
let MapInfo{
display_name,
creator,
game_id,
}=get_mapinfo(&dom).map_err(Error::GetMapInfo)?;
let game_id=game_id.map_err(Error::ParseGameID)?;
}=get_mapinfo(&dom,model_instance);
Ok(CreateResult{
AssetOwner:user_id as i64,
DisplayName:display_name.unwrap_or_default().to_owned(),
Creator:creator.unwrap_or_default().to_owned(),
GameID:game_id as i32,
AssetOwner:user_id,
DisplayName:display_name.ok().map(ToOwned::to_owned),
Creator:creator.ok().map(ToOwned::to_owned),
GameID:game_id.ok(),
AssetVersion:asset_version,
})
}

View File

@@ -24,13 +24,15 @@ impl crate::message_handler::MessageHandler{
// call create on api
self.api.create_mapfix(submissions_api::types::CreateMapfixRequest{
OperationID:create_info.OperationID,
AssetOwner:create_request.AssetOwner,
DisplayName:create_request.DisplayName.as_str(),
Creator:create_request.Creator.as_str(),
GameID:create_request.GameID,
AssetOwner:create_request.AssetOwner as i64,
DisplayName:create_request.DisplayName.as_deref().unwrap_or_default(),
Creator:create_request.Creator.as_deref().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(),
}).await.map_err(Error::ApiActionMapfixCreate)?;
Ok(())
@@ -43,7 +45,7 @@ impl crate::message_handler::MessageHandler{
if let Err(e)=create_result{
self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{
OperationID:operation_id,
StatusMessage:format!("{e}"),
StatusMessage:e.to_string(),
}).await?;
}

View File

@@ -1,5 +1,6 @@
use crate::nats_types::CreateSubmissionRequest;
use crate::create::CreateRequest;
use crate::rbx_util::GameID;
#[allow(dead_code)]
#[derive(Debug)]
@@ -19,15 +20,33 @@ impl crate::message_handler::MessageHandler{
let create_request=self.create_inner(CreateRequest{
ModelID:create_info.ModelID,
}).await.map_err(Error::Create)?;
// 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()
}else{
create_info.DisplayName.as_str()
};
let creator=if create_info.Creator.is_empty(){
create_request.Creator.as_deref().unwrap_or_default()
}else{
create_info.Creator.as_str()
};
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,
DisplayName:create_request.DisplayName.as_str(),
Creator:create_request.Creator.as_str(),
GameID:create_request.GameID,
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,
}).await.map_err(Error::ApiActionSubmissionCreate)?;
Ok(())
@@ -40,7 +59,7 @@ impl crate::message_handler::MessageHandler{
if let Err(e)=create_result{
self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{
OperationID:operation_id,
StatusMessage:format!("{e}"),
StatusMessage:e.to_string(),
}).await?;
}

View File

@@ -0,0 +1,26 @@
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
ModelLocationDownload(rbx_asset::cloud::GetError),
NonFreeModel,
ModelFileDownload(rbx_asset::cloud::GetError),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
pub async fn download_asset_version(cloud_context:&rbx_asset::cloud::Context,request:rbx_asset::cloud::GetAssetVersionRequest)->Result<rbx_asset::types::MaybeGzippedBytes,Error>{
// download the location of the map model
let location=cloud_context.get_asset_version_location(request).await.map_err(Error::ModelLocationDownload)?;
// if the location does not exist, you are not allowed to download it
let location=location.location.ok_or(Error::NonFreeModel)?;
// download the map model
let maybe_gzip=cloud_context.get_asset(&location).await.map_err(Error::ModelFileDownload)?;
Ok(maybe_gzip)
}

View File

@@ -4,6 +4,10 @@ mod rbx_util;
mod message_handler;
mod nats_types;
mod types;
mod download;
mod check;
mod check_mapfix;
mod check_submission;
mod create;
mod create_mapfix;
mod create_submission;

View File

@@ -7,6 +7,8 @@ pub enum HandleMessageError{
UnknownSubject(String),
CreateMapfix(submissions_api::Error),
CreateSubmission(submissions_api::Error),
CheckMapfix(crate::check_mapfix::Error),
CheckSubmission(crate::check_submission::Error),
UploadMapfix(crate::upload_mapfix::Error),
UploadSubmission(crate::upload_submission::Error),
ValidateMapfix(crate::validate_mapfix::Error),
@@ -52,6 +54,8 @@ impl MessageHandler{
match message.subject.as_str(){
"maptest.mapfixes.create"=>self.create_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::CreateMapfix),
"maptest.submissions.create"=>self.create_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::CreateSubmission),
"maptest.mapfixes.check"=>self.check_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::CheckMapfix),
"maptest.submissions.check"=>self.check_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::CheckSubmission),
"maptest.mapfixes.upload"=>self.upload_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadMapfix),
"maptest.submissions.upload"=>self.upload_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadSubmission),
"maptest.mapfixes.validate"=>self.validate_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateMapfix),

View File

@@ -10,6 +10,12 @@ pub struct CreateSubmissionRequest{
// operation_id is passed back in the response message
pub OperationID:i32,
pub ModelID:u64,
pub DisplayName:String,
pub Creator:String,
pub GameID:u32,
// initial status is passed back on create
pub Status:u32,
pub Roles:u32,
}
#[allow(nonstandard_style)]
@@ -18,6 +24,21 @@ pub struct CreateMapfixRequest{
pub OperationID:i32,
pub ModelID:u64,
pub TargetAssetID:u64,
pub Description:String,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct CheckSubmissionRequest{
pub SubmissionID:i64,
pub ModelID:u64,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct CheckMapfixRequest{
pub MapfixID:i64,
pub ModelID:u64,
}
#[allow(nonstandard_style)]

View File

@@ -5,8 +5,7 @@ pub enum ReadDomError{
Binary(rbx_binary::DecodeError),
Xml(rbx_xml::DecodeError),
Read(std::io::Error),
Seek(std::io::Error),
UnknownFormat([u8;8]),
UnknownFormat(Vec<u8>),
}
impl std::fmt::Display for ReadDomError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@@ -15,19 +14,13 @@ impl std::fmt::Display for ReadDomError{
}
impl std::error::Error for ReadDomError{}
pub fn read_dom<R:std::io::Read+std::io::Seek>(mut input:R)->Result<rbx_dom_weak::WeakDom,ReadDomError>{
let mut first_8=[0u8;8];
std::io::Read::read_exact(&mut input,&mut first_8).map_err(ReadDomError::Read)?;
std::io::Seek::rewind(&mut input).map_err(ReadDomError::Seek)?;
match &first_8[0..4]{
b"<rob"=>{
match &first_8[4..8]{
b"lox!"=>rbx_binary::from_reader(input).map_err(ReadDomError::Binary),
b"lox "=>rbx_xml::from_reader(input,rbx_xml::DecodeOptions::default()).map_err(ReadDomError::Xml),
_=>Err(ReadDomError::UnknownFormat(first_8)),
}
},
_=>Err(ReadDomError::UnknownFormat(first_8)),
pub fn read_dom<R:std::io::Read>(input:R)->Result<rbx_dom_weak::WeakDom,ReadDomError>{
let mut buf=std::io::BufReader::new(input);
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadDomError::Read)?;
match peek.get(0..8){
Some(b"<roblox!")=>rbx_binary::from_reader(buf).map_err(ReadDomError::Binary),
Some(b"<roblox ")=>rbx_xml::from_reader_default(buf).map_err(ReadDomError::Xml),
_=>Err(ReadDomError::UnknownFormat(peek.to_owned())),
}
}
@@ -77,6 +70,18 @@ impl std::str::FromStr for GameID{
return Err(ParseGameIDError);
}
}
pub struct GameIDError;
impl TryFrom<u32> for GameID{
type Error=GameIDError;
fn try_from(value:u32)->Result<Self,Self::Error>{
match value{
1=>Ok(GameID::Bhop),
2=>Ok(GameID::Surf),
5=>Ok(GameID::FlyTrials),
_=>Err(GameIDError)
}
}
}
pub struct MapInfo<'a>{
pub display_name:Result<&'a str,StringValueError>,
@@ -84,6 +89,7 @@ pub struct MapInfo<'a>{
pub game_id:Result<GameID,ParseGameIDError>,
}
#[derive(Debug)]
pub enum StringValueError{
ObjectNotFound,
ValueNotSet,
@@ -100,20 +106,24 @@ fn string_value(instance:Option<&rbx_dom_weak::Instance>)->Result<&str,StringVal
}
#[derive(Debug)]
pub enum GetMapInfoError{
pub enum GetRootInstanceError{
ModelFileRootMustHaveOneChild,
ModelFileChildRefIsNil,
}
pub fn get_mapinfo(dom:&rbx_dom_weak::WeakDom)->Result<MapInfo,GetMapInfoError>{
pub fn get_root_instance(dom:&rbx_dom_weak::WeakDom)->Result<&rbx_dom_weak::Instance,GetRootInstanceError>{
let &[map_ref]=dom.root().children()else{
return Err(GetMapInfoError::ModelFileRootMustHaveOneChild);
return Err(GetRootInstanceError::ModelFileRootMustHaveOneChild);
};
let model_instance=dom.get_by_ref(map_ref).ok_or(GetMapInfoError::ModelFileChildRefIsNil)?;
let model_instance=dom.get_by_ref(map_ref).ok_or(GetRootInstanceError::ModelFileChildRefIsNil)?;
Ok(MapInfo{
Ok(model_instance)
}
pub fn get_mapinfo<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&rbx_dom_weak::Instance)->MapInfo<'a>{
MapInfo{
display_name:string_value(find_first_child_class(dom,model_instance,"DisplayName","StringValue")),
creator:string_value(find_first_child_class(dom,model_instance,"Creator","StringValue")),
game_id:model_instance.name.parse(),
})
}
}

View File

@@ -1,11 +1,11 @@
use crate::download::download_asset_version;
use crate::nats_types::UploadMapfixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
GetLocation(rbx_asset::cloud::GetError),
NonFreeModel,
Get(rbx_asset::cloud::GetError),
Download(crate::download::Error),
IO(std::io::Error),
Json(serde_json::Error),
Upload(rbx_asset::cookie::UploadError),
ApiActionMapfixUploaded(submissions_api::Error),
@@ -19,19 +19,14 @@ impl std::error::Error for Error{}
impl crate::message_handler::MessageHandler{
pub async fn upload_mapfix(&self,upload_info:UploadMapfixRequest)->Result<(),Error>{
// download the location of the map model
let location=self.cloud_context.get_asset_version_location(rbx_asset::cloud::GetAssetVersionRequest{
// download the map model
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
asset_id:upload_info.ModelID,
version:upload_info.ModelVersion,
}).await.map_err(Error::GetLocation)?;
}).await.map_err(Error::Download)?;
// if the location does not exist, you are not allowed to donwload it
let Some(location)=location.location else{
return Err(Error::NonFreeModel);
};
// download the map model
let model_data=self.cloud_context.get_asset(&location).await.map_err(Error::Get)?;
// transparently handle gzipped models
let model_data=maybe_gzip.to_vec().map_err(Error::IO)?;
// upload the map to the strafesnet group
let _upload_response=self.cookie_context.upload(rbx_asset::cookie::UploadRequest{

View File

@@ -1,11 +1,11 @@
use crate::download::download_asset_version;
use crate::nats_types::UploadSubmissionRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
GetLocation(rbx_asset::cloud::GetError),
NonFreeModel,
Get(rbx_asset::cloud::GetError),
Download(crate::download::Error),
IO(std::io::Error),
Json(serde_json::Error),
Create(rbx_asset::cookie::CreateError),
SystemTime(std::time::SystemTimeError),
@@ -20,19 +20,14 @@ impl std::error::Error for Error{}
impl crate::message_handler::MessageHandler{
pub async fn upload_submission(&self,upload_info:UploadSubmissionRequest)->Result<(),Error>{
// download the location of the map model
let location=self.cloud_context.get_asset_version_location(rbx_asset::cloud::GetAssetVersionRequest{
// download the map model
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
asset_id:upload_info.ModelID,
version:upload_info.ModelVersion,
}).await.map_err(Error::GetLocation)?;
}).await.map_err(Error::Download)?;
// if the location does not exist, you are not allowed to donwload it
let Some(location)=location.location else{
return Err(Error::NonFreeModel);
};
// download the map model
let model_data=self.cloud_context.get_asset(&location).await.map_err(Error::Get)?;
// transparently handle gzipped models
let model_data=maybe_gzip.to_vec().map_err(Error::IO)?;
// upload the map to the strafesnet group
let upload_response=self.cookie_context.create(rbx_asset::cookie::CreateRequest{

View File

@@ -29,7 +29,7 @@ impl crate::message_handler::MessageHandler{
// update the mapfix model status to accepted
self.api.action_mapfix_accepted(submissions_api::types::ActionMapfixAcceptedRequest{
MapfixID:mapfix_id,
StatusMessage:format!("{e}"),
ErrorMessage:e.to_string(),
}).await.map_err(Error::ApiActionMapfixValidate)?;
},
}

View File

@@ -29,7 +29,7 @@ impl crate::message_handler::MessageHandler{
// update the submission model status to accepted
self.api.action_submission_accepted(submissions_api::types::ActionSubmissionAcceptedRequest{
SubmissionID:submission_id,
StatusMessage:format!("{e}"),
ErrorMessage:e.to_string(),
}).await.map_err(Error::ApiActionSubmissionValidate)?;
},
}

View File

@@ -1,6 +1,7 @@
use futures::TryStreamExt;
use submissions_api::types::ResourceType;
use crate::download::download_asset_version;
use crate::rbx_util::{class_is_a,read_dom,ReadDomError};
use crate::types::ResourceID;
@@ -36,9 +37,7 @@ pub enum Error{
ScriptFlaggedIllegalKeyword(String),
ScriptBlocked(Option<submissions_api::types::ScriptID>),
ScriptNotYetReviewed(Option<submissions_api::types::ScriptID>),
ModelLocationDownload(rbx_asset::cloud::GetError),
NonFreeModel,
ModelFileDownload(rbx_asset::cloud::GetError),
Download(crate::download::Error),
ModelFileDecode(ReadDomError),
ApiGetScriptPolicyFromHash(submissions_api::types::SingleItemError),
ApiGetScript(submissions_api::Error),
@@ -91,22 +90,14 @@ impl From<crate::nats_types::ValidateSubmissionRequest> for ValidateRequest{
impl crate::message_handler::MessageHandler{
pub async fn validate_inner(&self,validate_info:ValidateRequest)->Result<(),Error>{
// download the location of the map model
let location=self.cloud_context.get_asset_version_location(rbx_asset::cloud::GetAssetVersionRequest{
// download the map model
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
asset_id:validate_info.ModelID,
version:validate_info.ModelVersion,
}).await.map_err(Error::ModelLocationDownload)?;
// if the location does not exist, you are not allowed to donwload it
let Some(location)=location.location else{
return Err(Error::NonFreeModel);
};
// download the map model
let model_data=self.cloud_context.get_asset(&location).await.map_err(Error::ModelFileDownload)?;
}).await.map_err(Error::Download)?;
// decode dom (slow!)
let mut dom=read_dom(std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?;
let mut dom=maybe_gzip.read_with(read_dom,read_dom).map_err(Error::ModelFileDecode)?;
/* VALIDATE MAP */

View File

@@ -1,6 +1,11 @@
"use client"
import Link from "next/link"
import Image from "next/image";
import "./styles/header.scss"
import { UserInfo } from "@/app/ts/User";
import { useState, useEffect } from "react";
interface HeaderButton {
name: string,
@@ -15,6 +20,25 @@ function HeaderButton(header: HeaderButton) {
}
export default function Header() {
const handleLoginClick = () => {
window.location.href = "/auth/oauth2/login?redirect=" + window.location.href;
};
const [valid, setValid] = useState<boolean>(false)
const [user, setUser] = useState<UserInfo | null>(null)
useEffect(() => {
async function getLoginInfo() {
const [validateData, userData] = await Promise.all([
fetch("/api/session/validate").then(validateResponse => validateResponse.json()),
fetch("/api/session/user").then(userResponse => userResponse.json())
]);
setValid(validateData)
setUser(userData)
}
getLoginInfo()
}, [])
return (
<header className="header-bar">
<nav className="left">
@@ -24,6 +48,16 @@ export default function Header() {
</nav>
<nav className="right">
<HeaderButton name="Submit" href="/submit"/>
{valid && user ? (
<div className="author">
<Link href="/auth">
<Image className="avatar" width={28} height={28} priority={true} src={user.AvatarURL} alt={user.Username}/>
<button>{user.Username}</button>
</Link>
</div>
) : (
<button onClick={handleLoginClick}>Login</button>
)}
</nav>
</header>
)

View File

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

View File

@@ -0,0 +1,54 @@
@use "../../globals.scss";
::placeholder {
color: var(--placeholder-text)
}
.form-spacer {
margin-bottom: 20px;
&:last-of-type {
margin-top: 15px;
}
}
#target-asset-radio {
color: var(--text-color);
font-size: globals.$form-label-fontsize;
}
.form-field {
width: 850px;
& label, & input {
color: var(--text-color);
}
& fieldset {
border-color: rgb(100,100,100);
}
& span {
color: white;
}
}
main {
display: grid;
justify-content: center;
align-items: center;
margin-inline: auto;
width: 700px;
}
header h1 {
text-align: center;
color: var(--text-color);
}
form {
display: grid;
gap: 25px;
fieldset {
border: blue
}
}

View File

@@ -0,0 +1,65 @@
import { FormControl, Select, InputLabel, MenuItem } from "@mui/material";
import { styled } from '@mui/material/styles';
import InputBase from '@mui/material/InputBase';
import React from "react";
import { SelectChangeEvent } from "@mui/material";
// TODO: Properly style everything instead of pasting 🤚
type GameSelectionProps = {
game: number;
setGame: React.Dispatch<React.SetStateAction<number>>;
};
const BootstrapInput = styled(InputBase)(({ theme }) => ({
'label + &': {
marginTop: theme.spacing(3),
},
'& .MuiInputBase-input': {
backgroundColor: '#0000',
color: '#FFF',
border: '1px solid rgba(175, 175, 175, 0.66)',
fontSize: 16,
padding: '10px 26px 10px 12px',
transition: theme.transitions.create(['border-color', 'box-shadow']),
fontFamily: [
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'"Helvetica Neue"',
'Arial',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
].join(','),
'&:focus': {
borderRadius: 4,
borderColor: '#80bdff',
boxShadow: '0 0 0 0.2rem rgba(0,123,255,.25)',
},
},
}));
export default function GameSelection({ game, setGame }: GameSelectionProps) {
const handleChange = (event: SelectChangeEvent) => {
setGame(Number(event.target.value)); // TODO: Change later!! there's 100% a proper way of doing this
};
return (
<FormControl>
<InputLabel sx={{ color: "#646464" }}>Game</InputLabel>
<Select
value={String(game)}
label="Game"
onChange={handleChange}
input={<BootstrapInput />}
>
<MenuItem value={1}>Bhop</MenuItem>
<MenuItem value={2}>Surf</MenuItem>
<MenuItem value={3}>Fly Trials</MenuItem>
</Select>
</FormControl>
);
}

View File

@@ -0,0 +1,90 @@
"use client"
import { Button, TextField } from "@mui/material"
import GameSelection from "./_game";
import SendIcon from '@mui/icons-material/Send';
import Webpage from "@/app/_components/webpage"
import React, { useState } from "react";
import "./(styles)/page.scss"
interface SubmissionPayload {
AssetID: number;
DisplayName: string;
Creator: string;
GameID: number;
}
interface IdResponse {
OperationID: number;
}
export default function SubmissionInfoPage() {
const [game, setGame] = useState(1);
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const form = event.currentTarget;
const formData = new FormData(form);
const payload: SubmissionPayload = {
DisplayName: (formData.get("display-name") as string) ?? "unknown", // TEMPORARY! TODO: Change
Creator: (formData.get("creator") as string) ?? "unknown", // TEMPORARY! TODO: Change
GameID: game,
AssetID: Number((formData.get("asset-id") as string) ?? "0"),
};
console.log(payload)
console.log(JSON.stringify(payload))
try {
// Send the POST request
const response = await fetch("/api/submissions-admin", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
// Check if the HTTP request was successful
if (!response.ok) {
const errorDetails = await response.text();
// Throw an error with detailed information
throw new Error(`HTTP error! status: ${response.status}, details: ${errorDetails}`);
}
// Allow any HTTP status
const id_response:IdResponse = await response.json();
// navigate to newly created submission
window.location.assign(`/operations/${id_response.OperationID}`)
} catch (error) {
console.error("Error submitting data:", error);
}
};
return (
<Webpage>
<main>
<header>
<h1>Submit New Map on Behalf of Another User (Admin)</h1>
<span className="spacer form-spacer"></span>
</header>
<form onSubmit={handleSubmit}>
<TextField className="form-field" id="asset-id" name="asset-id" label="Asset ID (required)" variant="outlined"/>
<TextField className="form-field" id="display-name" name="display-name" label="Display Name" variant="outlined"/>
<TextField className="form-field" id="creator" name="creator" label="Creator" variant="outlined"/>
<GameSelection game={game} setGame={setGame} />
<span className="spacer form-spacer"></span>
<Button type="submit" variant="contained" startIcon={<SendIcon/>} sx={{
width: "400px",
height: "50px",
marginInline: "auto"
}}>Create Submission in ChangesRequested Status (Ready to Force-Submit)</Button>
</form>
</main>
</Webpage>
)
}

View File

@@ -12,9 +12,9 @@ interface CreatorAndReviewStatus {
asset_id: MapfixInfo["AssetID"],
creator: MapfixInfo["DisplayName"],
review: MapfixInfo["StatusID"],
status_message: MapfixInfo["StatusMessage"],
submitter: MapfixInfo["Submitter"],
target_asset_id: MapfixInfo["TargetAssetID"],
description: MapfixInfo["Description"],
comments: Comment[],
name: string
}
@@ -52,7 +52,7 @@ function LeaveAComment() {
)
}
export default function Comments(stats: CommentersProps) {
export function Comments(stats: CommentersProps) {
return (<>
<section className="comments">
{stats.comments_data.comments.length===0
@@ -66,5 +66,6 @@ export default function Comments(stats: CommentersProps) {
}
export {
type CreatorAndReviewStatus
type CreatorAndReviewStatus,
type Comment,
}

View File

@@ -10,6 +10,7 @@ interface ReviewAction {
const ReviewActions = {
Submit: {name:"Submit",action:"trigger-submit"} as ReviewAction,
ResetSubmitting: {name:"Reset Submitting (fix softlocked status)",action:"reset-submitting"} as ReviewAction,
Revoke: {name:"Revoke",action:"revoke"} as ReviewAction,
Accept: {name:"Accept",action:"trigger-validate"} as ReviewAction,
Reject: {name:"Reject",action:"reject"} as ReviewAction,
@@ -112,6 +113,9 @@ export default function ReviewButtons(props: ReviewId) {
if ([MapfixStatus.Submitted, MapfixStatus.ChangesRequested].includes(mapfixStatus!)) {
visibleButtons.push({ action: ReviewActions.Revoke, color: "info", mapfixId });
}
if (mapfixStatus === MapfixStatus.Submitting && mapfix.UpdatedAt<Math.floor(Date.now() / 1000)) {
visibleButtons.push({ action: ReviewActions.ResetSubmitting, color: "error", mapfixId });
}
}
if (roles&RolesConstants.MapfixReview) {

View File

@@ -5,7 +5,8 @@ import type { CreatorAndReviewStatus } from "./_comments";
import { MapImage } from "./_mapImage";
import { useParams } from "next/navigation";
import ReviewButtons from "./_reviewButtons";
import Comments from "./_comments";
import { Comments, Comment } from "./_comments";
import { AuditEvent, decodeAuditEvent as auditEventMessage } from "@/app/ts/AuditEvent";
import Webpage from "@/app/_components/webpage";
import Link from "next/link";
import { useState, useEffect } from "react";
@@ -54,7 +55,7 @@ function TitleAndComments(stats: CreatorAndReviewStatus) {
<p className="submitter">Submitter {stats.submitter}</p>
<p className="asset-id">Model Asset ID {stats.asset_id}</p>
<p className="target-asset-id">Target Asset ID {stats.target_asset_id}</p>
<p className="status-message">Validation Error: {stats.status_message}</p>
<p className="description">Description: {stats.description}</p>
<span className="spacer"></span>
<Comments comments_data={stats}/>
</main>
@@ -62,19 +63,42 @@ function TitleAndComments(stats: CreatorAndReviewStatus) {
}
export default function MapfixInfoPage() {
const dynamicId = useParams<{mapfixId: string}>()
const { mapfixId } = useParams < { mapfixId: string } >()
const [mapfix, setMapfix] = useState<MapfixInfo | null>(null)
const [auditEvents, setAuditEvents] = useState<AuditEvent[]>([])
useEffect(() => { // needs to be client sided since server doesn't have a session, nextjs got mad at me for exporting an async function: (https://nextjs.org/docs/messages/no-async-client-component)
async function getMapfix() {
const res = await fetch(`/api/mapfixes/${dynamicId.mapfixId}`)
const res = await fetch(`/api/mapfixes/${mapfixId}`)
if (res.ok) {
setMapfix(await res.json())
}
}
getMapfix()
}, [dynamicId.mapfixId])
async function getAuditEvents() {
const res = await fetch(`/api/mapfixes/${mapfixId}/audit-events?Page=1&Limit=100`)
if (res.ok) {
setAuditEvents(await res.json())
}
}
getMapfix()
getAuditEvents()
}, [mapfixId])
const comments:Comment[] = auditEvents.map((auditEvent) => {
let username = auditEvent.Username;
if (auditEvent.User == 9223372036854776000) {
username = "[Validator]";
}
if (username === "" && mapfix && auditEvent.User == mapfix.Submitter) {
username = "[Submitter]";
}
return {
date: auditEvent.CreatedAt,
name: username,
comment: auditEventMessage(auditEvent),
}
})
if (!mapfix) {
return <Webpage>
@@ -85,8 +109,17 @@ export default function MapfixInfoPage() {
<Webpage>
<main className="map-page-main">
<section className="review-section">
<RatingArea mapfixId={dynamicId.mapfixId} mapfixStatus={mapfix.StatusID} mapfixSubmitter={mapfix.Submitter} mapfixAssetId={mapfix.AssetID} mapfixTargetAssetId={mapfix.TargetAssetID} />
<TitleAndComments name={mapfix.DisplayName} creator={mapfix.Creator} review={mapfix.StatusID} status_message={mapfix.StatusMessage} asset_id={mapfix.AssetID} submitter={mapfix.Submitter} target_asset_id={mapfix.TargetAssetID} comments={[]}/>
<RatingArea mapfixId={mapfixId} mapfixStatus={mapfix.StatusID} mapfixSubmitter={mapfix.Submitter} mapfixAssetId={mapfix.AssetID} mapfixTargetAssetId={mapfix.TargetAssetID} />
<TitleAndComments
name={mapfix.DisplayName}
creator={mapfix.Creator}
review={mapfix.StatusID}
asset_id={mapfix.AssetID}
submitter={mapfix.Submitter}
target_asset_id={mapfix.TargetAssetID}
description={mapfix.Description}
comments={comments}
/>
</section>
</main>
</Webpage>

View File

@@ -1,6 +1,6 @@
'use client'
import React, { useState, useEffect } from "react";
import { useState, useEffect } from "react";
import { MapfixList } from "../ts/Mapfix";
import { MapfixCard } from "../_components/mapCard";
import Webpage from "@/app/_components/webpage";

View File

@@ -11,6 +11,7 @@ import "./(styles)/page.scss"
interface MapfixPayload {
AssetID: number;
TargetAssetID: number;
Description: string;
}
interface IdResponse {
OperationID: number;
@@ -28,6 +29,7 @@ export default function MapfixInfoPage() {
const payload: MapfixPayload = {
AssetID: Number((formData.get("asset-id") as string) ?? "0"),
TargetAssetID: Number(dynamicId.mapId),
Description: (formData.get("description") as string) ?? "unknown", // TEMPORARY! TODO: Change
};
console.log(payload)
@@ -70,12 +72,13 @@ export default function MapfixInfoPage() {
<form onSubmit={handleSubmit}>
{/* TODO: Add form data for mapfixes, such as changes they did, and any times that need to be deleted & what styles */}
<TextField className="form-field" id="asset-id" name="asset-id" label="Asset ID" variant="outlined"/>
<TextField className="form-field" id="description" name="description" label="Describe the Mapfix" variant="outlined"/>
<span className="spacer form-spacer"></span>
<Button type="submit" variant="contained" startIcon={<SendIcon/>} sx={{
width: "400px",
height: "50px",
marginInline: "auto"
}}>Submit</Button>
}}>Create Mapfix</Button>
</form>
</main>
</Webpage>

View File

@@ -12,7 +12,6 @@ interface CreatorAndReviewStatus {
asset_id: SubmissionInfo["AssetID"],
creator: SubmissionInfo["DisplayName"],
review: SubmissionInfo["StatusID"],
status_message: SubmissionInfo["StatusMessage"],
submitter: SubmissionInfo["Submitter"],
uploaded_asset_id: SubmissionInfo["UploadedAssetID"],
comments: Comment[],
@@ -52,7 +51,7 @@ function LeaveAComment() {
)
}
export default function Comments(stats: CommentersProps) {
export function Comments(stats: CommentersProps) {
return (<>
<section className="comments">
{stats.comments_data.comments.length===0
@@ -66,5 +65,6 @@ export default function Comments(stats: CommentersProps) {
}
export {
type CreatorAndReviewStatus
type CreatorAndReviewStatus,
type Comment,
}

View File

@@ -10,6 +10,8 @@ interface ReviewAction {
const ReviewActions = {
Submit: {name:"Submit",action:"trigger-submit"} as ReviewAction,
ForceSubmit: {name:"Force Submit",action:"trigger-submit"} as ReviewAction,
ResetSubmitting: {name:"Reset Submitting (fix softlocked status)",action:"reset-submitting"} as ReviewAction,
Revoke: {name:"Revoke",action:"revoke"} as ReviewAction,
Accept: {name:"Accept",action:"trigger-validate"} as ReviewAction,
Reject: {name:"Reject",action:"reject"} as ReviewAction,
@@ -112,9 +114,16 @@ export default function ReviewButtons(props: ReviewId) {
if ([SubmissionStatus.Submitted, SubmissionStatus.ChangesRequested].includes(submissionStatus!)) {
visibleButtons.push({ action: ReviewActions.Revoke, color: "info", submissionId });
}
if (submissionStatus === SubmissionStatus.Submitting) {
visibleButtons.push({ action: ReviewActions.ResetSubmitting, color: "error", submissionId });
}
}
if (roles&RolesConstants.SubmissionReview) {
// you can force submit a map in ChangesRequested status
if (!is_submitter && submissionStatus === SubmissionStatus.ChangesRequested) {
visibleButtons.push({ action: ReviewActions.ForceSubmit, color: "error", submissionId });
}
// you can't review your own submission!
// note that this means there needs to be more than one person with SubmissionReview
if (!is_submitter && submissionStatus === SubmissionStatus.Submitted) {

View File

@@ -5,7 +5,8 @@ import type { CreatorAndReviewStatus } from "./_comments";
import { MapImage } from "./_mapImage";
import { useParams } from "next/navigation";
import ReviewButtons from "./_reviewButtons";
import Comments from "./_comments";
import { Comments, Comment } from "./_comments";
import { AuditEvent, decodeAuditEvent as auditEventMessage } from "@/app/ts/AuditEvent";
import Webpage from "@/app/_components/webpage";
import Link from "next/link";
import { useState, useEffect } from "react";
@@ -46,7 +47,6 @@ function TitleAndComments(stats: CreatorAndReviewStatus) {
<p className="submitter">Submitter {stats.submitter}</p>
<p className="asset-id">Model Asset ID {stats.asset_id}</p>
<p className="uploaded-asset-id">Uploaded Asset ID {stats.uploaded_asset_id}</p>
<p className="status-message">Validation Error: {stats.status_message}</p>
<span className="spacer"></span>
<Comments comments_data={stats}/>
</main>
@@ -54,19 +54,42 @@ function TitleAndComments(stats: CreatorAndReviewStatus) {
}
export default function SubmissionInfoPage() {
const dynamicId = useParams<{submissionId: string}>()
const { submissionId } = useParams < { submissionId: string } >()
const [submission, setSubmission] = useState<SubmissionInfo | null>(null)
const [auditEvents, setAuditEvents] = useState<AuditEvent[]>([])
useEffect(() => { // needs to be client sided since server doesn't have a session, nextjs got mad at me for exporting an async function: (https://nextjs.org/docs/messages/no-async-client-component)
async function getSubmission() {
const res = await fetch(`/api/submissions/${dynamicId.submissionId}`)
const res = await fetch(`/api/submissions/${submissionId}`)
if (res.ok) {
setSubmission(await res.json())
}
}
getSubmission()
}, [dynamicId.submissionId])
async function getAuditEvents() {
const res = await fetch(`/api/submissions/${submissionId}/audit-events?Page=1&Limit=100`)
if (res.ok) {
setAuditEvents(await res.json())
}
}
getSubmission()
getAuditEvents()
}, [submissionId])
const comments:Comment[] = auditEvents.map((auditEvent) => {
let username = auditEvent.Username;
if (auditEvent.User == 9223372036854776000) {
username = "[Validator]";
}
if (username === "" && submission && auditEvent.User == submission.Submitter) {
username = "[Submitter]";
}
return {
date: auditEvent.CreatedAt,
name: username,
comment: auditEventMessage(auditEvent),
}
})
if (!submission) {
return <Webpage>
@@ -77,8 +100,8 @@ export default function SubmissionInfoPage() {
<Webpage>
<main className="map-page-main">
<section className="review-section">
<RatingArea assetId={submission.AssetID} submissionId={dynamicId.submissionId} submissionStatus={submission.StatusID} submissionSubmitter={submission.Submitter}/>
<TitleAndComments name={submission.DisplayName} creator={submission.Creator} review={submission.StatusID} status_message={submission.StatusMessage} asset_id={submission.AssetID} submitter={submission.Submitter} uploaded_asset_id={submission.UploadedAssetID} comments={[]}/>
<RatingArea assetId={submission.AssetID} submissionId={submissionId} submissionStatus={submission.StatusID} submissionSubmitter={submission.Submitter}/>
<TitleAndComments name={submission.DisplayName} creator={submission.Creator} review={submission.StatusID} asset_id={submission.AssetID} submitter={submission.Submitter} uploaded_asset_id={submission.UploadedAssetID} comments={comments}/>
</section>
</main>
</Webpage>

View File

@@ -1,6 +1,6 @@
'use client'
import React, { useState, useEffect } from "react";
import { useState, useEffect } from "react";
import { SubmissionList } from "../ts/Submission";
import { SubmissionCard } from "../_components/mapCard";
import Webpage from "@/app/_components/webpage";

View File

@@ -0,0 +1,65 @@
import { FormControl, Select, InputLabel, MenuItem } from "@mui/material";
import { styled } from '@mui/material/styles';
import InputBase from '@mui/material/InputBase';
import React from "react";
import { SelectChangeEvent } from "@mui/material";
// TODO: Properly style everything instead of pasting 🤚
type GameSelectionProps = {
game: number;
setGame: React.Dispatch<React.SetStateAction<number>>;
};
const BootstrapInput = styled(InputBase)(({ theme }) => ({
'label + &': {
marginTop: theme.spacing(3),
},
'& .MuiInputBase-input': {
backgroundColor: '#0000',
color: '#FFF',
border: '1px solid rgba(175, 175, 175, 0.66)',
fontSize: 16,
padding: '10px 26px 10px 12px',
transition: theme.transitions.create(['border-color', 'box-shadow']),
fontFamily: [
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'"Helvetica Neue"',
'Arial',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
].join(','),
'&:focus': {
borderRadius: 4,
borderColor: '#80bdff',
boxShadow: '0 0 0 0.2rem rgba(0,123,255,.25)',
},
},
}));
export default function GameSelection({ game, setGame }: GameSelectionProps) {
const handleChange = (event: SelectChangeEvent) => {
setGame(Number(event.target.value)); // TODO: Change later!! there's 100% a proper way of doing this
};
return (
<FormControl>
<InputLabel sx={{ color: "#646464" }}>Game</InputLabel>
<Select
value={String(game)}
label="Game"
onChange={handleChange}
input={<BootstrapInput />}
>
<MenuItem value={1}>Bhop</MenuItem>
<MenuItem value={2}>Surf</MenuItem>
<MenuItem value={3}>Fly Trials</MenuItem>
</Select>
</FormControl>
);
}

View File

@@ -2,19 +2,25 @@
import { Button, TextField } from "@mui/material"
import GameSelection from "./_game";
import SendIcon from '@mui/icons-material/Send';
import Webpage from "@/app/_components/webpage"
import React, { useState } from "react";
import "./(styles)/page.scss"
interface SubmissionPayload {
AssetID: number;
DisplayName: string;
Creator: string;
GameID: number;
}
interface IdResponse {
OperationID: number;
}
export default function SubmissionInfoPage() {
const [game, setGame] = useState(1);
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
@@ -23,6 +29,9 @@ export default function SubmissionInfoPage() {
const formData = new FormData(form);
const payload: SubmissionPayload = {
DisplayName: (formData.get("display-name") as string) ?? "unknown", // TEMPORARY! TODO: Change
Creator: (formData.get("creator") as string) ?? "unknown", // TEMPORARY! TODO: Change
GameID: game,
AssetID: Number((formData.get("asset-id") as string) ?? "0"),
};
@@ -64,13 +73,16 @@ export default function SubmissionInfoPage() {
<span className="spacer form-spacer"></span>
</header>
<form onSubmit={handleSubmit}>
<TextField className="form-field" id="asset-id" name="asset-id" label="Asset ID" variant="outlined"/>
<TextField className="form-field" id="asset-id" name="asset-id" label="Asset ID (required)" variant="outlined"/>
<TextField className="form-field" id="display-name" name="display-name" label="Display Name" variant="outlined"/>
<TextField className="form-field" id="creator" name="creator" label="Creator" variant="outlined"/>
<GameSelection game={game} setGame={setGame} />
<span className="spacer form-spacer"></span>
<Button type="submit" variant="contained" startIcon={<SendIcon/>} sx={{
width: "400px",
height: "50px",
marginInline: "auto"
}}>Submit</Button>
}}>Create Submission</Button>
</form>
</main>
</Webpage>

View File

@@ -0,0 +1,94 @@
import { SubmissionStatusToString } from "./Submission";
// Shared audit event types
export const enum AuditEventType {
Action = 0,
Comment = 1,
ChangeModel = 2,
ChangeValidatedModel = 3,
ChangeDisplayName = 4,
ChangeCreator = 5,
Error = 6,
}
// Discriminated union types for each event
export type AuditEventData =
| { EventType: AuditEventType.Action; EventData: AuditEventDataAction }
| { EventType: AuditEventType.Comment; EventData: AuditEventDataComment }
| { EventType: AuditEventType.ChangeModel; EventData: AuditEventDataChangeModel }
| { EventType: AuditEventType.ChangeValidatedModel; EventData: AuditEventDataChangeValidatedModel; }
| { EventType: AuditEventType.ChangeDisplayName; EventData: AuditEventDataChangeName; }
| { EventType: AuditEventType.ChangeCreator; EventData: AuditEventDataChangeName; }
| { EventType: AuditEventType.Error; EventData: AuditEventDataError };
// Concrete data interfaces
export interface AuditEventDataAction {
target_status: number;
}
export interface AuditEventDataComment {
comment: string;
}
export interface AuditEventDataChangeModel {
old_model_id: number;
old_model_version: number;
new_model_id: number;
new_model_version: number;
}
export interface AuditEventDataChangeValidatedModel {
validated_model_id: number;
validated_model_version: number;
}
export interface AuditEventDataChangeName {
old_name: string;
new_name: string;
}
export interface AuditEventDataError {
error: string;
}
// Full audit event type (mirroring the Go struct)
export interface AuditEvent {
Id: number;
CreatedAt: string; // ISO string, can convert to Date if needed
User: number;
Username: string;
ResourceType: string; // Assuming this is a string enum or similar
ResourceId: number;
EventType: AuditEventType;
EventData: unknown; // You'll decode this into a specific AuditEventData based on `event_type`
}
// Optional: decode function to parse event_data into strongly-typed structure
export function decodeAuditEvent(event: AuditEvent): string {
switch (event.EventType) {
case AuditEventType.Action:{
const data = event.EventData as AuditEventDataAction;
return `Changed status to ${SubmissionStatusToString(data.target_status)}`;
}case AuditEventType.Comment:{
const data = event.EventData as AuditEventDataComment;
return data.comment;
}case AuditEventType.ChangeModel:{
const data = event.EventData as AuditEventDataChangeModel;
return `Model changed to asset id = ${data.new_model_id}`;
}case AuditEventType.ChangeValidatedModel:{
const data = event.EventData as AuditEventDataChangeValidatedModel;
return `Model validated as asset id = ${data.validated_model_id}`;
}case AuditEventType.ChangeDisplayName:{
const data = event.EventData as AuditEventDataChangeName;
return `DisplayName changed to ${data.new_name}`;
}case AuditEventType.ChangeCreator:{
const data = event.EventData as AuditEventDataChangeName;
return `Creator changed to ${data.new_name}`;
}case AuditEventType.Error:{
const data = event.EventData as AuditEventDataError;
return `Error: ${data.error}`;
}
default:
throw new Error(`Unknown EventType: ${event.EventType}`);
}
}

View File

@@ -25,8 +25,8 @@ interface MapfixInfo {
readonly ValidatedAssetVersion: number,
readonly Completed: boolean,
readonly TargetAssetID: number,
readonly StatusID: MapfixStatus
readonly StatusMessage: string,
readonly StatusID: MapfixStatus,
readonly Description: string,
}
interface MapfixList {

View File

@@ -25,8 +25,7 @@ interface SubmissionInfo {
readonly ValidatedAssetVersion: number,
readonly Completed: boolean,
readonly UploadedAssetID: number,
readonly StatusID: SubmissionStatus
readonly StatusMessage: string,
readonly StatusID: SubmissionStatus,
}
interface SubmissionList {

5
web/src/app/ts/User.ts Normal file
View File

@@ -0,0 +1,5 @@
export interface UserInfo {
readonly UserID: number,
readonly Username: string,
readonly AvatarURL: string,
}

View File

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