diff --git a/combobulator/src/main.rs b/combobulator/src/main.rs index d1dbea4..cc356db 100644 --- a/combobulator/src/main.rs +++ b/combobulator/src/main.rs @@ -6,7 +6,7 @@ mod s3; const SUBJECT_MAPFIX_RELEASE:&str="maptest.mapfixes.release"; const SUBJECT_SUBMISSION_BATCHRELEASE:&str="maptest.submissions.batchrelease"; -const SUBJECT_SUBMISSION_RELEASE:&str="maptest.combobulator.submissions.release"; +const SUBJECT_SEED:&str="maptest.combobulator.seed"; #[derive(Debug)] pub enum StartupError{ @@ -55,22 +55,22 @@ async fn handle_message( message.ack().await.map_err(HandleMessageError::Ack)?; }, SUBJECT_SUBMISSION_BATCHRELEASE=>{ - // split batch into individual messages and republish + // split batch into individual seed messages let batch:nats_types::ReleaseSubmissionsBatchRequest=from_slice(&message.payload)?; println!("[combobulator] Splitting batch release (operation {}, {} submissions)", batch.OperationID,batch.Submissions.len()); for submission in batch.Submissions{ - let payload=serde_json::to_vec(&submission).map_err(HandleMessageError::Json)?; - jetstream.publish(SUBJECT_SUBMISSION_RELEASE,payload.into()) + let seed=nats_types::SeedCombobulatorRequest{AssetID:submission.UploadedAssetID}; + let payload=serde_json::to_vec(&seed).map_err(HandleMessageError::Json)?; + jetstream.publish(SUBJECT_SEED,payload.into()) .await.map_err(HandleMessageError::Publish)?; - println!("[combobulator] Published individual release for submission {}",submission.SubmissionID); + println!("[combobulator] Queued seed for asset {}",seed.AssetID); } - // ack the batch now that all individual messages are queued message.ack().await.map_err(HandleMessageError::Ack)?; }, - SUBJECT_SUBMISSION_RELEASE=>{ - let request:nats_types::ReleaseSubmissionRequest=from_slice(&message.payload)?; - processor.handle_submission_release(request).await.map_err(HandleMessageError::Process)?; + SUBJECT_SEED=>{ + let request:nats_types::SeedCombobulatorRequest=from_slice(&message.payload)?; + processor.handle_seed(request).await.map_err(HandleMessageError::Process)?; message.ack().await.map_err(HandleMessageError::Ack)?; }, other=>return Err(HandleMessageError::UnknownSubject(other.to_owned())), @@ -82,11 +82,7 @@ async fn handle_message( #[tokio::main] async fn main()->Result<(),StartupError>{ - // roblox cloud api for downloading models - let api_key=std::env::var("RBX_API_KEY").expect("RBX_API_KEY env required"); - let cloud_context=rbx_asset::cloud::Context::new(rbx_asset::cloud::ApiKey::new(api_key)); - - // roblox cookie api for downloading assets (textures, meshes, unions) + // roblox cookie api for downloading assets let cookie=std::env::var("RBXCOOKIE").expect("RBXCOOKIE env required"); let cookie_context=rbx_asset::cookie::Context::new(rbx_asset::cookie::Cookie::new(cookie)); @@ -97,7 +93,6 @@ async fn main()->Result<(),StartupError>{ let s3_cache=s3::S3Cache::new(s3_client,s3_bucket); let processor=process::Processor{ - cloud_context, cookie_context, s3:s3_cache, }; @@ -111,7 +106,7 @@ async fn main()->Result<(),StartupError>{ let filter_subjects=vec![ SUBJECT_MAPFIX_RELEASE.to_owned(), SUBJECT_SUBMISSION_BATCHRELEASE.to_owned(), - SUBJECT_SUBMISSION_RELEASE.to_owned(), + SUBJECT_SEED.to_owned(), ]; let nats_config=async_nats::jetstream::consumer::pull::Config{ diff --git a/combobulator/src/nats_types.rs b/combobulator/src/nats_types.rs index a6b6d99..4b99f94 100644 --- a/combobulator/src/nats_types.rs +++ b/combobulator/src/nats_types.rs @@ -7,8 +7,8 @@ pub struct ReleaseMapfixRequest{ pub TargetAssetID:u64, } -#[expect(nonstandard_style)] -#[derive(serde::Deserialize,serde::Serialize)] +#[expect(nonstandard_style,dead_code)] +#[derive(serde::Deserialize)] pub struct ReleaseSubmissionRequest{ pub SubmissionID:u64, pub ReleaseDate:i64, @@ -27,3 +27,9 @@ pub struct ReleaseSubmissionsBatchRequest{ pub Submissions:Vec, pub OperationID:u32, } + +#[expect(nonstandard_style)] +#[derive(serde::Deserialize,serde::Serialize)] +pub struct SeedCombobulatorRequest{ + pub AssetID:u64, +} diff --git a/combobulator/src/process.rs b/combobulator/src/process.rs index 79fb5fc..da05776 100644 --- a/combobulator/src/process.rs +++ b/combobulator/src/process.rs @@ -4,9 +4,7 @@ use crate::s3::S3Cache; #[expect(dead_code)] #[derive(Debug)] pub enum Error{ - Download(rbx_asset::cloud::GetError), - Decompress(std::io::Error), - NonFreeModel, + ArchivedModel, GetAssets(map_tool::roblox::UniqueAssetError), DownloadAsset(map_tool::roblox::DownloadAssetError), ConvertTexture(map_tool::roblox::ConvertTextureError), @@ -22,28 +20,11 @@ impl std::fmt::Display for Error{ impl std::error::Error for Error{} pub struct Processor{ - pub cloud_context:rbx_asset::cloud::Context, pub cookie_context:rbx_asset::cookie::Context, pub s3:S3Cache, } impl Processor{ - /// Download a model version from Roblox cloud API. - async fn download_model(&self,model_id:u64,model_version:u64)->Result,Error>{ - let location=self.cloud_context.get_asset_version_location( - rbx_asset::cloud::GetAssetVersionRequest{ - asset_id:model_id, - version:model_version, - } - ).await.map_err(Error::Download)?; - - let location=location.location.ok_or(Error::NonFreeModel)?; - - let maybe_gzip=self.cloud_context.get_asset(&location).await.map_err(Error::Download)?; - - Ok(maybe_gzip.to_vec().map_err(Error::Decompress)?) - } - /// Download an asset, returning None if the asset is archived. async fn download_asset(&self,asset_id:u64)->Result>,Error>{ match map_tool::roblox::download_asset(&self.cookie_context,asset_id).await{ @@ -61,9 +42,10 @@ impl Processor{ } /// Process a single model: extract assets, cache to S3, build SNF. - async fn process_model(&self,model_id:u64,model_version:u64)->Result<(),Error>{ - println!("[combobulator] Downloading model {model_id} v{model_version}"); - let rbxl_bytes=self.download_model(model_id,model_version).await?; + async fn process_model(&self,asset_id:u64)->Result<(),Error>{ + println!("[combobulator] Downloading model {asset_id}"); + let rbxl_bytes=self.download_asset(asset_id).await? + .ok_or(Error::ArchivedModel)?; // extract unique assets from the file let assets=map_tool::roblox::get_unique_assets_from_file(&rbxl_bytes) @@ -135,7 +117,7 @@ impl Processor{ println!("[combobulator] Converting to SNF"); let output=map_tool::roblox::convert_to_snf(&rbxl_bytes) .map_err(Error::ConvertSnf)?; - let snf_key=S3Cache::snf_key(model_id); + let snf_key=S3Cache::snf_key(asset_id); self.s3.put(&snf_key,output.snf).await.map_err(Error::S3Put)?; println!("[combobulator] SNF uploaded to {snf_key}"); @@ -144,15 +126,14 @@ impl Processor{ /// Handle a mapfix release message. pub async fn handle_mapfix_release(&self,request:ReleaseMapfixRequest)->Result<(),Error>{ - println!("[combobulator] Processing mapfix {} (model {} v{})", - request.MapfixID,request.ModelID,request.ModelVersion); - self.process_model(request.ModelID,request.ModelVersion).await + println!("[combobulator] Processing mapfix {} (asset {})", + request.MapfixID,request.TargetAssetID); + self.process_model(request.TargetAssetID).await } - /// Handle an individual submission release message. - pub async fn handle_submission_release(&self,request:crate::nats_types::ReleaseSubmissionRequest)->Result<(),Error>{ - println!("[combobulator] Processing submission {} (model {} v{})", - request.SubmissionID,request.ModelID,request.ModelVersion); - self.process_model(request.ModelID,request.ModelVersion).await + /// Handle a seed request (reprocess an existing map). + pub async fn handle_seed(&self,request:crate::nats_types::SeedCombobulatorRequest)->Result<(),Error>{ + println!("[combobulator] Seeding asset {}",request.AssetID); + self.process_model(request.AssetID).await } } diff --git a/docs/docs.go b/docs/docs.go index ecfa982..4498bb4 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -115,6 +115,46 @@ const docTemplate = `{ } } } + }, + "/map/{id}/snfm": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Redirects to a signed download URL for a map's SNFM file", + "tags": [ + "maps" + ], + "summary": "Download SNFM file", + "parameters": [ + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "307": { + "description": "Redirect to signed S3 URL" + }, + "404": { + "description": "Map not found", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "General error response", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } } }, "definitions": { diff --git a/docs/swagger.json b/docs/swagger.json index 3465447..da4f96d 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -108,6 +108,46 @@ } } } + }, + "/map/{id}/snfm": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Redirects to a signed download URL for a map's SNFM file", + "tags": [ + "maps" + ], + "summary": "Download SNFM file", + "parameters": [ + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "307": { + "description": "Redirect to signed S3 URL" + }, + "404": { + "description": "Map not found", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "General error response", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } } }, "definitions": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index ffc6d9c..b402668 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -133,6 +133,31 @@ paths: summary: Get map by ID tags: - maps + /map/{id}/snfm: + get: + description: Redirects to a signed download URL for a map's SNFM file + parameters: + - description: Map ID + in: path + name: id + required: true + type: integer + responses: + "307": + description: Redirect to signed S3 URL + "404": + description: Map not found + schema: + $ref: '#/definitions/Error' + default: + description: General error response + schema: + $ref: '#/definitions/Error' + security: + - ApiKeyAuth: [] + summary: Download SNFM file + tags: + - maps securityDefinitions: ApiKeyAuth: in: header diff --git a/generate.go b/generate.go index 5a37007..c3c141c 100644 --- a/generate.go +++ b/generate.go @@ -1,4 +1,4 @@ package main -//go:generate swag init -g ./cmd/maps-service/service.go +//go:generate go run github.com/swaggo/swag/cmd/swag@latest init -g ./cmd/maps-service/service.go //go:generate go run github.com/ogen-go/ogen/cmd/ogen@latest --target pkg/api --clean openapi.yaml diff --git a/go.mod b/go.mod index 400a74e..c384c54 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.24.5 require ( git.itzana.me/StrafesNET/dev-service v0.0.0-20250628052121-92af8193b5ed - git.itzana.me/strafesnet/go-grpc v0.0.0-20251228204118-c20dbb42afec + git.itzana.me/strafesnet/go-grpc v0.0.0-20260301211036-f2db3cb46e8c git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9 github.com/dchest/siphash v1.2.3 github.com/gin-gonic/gin v1.10.1 @@ -20,9 +20,9 @@ require ( github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.6 github.com/urfave/cli/v2 v2.27.6 - go.opentelemetry.io/otel v1.39.0 - go.opentelemetry.io/otel/metric v1.39.0 - go.opentelemetry.io/otel/trace v1.39.0 + go.opentelemetry.io/otel v1.40.0 + go.opentelemetry.io/otel/metric v1.40.0 + go.opentelemetry.io/otel/trace v1.40.0 google.golang.org/grpc v1.48.0 gorm.io/driver/postgres v1.6.0 gorm.io/gorm v1.25.12 @@ -32,6 +32,25 @@ require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.2 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.10 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.10 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 // indirect + github.com/aws/smithy-go v1.24.1 // indirect github.com/bytedance/sonic v1.11.6 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/go.sum b/go.sum index c2c5099..2f68280 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,10 @@ git.itzana.me/StrafesNET/dev-service v0.0.0-20250628052121-92af8193b5ed h1:eGWIQ git.itzana.me/StrafesNET/dev-service v0.0.0-20250628052121-92af8193b5ed/go.mod h1:KJal0K++M6HEzSry6JJ2iDPZtOQn5zSstNlDbU3X4Jg= git.itzana.me/strafesnet/go-grpc v0.0.0-20251228204118-c20dbb42afec h1:JSar9If1kzb02+Erp+zmSqHKWPPP2NqMQVK15pRmkLE= git.itzana.me/strafesnet/go-grpc v0.0.0-20251228204118-c20dbb42afec/go.mod h1:X7XTRUScRkBWq8q8bplbeso105RPDlnY7J6Wy1IwBMs= +git.itzana.me/strafesnet/go-grpc v0.0.0-20260301210537-0bea64387f6d h1:I73hWqmIcsSH90VHjwsg50v6emQkM0IAA04vb4wktBA= +git.itzana.me/strafesnet/go-grpc v0.0.0-20260301210537-0bea64387f6d/go.mod h1:X7XTRUScRkBWq8q8bplbeso105RPDlnY7J6Wy1IwBMs= +git.itzana.me/strafesnet/go-grpc v0.0.0-20260301211036-f2db3cb46e8c h1:sI50ymozoI+HFbxg1AOdCeWF6bJgpeP6OrnCvyjuQ9U= +git.itzana.me/strafesnet/go-grpc v0.0.0-20260301211036-f2db3cb46e8c/go.mod h1:X7XTRUScRkBWq8q8bplbeso105RPDlnY7J6Wy1IwBMs= git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9 h1:7lU6jyR7S7Rhh1dnUp7GyIRHUTBXZagw8F4n4hOyxLw= git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9/go.mod h1:uyYerSieEt4v0MJCdPLppG0LtJ4Yj035vuTetWGsxjY= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -14,6 +18,44 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/aws/aws-sdk-go-v2 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls= +github.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 h1:zWFmPmgw4sveAYi1mRqG+E/g0461cJ5M4bJ8/nc6d3Q= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5/go.mod h1:nVUlMLVV8ycXSb7mSkcNu9e3v/1TJq2RTlrPwhYWr5c= +github.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI= +github.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw= +github.com/aws/aws-sdk-go-v2/credentials v1.19.10 h1:EEhmEUFCE1Yhl7vDhNOI5OCL/iKMdkkYFTRpZXNw7m8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.10/go.mod h1:RnnlFCAlxQCkN2Q379B67USkBMu1PipEEiibzYN5UTE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 h1:eZioDaZGJ0tMM4gzmkNIO2aAoQd+je7Ug7TkvAzlmkU= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18/go.mod h1:CCXwUKAJdoWr6/NcxZ+zsiPr6oH/Q5aTooRGYieAyj4= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 h1:fJvQ5mIBVfKtiyx0AHY6HeWcRX5LGANLpq8SVR+Uazs= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10/go.mod h1:Kzm5e6OmNH8VMkgK9t+ry5jEih4Y8whqs+1hrkxim1I= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 h1:/A/xDuZAVD2BpsS2fftFRo/NoEKQJ8YTnJDEHBy2Gtg= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18/go.mod h1:hWe9b4f+djUQGmyiGEeOnZv69dtMSgpDRIvNMvuvzvY= +github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 h1:M1A9AjcFwlxTLuf0Faj88L8Iqw0n/AJHjpZTQzMMsSc= +github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2/go.mod h1:KsdTV6Q9WKUZm2mNJnUFmIoXfZux91M3sr/a4REX8e0= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs= +github.com/aws/smithy-go v1.24.1 h1:VbyeNfmYkWoxMVpGUAbQumkODcYmfMRfZ8yQiH30SK0= +github.com/aws/smithy-go v1.24.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -237,10 +279,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= diff --git a/openapi.yaml b/openapi.yaml index fb14d98..7a2d1fa 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -212,6 +212,21 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /maps-admin/seed-combobulator: + post: + summary: Queue all maps for combobulator processing + operationId: seedCombobulator + tags: + - Maps + responses: + "204": + description: Successful response + default: + description: General Error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" /mapfixes: get: summary: Get list of mapfixes diff --git a/pkg/api/oas_cfg_gen.go b/pkg/api/oas_cfg_gen.go index 5dafb54..d8d3ddb 100644 --- a/pkg/api/oas_cfg_gen.go +++ b/pkg/api/oas_cfg_gen.go @@ -4,6 +4,7 @@ package api import ( "net/http" + "strings" ht "github.com/ogen-go/ogen/http" "github.com/ogen-go/ogen/middleware" @@ -82,18 +83,8 @@ func (o otelOptionFunc) applyServer(c *serverConfig) { func newServerConfig(opts ...ServerOption) serverConfig { cfg := serverConfig{ - NotFound: http.NotFound, - MethodNotAllowed: func(w http.ResponseWriter, r *http.Request, allowed string) { - status := http.StatusMethodNotAllowed - if r.Method == "OPTIONS" { - w.Header().Set("Access-Control-Allow-Methods", allowed) - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - status = http.StatusNoContent - } else { - w.Header().Set("Allow", allowed) - } - w.WriteHeader(status) - }, + NotFound: http.NotFound, + MethodNotAllowed: nil, ErrorHandler: ogenerrors.DefaultErrorHandler, Middleware: nil, MaxMultipartMemory: 32 << 20, // 32 MB @@ -116,8 +107,44 @@ func (s baseServer) notFound(w http.ResponseWriter, r *http.Request) { s.cfg.NotFound(w, r) } -func (s baseServer) notAllowed(w http.ResponseWriter, r *http.Request, allowed string) { - s.cfg.MethodNotAllowed(w, r, allowed) +type notAllowedParams struct { + allowedMethods string + allowedHeaders map[string]string + acceptPost string + acceptPatch string +} + +func (s baseServer) notAllowed(w http.ResponseWriter, r *http.Request, params notAllowedParams) { + h := w.Header() + isOptions := r.Method == "OPTIONS" + if isOptions { + h.Set("Access-Control-Allow-Methods", params.allowedMethods) + if params.allowedHeaders != nil { + m := r.Header.Get("Access-Control-Request-Method") + if m != "" { + allowedHeaders, ok := params.allowedHeaders[strings.ToUpper(m)] + if ok { + h.Set("Access-Control-Allow-Headers", allowedHeaders) + } + } + } + if params.acceptPost != "" { + h.Set("Accept-Post", params.acceptPost) + } + if params.acceptPatch != "" { + h.Set("Accept-Patch", params.acceptPatch) + } + } + if s.cfg.MethodNotAllowed != nil { + s.cfg.MethodNotAllowed(w, r, params.allowedMethods) + return + } + status := http.StatusNoContent + if !isOptions { + h.Set("Allow", params.allowedMethods) + status = http.StatusMethodNotAllowed + } + w.WriteHeader(status) } func (cfg serverConfig) baseServer() (s baseServer, err error) { diff --git a/pkg/api/oas_client_gen.go b/pkg/api/oas_client_gen.go index be72604..9451c01 100644 --- a/pkg/api/oas_client_gen.go +++ b/pkg/api/oas_client_gen.go @@ -17,7 +17,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/metric" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" "go.opentelemetry.io/otel/trace" ) @@ -355,6 +355,12 @@ type Invoker interface { // // POST /release-submissions ReleaseSubmissions(ctx context.Context, request []ReleaseInfo) (*OperationID, error) + // SeedCombobulator invokes seedCombobulator operation. + // + // Queue all maps for combobulator processing. + // + // POST /maps-admin/seed-combobulator + SeedCombobulator(ctx context.Context) error // SessionRoles invokes sessionRoles operation. // // Get list of roles for the current session. @@ -423,14 +429,6 @@ type Client struct { sec SecuritySource baseClient } -type errorHandler interface { - NewError(ctx context.Context, err error) *ErrorStatusCode -} - -var _ Handler = struct { - errorHandler - *Client -}{} // NewClient initializes new Client defined by OAS. func NewClient(serverURL string, sec SecuritySource, opts ...ClientOption) (*Client, error) { @@ -580,7 +578,8 @@ func (c *Client) sendActionMapfixAccepted(ctx context.Context, params ActionMapf if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixAcceptedResponse(resp) @@ -705,7 +704,8 @@ func (c *Client) sendActionMapfixReject(ctx context.Context, params ActionMapfix if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixRejectResponse(resp) @@ -830,7 +830,8 @@ func (c *Client) sendActionMapfixRequestChanges(ctx context.Context, params Acti if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixRequestChangesResponse(resp) @@ -956,7 +957,8 @@ func (c *Client) sendActionMapfixResetSubmitting(ctx context.Context, params Act if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixResetSubmittingResponse(resp) @@ -1081,7 +1083,8 @@ func (c *Client) sendActionMapfixRetryValidate(ctx context.Context, params Actio if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixRetryValidateResponse(resp) @@ -1206,7 +1209,8 @@ func (c *Client) sendActionMapfixRevoke(ctx context.Context, params ActionMapfix if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixRevokeResponse(resp) @@ -1331,7 +1335,8 @@ func (c *Client) sendActionMapfixTriggerRelease(ctx context.Context, params Acti if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixTriggerReleaseResponse(resp) @@ -1456,7 +1461,8 @@ func (c *Client) sendActionMapfixTriggerSubmit(ctx context.Context, params Actio if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixTriggerSubmitResponse(resp) @@ -1581,7 +1587,8 @@ func (c *Client) sendActionMapfixTriggerSubmitUnchecked(ctx context.Context, par if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixTriggerSubmitUncheckedResponse(resp) @@ -1706,7 +1713,8 @@ func (c *Client) sendActionMapfixTriggerUpload(ctx context.Context, params Actio if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixTriggerUploadResponse(resp) @@ -1831,7 +1839,8 @@ func (c *Client) sendActionMapfixTriggerValidate(ctx context.Context, params Act if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixTriggerValidateResponse(resp) @@ -1956,7 +1965,8 @@ func (c *Client) sendActionMapfixUploaded(ctx context.Context, params ActionMapf if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixUploadedResponse(resp) @@ -2081,7 +2091,8 @@ func (c *Client) sendActionMapfixValidated(ctx context.Context, params ActionMap if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionMapfixValidatedResponse(resp) @@ -2206,7 +2217,8 @@ func (c *Client) sendActionSubmissionAccepted(ctx context.Context, params Action if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionSubmissionAcceptedResponse(resp) @@ -2331,7 +2343,8 @@ func (c *Client) sendActionSubmissionReject(ctx context.Context, params ActionSu if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionSubmissionRejectResponse(resp) @@ -2456,7 +2469,8 @@ func (c *Client) sendActionSubmissionRequestChanges(ctx context.Context, params if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionSubmissionRequestChangesResponse(resp) @@ -2582,7 +2596,8 @@ func (c *Client) sendActionSubmissionResetSubmitting(ctx context.Context, params if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionSubmissionResetSubmittingResponse(resp) @@ -2707,7 +2722,8 @@ func (c *Client) sendActionSubmissionRetryValidate(ctx context.Context, params A if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionSubmissionRetryValidateResponse(resp) @@ -2832,7 +2848,8 @@ func (c *Client) sendActionSubmissionRevoke(ctx context.Context, params ActionSu if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionSubmissionRevokeResponse(resp) @@ -2957,7 +2974,8 @@ func (c *Client) sendActionSubmissionTriggerSubmit(ctx context.Context, params A if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionSubmissionTriggerSubmitResponse(resp) @@ -3082,7 +3100,8 @@ func (c *Client) sendActionSubmissionTriggerSubmitUnchecked(ctx context.Context, if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionSubmissionTriggerSubmitUncheckedResponse(resp) @@ -3207,7 +3226,8 @@ func (c *Client) sendActionSubmissionTriggerUpload(ctx context.Context, params A if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionSubmissionTriggerUploadResponse(resp) @@ -3332,7 +3352,8 @@ func (c *Client) sendActionSubmissionTriggerValidate(ctx context.Context, params if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionSubmissionTriggerValidateResponse(resp) @@ -3458,7 +3479,8 @@ func (c *Client) sendActionSubmissionValidated(ctx context.Context, params Actio if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeActionSubmissionValidatedResponse(resp) @@ -3534,7 +3556,8 @@ func (c *Client) sendBatchAssetThumbnails(ctx context.Context, request *BatchAss if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeBatchAssetThumbnailsResponse(resp) @@ -3610,7 +3633,8 @@ func (c *Client) sendBatchUserThumbnails(ctx context.Context, request *BatchUser if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeBatchUserThumbnailsResponse(resp) @@ -3686,7 +3710,8 @@ func (c *Client) sendBatchUsernames(ctx context.Context, request *BatchUsernames if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeBatchUsernamesResponse(resp) @@ -3795,7 +3820,8 @@ func (c *Client) sendCreateMapfix(ctx context.Context, request *MapfixTriggerCre if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeCreateMapfixResponse(resp) @@ -3923,7 +3949,8 @@ func (c *Client) sendCreateMapfixAuditComment(ctx context.Context, request Creat if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeCreateMapfixAuditCommentResponse(resp) @@ -4032,7 +4059,8 @@ func (c *Client) sendCreateScript(ctx context.Context, request *ScriptCreate) (r if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeCreateScriptResponse(resp) @@ -4141,7 +4169,8 @@ func (c *Client) sendCreateScriptPolicy(ctx context.Context, request *ScriptPoli if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeCreateScriptPolicyResponse(resp) @@ -4250,7 +4279,8 @@ func (c *Client) sendCreateSubmission(ctx context.Context, request *SubmissionTr if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeCreateSubmissionResponse(resp) @@ -4359,7 +4389,8 @@ func (c *Client) sendCreateSubmissionAdmin(ctx context.Context, request *Submiss if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeCreateSubmissionAdminResponse(resp) @@ -4487,7 +4518,8 @@ func (c *Client) sendCreateSubmissionAuditComment(ctx context.Context, request C if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeCreateSubmissionAuditCommentResponse(resp) @@ -4611,7 +4643,8 @@ func (c *Client) sendDeleteScript(ctx context.Context, params DeleteScriptParams if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeDeleteScriptResponse(resp) @@ -4735,7 +4768,8 @@ func (c *Client) sendDeleteScriptPolicy(ctx context.Context, params DeleteScript if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeDeleteScriptPolicyResponse(resp) @@ -4860,7 +4894,8 @@ func (c *Client) sendDownloadMapAsset(ctx context.Context, params DownloadMapAss if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeDownloadMapAssetResponse(resp) @@ -4972,7 +5007,8 @@ func (c *Client) sendGetAssetThumbnail(ctx context.Context, params GetAssetThumb if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeGetAssetThumbnailResponse(resp) @@ -5063,7 +5099,8 @@ func (c *Client) sendGetMap(ctx context.Context, params GetMapParams) (res *Map, if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeGetMapResponse(resp) @@ -5154,7 +5191,8 @@ func (c *Client) sendGetMapfix(ctx context.Context, params GetMapfixParams) (res if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeGetMapfixResponse(resp) @@ -5278,7 +5316,8 @@ func (c *Client) sendGetOperation(ctx context.Context, params GetOperationParams if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeGetOperationResponse(resp) @@ -5369,7 +5408,8 @@ func (c *Client) sendGetScript(ctx context.Context, params GetScriptParams) (res if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeGetScriptResponse(resp) @@ -5460,7 +5500,8 @@ func (c *Client) sendGetScriptPolicy(ctx context.Context, params GetScriptPolicy if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeGetScriptPolicyResponse(resp) @@ -5533,7 +5574,8 @@ func (c *Client) sendGetStats(ctx context.Context) (res *Stats, err error) { if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeGetStatsResponse(resp) @@ -5624,7 +5666,8 @@ func (c *Client) sendGetSubmission(ctx context.Context, params GetSubmissionPara if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeGetSubmissionResponse(resp) @@ -5736,7 +5779,8 @@ func (c *Client) sendGetUserThumbnail(ctx context.Context, params GetUserThumbna if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeGetUserThumbnailResponse(resp) @@ -5860,7 +5904,8 @@ func (c *Client) sendListMapfixAuditEvents(ctx context.Context, params ListMapfi if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeListMapfixAuditEventsResponse(resp) @@ -6118,7 +6163,8 @@ func (c *Client) sendListMapfixes(ctx context.Context, params ListMapfixesParams if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeListMapfixesResponse(resp) @@ -6291,7 +6337,8 @@ func (c *Client) sendListMaps(ctx context.Context, params ListMapsParams) (res [ if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeListMapsResponse(resp) @@ -6447,7 +6494,8 @@ func (c *Client) sendListScriptPolicy(ctx context.Context, params ListScriptPoli if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeListScriptPolicyResponse(resp) @@ -6637,7 +6685,8 @@ func (c *Client) sendListScripts(ctx context.Context, params ListScriptsParams) if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeListScriptsResponse(resp) @@ -6761,7 +6810,8 @@ func (c *Client) sendListSubmissionAuditEvents(ctx context.Context, params ListS if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeListSubmissionAuditEventsResponse(resp) @@ -7019,7 +7069,8 @@ func (c *Client) sendListSubmissions(ctx context.Context, params ListSubmissions if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeListSubmissionsResponse(resp) @@ -7128,7 +7179,8 @@ func (c *Client) sendReleaseSubmissions(ctx context.Context, request []ReleaseIn if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeReleaseSubmissionsResponse(resp) @@ -7139,6 +7191,113 @@ func (c *Client) sendReleaseSubmissions(ctx context.Context, request []ReleaseIn return result, nil } +// SeedCombobulator invokes seedCombobulator operation. +// +// Queue all maps for combobulator processing. +// +// POST /maps-admin/seed-combobulator +func (c *Client) SeedCombobulator(ctx context.Context) error { + _, err := c.sendSeedCombobulator(ctx) + return err +} + +func (c *Client) sendSeedCombobulator(ctx context.Context) (res *SeedCombobulatorNoContent, err error) { + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("seedCombobulator"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.URLTemplateKey.String("/maps-admin/seed-combobulator"), + } + otelAttrs = append(otelAttrs, c.cfg.Attributes...) + + // 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, SeedCombobulatorOperation, + 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] = "/maps-admin/seed-combobulator" + uri.AddPathParts(u, pathParts[:]...) + + stage = "EncodeRequest" + r, err := ht.NewRequest(ctx, "POST", u) + if err != nil { + return res, errors.Wrap(err, "create request") + } + + { + type bitset = [1]uint8 + var satisfied bitset + { + stage = "Security:CookieAuth" + switch err := c.securityCookieAuth(ctx, SeedCombobulatorOperation, 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") + } + body := resp.Body + defer body.Close() + + stage = "DecodeResponse" + result, err := decodeSeedCombobulatorResponse(resp) + if err != nil { + return res, errors.Wrap(err, "decode response") + } + + return result, nil +} + // SessionRoles invokes sessionRoles operation. // // Get list of roles for the current session. @@ -7234,7 +7393,8 @@ func (c *Client) sendSessionRoles(ctx context.Context) (res *Roles, err error) { if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeSessionRolesResponse(resp) @@ -7340,7 +7500,8 @@ func (c *Client) sendSessionUser(ctx context.Context) (res *User, err error) { if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeSessionUserResponse(resp) @@ -7446,7 +7607,8 @@ func (c *Client) sendSessionValidate(ctx context.Context) (res bool, err error) if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeSessionValidateResponse(resp) @@ -7571,7 +7733,8 @@ func (c *Client) sendSetMapfixCompleted(ctx context.Context, params SetMapfixCom if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeSetMapfixCompletedResponse(resp) @@ -7696,7 +7859,8 @@ func (c *Client) sendSetSubmissionCompleted(ctx context.Context, params SetSubmi if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeSetSubmissionCompletedResponse(resp) @@ -7824,7 +7988,8 @@ func (c *Client) sendUpdateMapfixDescription(ctx context.Context, request Update if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeUpdateMapfixDescriptionResponse(resp) @@ -7981,7 +8146,8 @@ func (c *Client) sendUpdateMapfixModel(ctx context.Context, params UpdateMapfixM if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeUpdateMapfixModelResponse(resp) @@ -8108,7 +8274,8 @@ func (c *Client) sendUpdateScript(ctx context.Context, request *ScriptUpdate, pa if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeUpdateScriptResponse(resp) @@ -8235,7 +8402,8 @@ func (c *Client) sendUpdateScriptPolicy(ctx context.Context, request *ScriptPoli if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeUpdateScriptPolicyResponse(resp) @@ -8392,7 +8560,8 @@ func (c *Client) sendUpdateSubmissionModel(ctx context.Context, params UpdateSub if err != nil { return res, errors.Wrap(err, "do request") } - defer resp.Body.Close() + body := resp.Body + defer body.Close() stage = "DecodeResponse" result, err := decodeUpdateSubmissionModelResponse(resp) diff --git a/pkg/api/oas_handlers_gen.go b/pkg/api/oas_handlers_gen.go index 7373e99..9db5b66 100644 --- a/pkg/api/oas_handlers_gen.go +++ b/pkg/api/oas_handlers_gen.go @@ -15,7 +15,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/metric" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" "go.opentelemetry.io/otel/trace" ) @@ -46,6 +46,8 @@ func (s *Server) handleActionMapfixAcceptedRequest(args [1]string, argsEscaped b semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/reset-validating"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixAcceptedOperation, @@ -244,6 +246,8 @@ func (s *Server) handleActionMapfixRejectRequest(args [1]string, argsEscaped boo semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/reject"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixRejectOperation, @@ -442,6 +446,8 @@ func (s *Server) handleActionMapfixRequestChangesRequest(args [1]string, argsEsc semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/request-changes"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixRequestChangesOperation, @@ -641,6 +647,8 @@ func (s *Server) handleActionMapfixResetSubmittingRequest(args [1]string, argsEs semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/reset-submitting"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixResetSubmittingOperation, @@ -839,6 +847,8 @@ func (s *Server) handleActionMapfixRetryValidateRequest(args [1]string, argsEsca semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/retry-validate"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixRetryValidateOperation, @@ -1037,6 +1047,8 @@ func (s *Server) handleActionMapfixRevokeRequest(args [1]string, argsEscaped boo semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/revoke"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixRevokeOperation, @@ -1235,6 +1247,8 @@ func (s *Server) handleActionMapfixTriggerReleaseRequest(args [1]string, argsEsc semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/trigger-release"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixTriggerReleaseOperation, @@ -1433,6 +1447,8 @@ func (s *Server) handleActionMapfixTriggerSubmitRequest(args [1]string, argsEsca semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/trigger-submit"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixTriggerSubmitOperation, @@ -1631,6 +1647,8 @@ func (s *Server) handleActionMapfixTriggerSubmitUncheckedRequest(args [1]string, semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/trigger-submit-unchecked"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixTriggerSubmitUncheckedOperation, @@ -1829,6 +1847,8 @@ func (s *Server) handleActionMapfixTriggerUploadRequest(args [1]string, argsEsca semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/trigger-upload"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixTriggerUploadOperation, @@ -2027,6 +2047,8 @@ func (s *Server) handleActionMapfixTriggerValidateRequest(args [1]string, argsEs semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/trigger-validate"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixTriggerValidateOperation, @@ -2225,6 +2247,8 @@ func (s *Server) handleActionMapfixUploadedRequest(args [1]string, argsEscaped b semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/reset-releasing"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixUploadedOperation, @@ -2423,6 +2447,8 @@ func (s *Server) handleActionMapfixValidatedRequest(args [1]string, argsEscaped semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/status/reset-uploading"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionMapfixValidatedOperation, @@ -2621,6 +2647,8 @@ func (s *Server) handleActionSubmissionAcceptedRequest(args [1]string, argsEscap semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/reset-validating"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionAcceptedOperation, @@ -2819,6 +2847,8 @@ func (s *Server) handleActionSubmissionRejectRequest(args [1]string, argsEscaped semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/reject"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionRejectOperation, @@ -3017,6 +3047,8 @@ func (s *Server) handleActionSubmissionRequestChangesRequest(args [1]string, arg semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/request-changes"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionRequestChangesOperation, @@ -3216,6 +3248,8 @@ func (s *Server) handleActionSubmissionResetSubmittingRequest(args [1]string, ar semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/reset-submitting"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionResetSubmittingOperation, @@ -3414,6 +3448,8 @@ func (s *Server) handleActionSubmissionRetryValidateRequest(args [1]string, args semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/retry-validate"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionRetryValidateOperation, @@ -3612,6 +3648,8 @@ func (s *Server) handleActionSubmissionRevokeRequest(args [1]string, argsEscaped semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/revoke"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionRevokeOperation, @@ -3810,6 +3848,8 @@ func (s *Server) handleActionSubmissionTriggerSubmitRequest(args [1]string, args semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/trigger-submit"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionTriggerSubmitOperation, @@ -4008,6 +4048,8 @@ func (s *Server) handleActionSubmissionTriggerSubmitUncheckedRequest(args [1]str semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/trigger-submit-unchecked"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionTriggerSubmitUncheckedOperation, @@ -4206,6 +4248,8 @@ func (s *Server) handleActionSubmissionTriggerUploadRequest(args [1]string, args semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/trigger-upload"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionTriggerUploadOperation, @@ -4404,6 +4448,8 @@ func (s *Server) handleActionSubmissionTriggerValidateRequest(args [1]string, ar semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/trigger-validate"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionTriggerValidateOperation, @@ -4603,6 +4649,8 @@ func (s *Server) handleActionSubmissionValidatedRequest(args [1]string, argsEsca semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/status/reset-uploading"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ActionSubmissionValidatedOperation, @@ -4801,6 +4849,8 @@ func (s *Server) handleBatchAssetThumbnailsRequest(args [0]string, argsEscaped b semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/thumbnails/assets"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), BatchAssetThumbnailsOperation, @@ -4953,6 +5003,8 @@ func (s *Server) handleBatchUserThumbnailsRequest(args [0]string, argsEscaped bo semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/thumbnails/users"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), BatchUserThumbnailsOperation, @@ -5105,6 +5157,8 @@ func (s *Server) handleBatchUsernamesRequest(args [0]string, argsEscaped bool, w semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/usernames"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), BatchUsernamesOperation, @@ -5257,6 +5311,8 @@ func (s *Server) handleCreateMapfixRequest(args [0]string, argsEscaped bool, w h semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), CreateMapfixOperation, @@ -5455,6 +5511,8 @@ func (s *Server) handleCreateMapfixAuditCommentRequest(args [1]string, argsEscap semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/comment"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), CreateMapfixAuditCommentOperation, @@ -5668,6 +5726,8 @@ func (s *Server) handleCreateScriptRequest(args [0]string, argsEscaped bool, w h semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/scripts"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), CreateScriptOperation, @@ -5866,6 +5926,8 @@ func (s *Server) handleCreateScriptPolicyRequest(args [0]string, argsEscaped boo semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/script-policy"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), CreateScriptPolicyOperation, @@ -6064,6 +6126,8 @@ func (s *Server) handleCreateSubmissionRequest(args [0]string, argsEscaped bool, semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), CreateSubmissionOperation, @@ -6262,6 +6326,8 @@ func (s *Server) handleCreateSubmissionAdminRequest(args [0]string, argsEscaped semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions-admin"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), CreateSubmissionAdminOperation, @@ -6460,6 +6526,8 @@ func (s *Server) handleCreateSubmissionAuditCommentRequest(args [1]string, argsE semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/comment"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), CreateSubmissionAuditCommentOperation, @@ -6673,6 +6741,8 @@ func (s *Server) handleDeleteScriptRequest(args [1]string, argsEscaped bool, w h semconv.HTTPRequestMethodKey.String("DELETE"), semconv.HTTPRouteKey.String("/scripts/{ScriptID}"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), DeleteScriptOperation, @@ -6871,6 +6941,8 @@ func (s *Server) handleDeleteScriptPolicyRequest(args [1]string, argsEscaped boo semconv.HTTPRequestMethodKey.String("DELETE"), semconv.HTTPRouteKey.String("/script-policy/{ScriptPolicyID}"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), DeleteScriptPolicyOperation, @@ -7069,6 +7141,8 @@ func (s *Server) handleDownloadMapAssetRequest(args [1]string, argsEscaped bool, semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/maps/{MapID}/download"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), DownloadMapAssetOperation, @@ -7267,6 +7341,8 @@ func (s *Server) handleGetAssetThumbnailRequest(args [1]string, argsEscaped bool semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/thumbnails/asset/{AssetID}"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetAssetThumbnailOperation, @@ -7423,6 +7499,8 @@ func (s *Server) handleGetMapRequest(args [1]string, argsEscaped bool, w http.Re semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/maps/{MapID}"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetMapOperation, @@ -7575,6 +7653,8 @@ func (s *Server) handleGetMapfixRequest(args [1]string, argsEscaped bool, w http semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetMapfixOperation, @@ -7727,6 +7807,8 @@ func (s *Server) handleGetOperationRequest(args [1]string, argsEscaped bool, w h semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/operations/{OperationID}"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetOperationOperation, @@ -7925,6 +8007,8 @@ func (s *Server) handleGetScriptRequest(args [1]string, argsEscaped bool, w http semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/scripts/{ScriptID}"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetScriptOperation, @@ -8077,6 +8161,8 @@ func (s *Server) handleGetScriptPolicyRequest(args [1]string, argsEscaped bool, semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/script-policy/{ScriptPolicyID}"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetScriptPolicyOperation, @@ -8229,6 +8315,8 @@ func (s *Server) handleGetStatsRequest(args [0]string, argsEscaped bool, w http. semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/stats"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetStatsOperation, @@ -8362,6 +8450,8 @@ func (s *Server) handleGetSubmissionRequest(args [1]string, argsEscaped bool, w semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetSubmissionOperation, @@ -8514,6 +8604,8 @@ func (s *Server) handleGetUserThumbnailRequest(args [1]string, argsEscaped bool, semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/thumbnails/user/{UserID}"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetUserThumbnailOperation, @@ -8670,6 +8762,8 @@ func (s *Server) handleListMapfixAuditEventsRequest(args [1]string, argsEscaped semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/audit-events"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ListMapfixAuditEventsOperation, @@ -8830,6 +8924,8 @@ func (s *Server) handleListMapfixesRequest(args [0]string, argsEscaped bool, w h semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/mapfixes"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ListMapfixesOperation, @@ -9022,6 +9118,8 @@ func (s *Server) handleListMapsRequest(args [0]string, argsEscaped bool, w http. semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/maps"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ListMapsOperation, @@ -9194,6 +9292,8 @@ func (s *Server) handleListScriptPolicyRequest(args [0]string, argsEscaped bool, semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/script-policy"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ListScriptPolicyOperation, @@ -9362,6 +9462,8 @@ func (s *Server) handleListScriptsRequest(args [0]string, argsEscaped bool, w ht semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/scripts"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ListScriptsOperation, @@ -9538,6 +9640,8 @@ func (s *Server) handleListSubmissionAuditEventsRequest(args [1]string, argsEsca semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/audit-events"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ListSubmissionAuditEventsOperation, @@ -9698,6 +9802,8 @@ func (s *Server) handleListSubmissionsRequest(args [0]string, argsEscaped bool, semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/submissions"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ListSubmissionsOperation, @@ -9890,6 +9996,8 @@ func (s *Server) handleReleaseSubmissionsRequest(args [0]string, argsEscaped boo semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/release-submissions"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ReleaseSubmissionsOperation, @@ -10075,6 +10183,191 @@ func (s *Server) handleReleaseSubmissionsRequest(args [0]string, argsEscaped boo } } +// handleSeedCombobulatorRequest handles seedCombobulator operation. +// +// Queue all maps for combobulator processing. +// +// POST /maps-admin/seed-combobulator +func (s *Server) handleSeedCombobulatorRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("seedCombobulator"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.HTTPRouteKey.String("/maps-admin/seed-combobulator"), + } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) + + // Start a span for this request. + ctx, span := s.cfg.Tracer.Start(r.Context(), SeedCombobulatorOperation, + 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: SeedCombobulatorOperation, + ID: "seedCombobulator", + } + ) + { + type bitset = [1]uint8 + var satisfied bitset + { + sctx, ok, err := s.securityCookieAuth(ctx, SeedCombobulatorOperation, 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 + } + } + + var rawBody []byte + + var response *SeedCombobulatorNoContent + if m := s.cfg.Middleware; m != nil { + mreq := middleware.Request{ + Context: ctx, + OperationName: SeedCombobulatorOperation, + OperationSummary: "Queue all maps for combobulator processing", + OperationID: "seedCombobulator", + Body: nil, + RawBody: rawBody, + Params: middleware.Parameters{}, + Raw: r, + } + + type ( + Request = struct{} + Params = struct{} + Response = *SeedCombobulatorNoContent + ) + response, err = middleware.HookMiddleware[ + Request, + Params, + Response, + ]( + m, + mreq, + nil, + func(ctx context.Context, request Request, params Params) (response Response, err error) { + err = s.h.SeedCombobulator(ctx) + return response, err + }, + ) + } else { + err = s.h.SeedCombobulator(ctx) + } + 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 := encodeSeedCombobulatorResponse(response, w, span); err != nil { + defer recordError("EncodeResponse", err) + if !errors.Is(err, ht.ErrInternalServerErrorResponse) { + s.cfg.ErrorHandler(ctx, w, r, err) + } + return + } +} + // handleSessionRolesRequest handles sessionRoles operation. // // Get list of roles for the current session. @@ -10088,6 +10381,8 @@ func (s *Server) handleSessionRolesRequest(args [0]string, argsEscaped bool, w h semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/session/roles"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), SessionRolesOperation, @@ -10271,6 +10566,8 @@ func (s *Server) handleSessionUserRequest(args [0]string, argsEscaped bool, w ht semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/session/user"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), SessionUserOperation, @@ -10454,6 +10751,8 @@ func (s *Server) handleSessionValidateRequest(args [0]string, argsEscaped bool, semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/session/validate"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), SessionValidateOperation, @@ -10637,6 +10936,8 @@ func (s *Server) handleSetMapfixCompletedRequest(args [1]string, argsEscaped boo semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/completed"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), SetMapfixCompletedOperation, @@ -10835,6 +11136,8 @@ func (s *Server) handleSetSubmissionCompletedRequest(args [1]string, argsEscaped semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/completed"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), SetSubmissionCompletedOperation, @@ -11033,6 +11336,8 @@ func (s *Server) handleUpdateMapfixDescriptionRequest(args [1]string, argsEscape semconv.HTTPRequestMethodKey.String("PATCH"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/description"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), UpdateMapfixDescriptionOperation, @@ -11246,6 +11551,8 @@ func (s *Server) handleUpdateMapfixModelRequest(args [1]string, argsEscaped bool semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/mapfixes/{MapfixID}/model"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), UpdateMapfixModelOperation, @@ -11452,6 +11759,8 @@ func (s *Server) handleUpdateScriptRequest(args [1]string, argsEscaped bool, w h semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/scripts/{ScriptID}"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), UpdateScriptOperation, @@ -11665,6 +11974,8 @@ func (s *Server) handleUpdateScriptPolicyRequest(args [1]string, argsEscaped boo semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/script-policy/{ScriptPolicyID}"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), UpdateScriptPolicyOperation, @@ -11878,6 +12189,8 @@ func (s *Server) handleUpdateSubmissionModelRequest(args [1]string, argsEscaped semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/submissions/{SubmissionID}/model"), } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), UpdateSubmissionModelOperation, diff --git a/pkg/api/oas_operations_gen.go b/pkg/api/oas_operations_gen.go index 260e79e..f251574 100644 --- a/pkg/api/oas_operations_gen.go +++ b/pkg/api/oas_operations_gen.go @@ -60,6 +60,7 @@ const ( ListSubmissionAuditEventsOperation OperationName = "ListSubmissionAuditEvents" ListSubmissionsOperation OperationName = "ListSubmissions" ReleaseSubmissionsOperation OperationName = "ReleaseSubmissions" + SeedCombobulatorOperation OperationName = "SeedCombobulator" SessionRolesOperation OperationName = "SessionRoles" SessionUserOperation OperationName = "SessionUser" SessionValidateOperation OperationName = "SessionValidate" diff --git a/pkg/api/oas_response_decoders_gen.go b/pkg/api/oas_response_decoders_gen.go index fe3a927..9134132 100644 --- a/pkg/api/oas_response_decoders_gen.go +++ b/pkg/api/oas_response_decoders_gen.go @@ -4392,6 +4392,66 @@ func decodeReleaseSubmissionsResponse(resp *http.Response) (res *OperationID, _ return res, errors.Wrap(defRes, "error") } +func decodeSeedCombobulatorResponse(resp *http.Response) (res *SeedCombobulatorNoContent, _ error) { + switch resp.StatusCode { + case 204: + // Code 204. + return &SeedCombobulatorNoContent{}, 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 decodeSessionRolesResponse(resp *http.Response) (res *Roles, _ error) { switch resp.StatusCode { case 200: diff --git a/pkg/api/oas_response_encoders_gen.go b/pkg/api/oas_response_encoders_gen.go index ea5dbc8..aa75e85 100644 --- a/pkg/api/oas_response_encoders_gen.go +++ b/pkg/api/oas_response_encoders_gen.go @@ -340,6 +340,7 @@ func encodeDownloadMapAssetResponse(response DownloadMapAssetOK, w http.Response } func encodeGetAssetThumbnailResponse(response *GetAssetThumbnailFound, w http.ResponseWriter, span trace.Span) error { + w.Header().Set("Access-Control-Expose-Headers", "Location") // Encoding response headers. { h := uri.NewHeaderEncoder(w.Header()) @@ -464,6 +465,7 @@ func encodeGetSubmissionResponse(response *Submission, w http.ResponseWriter, sp } func encodeGetUserThumbnailResponse(response *GetUserThumbnailFound, w http.ResponseWriter, span trace.Span) error { + w.Header().Set("Access-Control-Expose-Headers", "Location") // Encoding response headers. { h := uri.NewHeaderEncoder(w.Header()) @@ -621,6 +623,13 @@ func encodeReleaseSubmissionsResponse(response *OperationID, w http.ResponseWrit return nil } +func encodeSeedCombobulatorResponse(response *SeedCombobulatorNoContent, w http.ResponseWriter, span trace.Span) error { + w.WriteHeader(204) + span.SetStatus(codes.Ok, http.StatusText(204)) + + return nil +} + func encodeSessionRolesResponse(response *Roles, w http.ResponseWriter, span trace.Span) error { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(200) diff --git a/pkg/api/oas_router_gen.go b/pkg/api/oas_router_gen.go index 23dde03..f44d473 100644 --- a/pkg/api/oas_router_gen.go +++ b/pkg/api/oas_router_gen.go @@ -10,6 +10,51 @@ import ( "github.com/ogen-go/ogen/uri" ) +var ( + rn42AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } + rn44AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } + rn83AllowedHeaders = map[string]string{ + "PATCH": "Content-Type", + } + rn73AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } + rn48AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } + rn56AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } + rn46AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } + rn54AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } + rn49AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } + rn50AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } + rn52AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } + rn38AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } + rn40AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } + rn41AllowedHeaders = map[string]string{ + "POST": "Content-Type", + } +) + func (s *Server) cutPrefix(path string) (string, bool) { prefix := s.cfg.Prefix if prefix == "" { @@ -88,7 +133,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "POST": s.handleCreateMapfixRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET,POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET,POST", + allowedHeaders: rn42AllowedHeaders, + acceptPost: "application/json", + acceptPatch: "", + }) } return @@ -118,7 +168,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -152,7 +207,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -186,7 +246,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: rn44AllowedHeaders, + acceptPost: "text/plain", + acceptPatch: "", + }) } return @@ -208,7 +273,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -232,7 +302,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "PATCH") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "PATCH", + allowedHeaders: rn83AllowedHeaders, + acceptPost: "", + acceptPatch: "text/plain", + }) } return @@ -254,7 +329,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -300,7 +380,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -322,7 +407,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -356,7 +446,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -378,7 +473,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -400,7 +500,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -422,7 +527,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -446,7 +556,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -468,7 +583,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -504,7 +624,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -525,7 +650,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -547,7 +677,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -571,7 +706,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -593,7 +733,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -622,12 +767,42 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "GET": s.handleListMapsRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return } switch elem[0] { + case '-': // Prefix: "-admin/seed-combobulator" + + if l := len("-admin/seed-combobulator"); len(elem) >= l && elem[0:l] == "-admin/seed-combobulator" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleSeedCombobulatorRequest([0]string{}, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) + } + + return + } + case '/': // Prefix: "/" if l := len("/"); len(elem) >= l && elem[0:l] == "/" { @@ -652,7 +827,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -674,7 +854,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -711,7 +896,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -731,7 +921,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "POST": s.handleReleaseSubmissionsRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: rn73AllowedHeaders, + acceptPost: "application/json", + acceptPatch: "", + }) } return @@ -776,7 +971,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "POST": s.handleCreateScriptPolicyRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET,POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET,POST", + allowedHeaders: rn48AllowedHeaders, + acceptPost: "application/json", + acceptPatch: "", + }) } return @@ -815,7 +1015,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "DELETE,GET,POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "DELETE,GET,POST", + allowedHeaders: rn56AllowedHeaders, + acceptPost: "application/json", + acceptPatch: "", + }) } return @@ -838,7 +1043,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "POST": s.handleCreateScriptRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET,POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET,POST", + allowedHeaders: rn46AllowedHeaders, + acceptPost: "application/json", + acceptPatch: "", + }) } return @@ -877,7 +1087,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "DELETE,GET,POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "DELETE,GET,POST", + allowedHeaders: rn54AllowedHeaders, + acceptPost: "application/json", + acceptPatch: "", + }) } return @@ -913,7 +1128,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "GET": s.handleSessionRolesRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -933,7 +1153,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "GET": s.handleSessionUserRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -953,7 +1178,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "GET": s.handleSessionValidateRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -975,7 +1205,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "GET": s.handleGetStatsRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -996,7 +1231,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "POST": s.handleCreateSubmissionRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET,POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET,POST", + allowedHeaders: rn49AllowedHeaders, + acceptPost: "application/json", + acceptPatch: "", + }) } return @@ -1016,7 +1256,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "POST": s.handleCreateSubmissionAdminRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: rn50AllowedHeaders, + acceptPost: "application/json", + acceptPatch: "", + }) } return @@ -1046,7 +1291,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1080,7 +1330,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1114,7 +1369,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: rn52AllowedHeaders, + acceptPost: "text/plain", + acceptPatch: "", + }) } return @@ -1136,7 +1396,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1160,7 +1425,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1206,7 +1476,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1228,7 +1503,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1262,7 +1542,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1284,7 +1569,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1306,7 +1596,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1330,7 +1625,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1352,7 +1652,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1387,7 +1692,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1409,7 +1719,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1433,7 +1748,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1455,7 +1775,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1522,7 +1847,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1542,7 +1872,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "POST": s.handleBatchAssetThumbnailsRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: rn38AllowedHeaders, + acceptPost: "application/json", + acceptPatch: "", + }) } return @@ -1587,7 +1922,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { args[0], }, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "GET") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: nil, + acceptPost: "", + acceptPatch: "", + }) } return @@ -1607,7 +1947,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "POST": s.handleBatchUserThumbnailsRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: rn40AllowedHeaders, + acceptPost: "application/json", + acceptPatch: "", + }) } return @@ -1631,7 +1976,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "POST": s.handleBatchUsernamesRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "POST", + allowedHeaders: rn41AllowedHeaders, + acceptPost: "application/json", + acceptPatch: "", + }) } return @@ -2378,6 +2728,31 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { } } switch elem[0] { + case '-': // Prefix: "-admin/seed-combobulator" + + if l := len("-admin/seed-combobulator"); len(elem) >= l && elem[0:l] == "-admin/seed-combobulator" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = SeedCombobulatorOperation + r.summary = "Queue all maps for combobulator processing" + r.operationID = "seedCombobulator" + r.operationGroup = "" + r.pathPattern = "/maps-admin/seed-combobulator" + r.args = args + r.count = 0 + return r, true + default: + return + } + } + case '/': // Prefix: "/" if l := len("/"); len(elem) >= l && elem[0:l] == "/" { diff --git a/pkg/api/oas_schemas_gen.go b/pkg/api/oas_schemas_gen.go index a05861d..3f100e0 100644 --- a/pkg/api/oas_schemas_gen.go +++ b/pkg/api/oas_schemas_gen.go @@ -1993,6 +1993,9 @@ func (s *ScriptUpdate) SetResourceID(val OptInt64) { s.ResourceID = val } +// SeedCombobulatorNoContent is response for SeedCombobulator operation. +type SeedCombobulatorNoContent struct{} + // SetMapfixCompletedNoContent is response for SetMapfixCompleted operation. type SetMapfixCompletedNoContent struct{} diff --git a/pkg/api/oas_security_gen.go b/pkg/api/oas_security_gen.go index fe7d2cc..f299a04 100644 --- a/pkg/api/oas_security_gen.go +++ b/pkg/api/oas_security_gen.go @@ -32,6 +32,7 @@ func findAuthorization(h http.Header, prefix string) (string, bool) { return "", false } +// operationRolesCookieAuth is a private map storing roles per operation. var operationRolesCookieAuth = map[string][]string{ ActionMapfixAcceptedOperation: []string{}, ActionMapfixRejectOperation: []string{}, @@ -69,6 +70,7 @@ var operationRolesCookieAuth = map[string][]string{ DownloadMapAssetOperation: []string{}, GetOperationOperation: []string{}, ReleaseSubmissionsOperation: []string{}, + SeedCombobulatorOperation: []string{}, SessionRolesOperation: []string{}, SessionUserOperation: []string{}, SessionValidateOperation: []string{}, @@ -81,6 +83,27 @@ var operationRolesCookieAuth = map[string][]string{ UpdateSubmissionModelOperation: []string{}, } +// GetRolesForCookieAuth returns the required roles for the given operation. +// +// This is useful for authorization scenarios where you need to know which roles +// are required for an operation. +// +// Example: +// +// requiredRoles := GetRolesForCookieAuth(AddPetOperation) +// +// Returns nil if the operation has no role requirements or if the operation is unknown. +func GetRolesForCookieAuth(operation string) []string { + roles, ok := operationRolesCookieAuth[operation] + if !ok { + return nil + } + // Return a copy to prevent external modification + result := make([]string, len(roles)) + copy(result, roles) + return result +} + func (s *Server) securityCookieAuth(ctx context.Context, operationName OperationName, req *http.Request) (context.Context, bool, error) { var t CookieAuth const parameterName = "session_id" diff --git a/pkg/api/oas_server_gen.go b/pkg/api/oas_server_gen.go index dc3be49..707c53f 100644 --- a/pkg/api/oas_server_gen.go +++ b/pkg/api/oas_server_gen.go @@ -335,6 +335,12 @@ type Handler interface { // // POST /release-submissions ReleaseSubmissions(ctx context.Context, req []ReleaseInfo) (*OperationID, error) + // SeedCombobulator implements seedCombobulator operation. + // + // Queue all maps for combobulator processing. + // + // POST /maps-admin/seed-combobulator + SeedCombobulator(ctx context.Context) error // SessionRoles implements sessionRoles operation. // // Get list of roles for the current session. diff --git a/pkg/api/oas_unimplemented_gen.go b/pkg/api/oas_unimplemented_gen.go index bd924ef..dc5a3fa 100644 --- a/pkg/api/oas_unimplemented_gen.go +++ b/pkg/api/oas_unimplemented_gen.go @@ -502,6 +502,15 @@ func (UnimplementedHandler) ReleaseSubmissions(ctx context.Context, req []Releas return r, ht.ErrNotImplemented } +// SeedCombobulator implements seedCombobulator operation. +// +// Queue all maps for combobulator processing. +// +// POST /maps-admin/seed-combobulator +func (UnimplementedHandler) SeedCombobulator(ctx context.Context) error { + return ht.ErrNotImplemented +} + // SessionRoles implements sessionRoles operation. // // Get list of roles for the current session. diff --git a/pkg/cmds/serve.go b/pkg/cmds/serve.go index 4def873..563a7eb 100644 --- a/pkg/cmds/serve.go +++ b/pkg/cmds/serve.go @@ -19,6 +19,8 @@ import ( "git.itzana.me/strafesnet/maps-service/pkg/service" "git.itzana.me/strafesnet/maps-service/pkg/validator_controller" "git.itzana.me/strafesnet/maps-service/pkg/web_api" + awsconfig "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/nats-io/nats.go" "github.com/redis/go-redis/v9" log "github.com/sirupsen/logrus" @@ -123,6 +125,12 @@ func NewServeCommand() *cli.Command { EnvVars: []string{"REDIS_DB"}, Value: 0, }, + &cli.StringFlag{ + Name: "s3-bucket", + Usage: "S3 bucket for map assets", + EnvVars: []string{"S3_BUCKET"}, + Required: true, + }, }, } } @@ -168,6 +176,13 @@ func serve(ctx *cli.Context) error { ApiKey: ctx.String("rbx-api-key"), } + // Initialize S3 client + awsCfg, err := awsconfig.LoadDefaultConfig(ctx.Context) + if err != nil { + log.WithError(err).Fatal("failed to load AWS config") + } + s3Client := s3.NewFromConfig(awsCfg) + // connect to main game database conn, err := grpc.Dial(ctx.String("data-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { @@ -180,6 +195,8 @@ func serve(ctx *cli.Context) error { users.NewUsersServiceClient(conn), robloxClient, redisClient, + s3Client, + ctx.String("s3-bucket"), ) svc_external := web_api.NewService( diff --git a/pkg/controller/maps.go b/pkg/controller/maps.go index d751d64..e668af3 100644 --- a/pkg/controller/maps.go +++ b/pkg/controller/maps.go @@ -195,3 +195,13 @@ func (svc *Maps) IncrementLoadCount(ctx context.Context, request *maps_extended. } return &maps_extended.NullResponse{}, nil } + +func (svc *Maps) GetSnfmDownloadUrl(ctx context.Context, request *maps_extended.MapId) (*maps_extended.SnfmDownloadUrl, error) { + url, err := svc.inner.GetSnfmDownloadUrl(ctx, request.ID) + if err != nil { + return nil, err + } + return &maps_extended.SnfmDownloadUrl{ + Url: url, + }, nil +} diff --git a/pkg/datastore/datastore.go b/pkg/datastore/datastore.go index 0c43d1c..d8d4e49 100644 --- a/pkg/datastore/datastore.go +++ b/pkg/datastore/datastore.go @@ -47,6 +47,7 @@ type Maps interface { Create(ctx context.Context, smap model.Map) (model.Map, error) Update(ctx context.Context, id int64, values OptionalMap) error Delete(ctx context.Context, id int64) error + GetAll(ctx context.Context) ([]model.Map, error) List(ctx context.Context, filters OptionalMap, page model.Page) ([]model.Map, error) IncrementLoadCount(ctx context.Context, id int64) error } diff --git a/pkg/datastore/gormstore/maps.go b/pkg/datastore/gormstore/maps.go index a47e27f..647e4f2 100644 --- a/pkg/datastore/gormstore/maps.go +++ b/pkg/datastore/gormstore/maps.go @@ -74,6 +74,14 @@ func (env *Maps) Delete(ctx context.Context, id int64) error { return nil } +func (env *Maps) GetAll(ctx context.Context) ([]model.Map, error) { + var maps []model.Map + if err := env.db.Find(&maps).Error; err != nil { + return nil, err + } + return maps, nil +} + func (env *Maps) List(ctx context.Context, filters datastore.OptionalMap, page model.Page) ([]model.Map, error) { var events []model.Map if err := env.db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&events).Error; err != nil { diff --git a/pkg/model/nats.go b/pkg/model/nats.go index 081ba13..ddd86ca 100644 --- a/pkg/model/nats.go +++ b/pkg/model/nats.go @@ -91,3 +91,7 @@ type ReleaseMapfixRequest struct { ModelVersion uint64 TargetAssetID uint64 } + +type SeedCombobulatorRequest struct { + AssetID uint64 +} diff --git a/pkg/public_api/dto/map.go b/pkg/public_api/dto/map.go index 4977c9e..c0573b3 100644 --- a/pkg/public_api/dto/map.go +++ b/pkg/public_api/dto/map.go @@ -1,8 +1,9 @@ package dto import ( - "git.itzana.me/strafesnet/go-grpc/maps_extended" "time" + + "git.itzana.me/strafesnet/go-grpc/maps_extended" ) type MapFilter struct { diff --git a/pkg/public_api/handlers/maps.go b/pkg/public_api/handlers/maps.go index f4fdbde..2fd767b 100644 --- a/pkg/public_api/handlers/maps.go +++ b/pkg/public_api/handlers/maps.go @@ -81,6 +81,47 @@ func (h *MapHandler) Get(ctx *gin.Context) { }) } +// @Summary Download SNFM file +// @Description Redirects to a signed download URL for a map's SNFM file +// @Tags maps +// @Security ApiKeyAuth +// @Param id path int true "Map ID" +// @Success 307 "Redirect to signed S3 URL" +// @Failure 404 {object} dto.Error "Map not found" +// @Failure default {object} dto.Error "General error response" +// @Router /map/{id}/snfm [get] +func (h *MapHandler) GetSnfmDownloadUrl(ctx *gin.Context) { + id := ctx.Param("id") + mapID, err := strconv.ParseInt(id, 10, 64) + if err != nil { + ctx.JSON(http.StatusBadRequest, dto.Error{ + Error: "Invalid map ID format", + }) + return + } + + resp, err := maps_extended.NewMapsServiceClient(h.mapsClient).GetSnfmDownloadUrl(ctx, &maps_extended.MapId{ + ID: mapID, + }) + if err != nil { + statusCode := http.StatusInternalServerError + errorMessage := "Failed to get download URL" + + if status.Code(err) == codes.NotFound { + statusCode = http.StatusNotFound + errorMessage = "Map not found" + } + + ctx.JSON(statusCode, dto.Error{ + Error: errorMessage, + }) + log.WithError(err).Error("Failed to get SNFM download URL") + return + } + + ctx.Redirect(http.StatusTemporaryRedirect, resp.Url) +} + // @Summary List maps // @Description Get a list of maps // @Tags maps diff --git a/pkg/public_api/router.go b/pkg/public_api/router.go index 1d889ab..5986167 100644 --- a/pkg/public_api/router.go +++ b/pkg/public_api/router.go @@ -93,6 +93,13 @@ func setupRoutes(cfg *RouterConfig) (*gin.Engine, error) { v1.GET("/map/:id", mapsHandler.Get) } + v1Download := public_api.Group("/v1") + { + v1Download.Use(middleware.ValidateRequest("Maps", "Download", cfg.devClient)) + + v1Download.GET("/map/:id/snfm", mapsHandler.GetSnfmDownloadUrl) + } + // Docs public_api.GET("/docs/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) public_api.GET("/", func(ctx *gin.Context) { diff --git a/pkg/service/maps.go b/pkg/service/maps.go index 3365988..482bbcf 100644 --- a/pkg/service/maps.go +++ b/pkg/service/maps.go @@ -99,6 +99,10 @@ func (svc *Service) CreateMap(ctx context.Context, item model.Map) (int64, error return map_item.ID, nil } +func (svc *Service) GetAllMaps(ctx context.Context) ([]model.Map, error) { + return svc.db.Maps().GetAll(ctx) +} + func (svc *Service) ListMaps(ctx context.Context, filter MapFilter, page model.Page) ([]model.Map, error) { return svc.db.Maps().List(ctx, datastore.OptionalMap(filter), page) } diff --git a/pkg/service/nats_maps.go b/pkg/service/nats_maps.go new file mode 100644 index 0000000..6b8298f --- /dev/null +++ b/pkg/service/nats_maps.go @@ -0,0 +1,21 @@ +package service + +import ( + "encoding/json" + + "git.itzana.me/strafesnet/maps-service/pkg/model" +) + +func (svc *Service) NatsSeedCombobulator(assetID uint64) error { + request := model.SeedCombobulatorRequest{ + AssetID: assetID, + } + + j, err := json.Marshal(request) + if err != nil { + return err + } + + _, err = svc.nats.Publish("maptest.combobulator.seed", j) + return err +} diff --git a/pkg/service/service.go b/pkg/service/service.go index 370cd64..4dc1404 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -2,11 +2,14 @@ package service import ( "context" + "fmt" + "time" "git.itzana.me/strafesnet/go-grpc/maps" "git.itzana.me/strafesnet/go-grpc/users" "git.itzana.me/strafesnet/maps-service/pkg/datastore" "git.itzana.me/strafesnet/maps-service/pkg/roblox" + "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/nats-io/nats.go" "github.com/redis/go-redis/v9" ) @@ -17,6 +20,8 @@ type Service struct { maps maps.MapsServiceClient users users.UsersServiceClient thumbnailService *ThumbnailService + s3Presign *s3.PresignClient + s3Bucket string } func NewService( @@ -26,6 +31,8 @@ func NewService( users users.UsersServiceClient, robloxClient *roblox.Client, redisClient *redis.Client, + s3Client *s3.Client, + s3Bucket string, ) Service { return Service{ db: db, @@ -33,9 +40,23 @@ func NewService( maps: maps, users: users, thumbnailService: NewThumbnailService(robloxClient, redisClient), + s3Presign: s3.NewPresignClient(s3Client), + s3Bucket: s3Bucket, } } +func (s *Service) GetSnfmDownloadUrl(ctx context.Context, mapID int64) (string, error) { + key := fmt.Sprintf("maps/%d.snfm", mapID) + presigned, err := s.s3Presign.PresignGetObject(ctx, &s3.GetObjectInput{ + Bucket: &s.s3Bucket, + Key: &key, + }, s3.WithPresignExpires(5*time.Minute)) + if err != nil { + return "", err + } + return presigned.URL, nil +} + // GetAssetThumbnails proxies to the thumbnail service func (s *Service) GetAssetThumbnails(ctx context.Context, assetIDs []uint64, size roblox.ThumbnailSize) (map[uint64]string, error) { return s.thumbnailService.GetAssetThumbnails(ctx, assetIDs, size) diff --git a/pkg/web_api/maps.go b/pkg/web_api/maps.go index 97574b1..5764f7c 100644 --- a/pkg/web_api/maps.go +++ b/pkg/web_api/maps.go @@ -86,6 +86,39 @@ func (svc *Service) GetMap(ctx context.Context, params api.GetMapParams) (*api.M }, nil } +// SeedCombobulator implements seedCombobulator operation. +// +// Queue all maps for combobulator processing. +// +// POST /maps-admin/seed-combobulator +func (svc *Service) SeedCombobulator(ctx context.Context) error { + userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) + if !ok { + return ErrUserInfo + } + + has_role, err := userInfo.HasRoleSubmissionRelease() + if err != nil { + return err + } + if !has_role { + return ErrPermissionDeniedNeedRoleSubmissionRelease + } + + maps, err := svc.inner.GetAllMaps(ctx) + if err != nil { + return err + } + + for _, m := range maps { + if err := svc.inner.NatsSeedCombobulator(uint64(m.ID)); err != nil { + return err + } + } + + return nil +} + // DownloadMapAsset invokes downloadMapAsset operation. // // Download the map asset.