diff --git a/Cargo.lock b/Cargo.lock index 2f71fec..28333f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1297,9 +1297,9 @@ dependencies = [ [[package]] name = "rbx_asset" -version = "0.2.5" +version = "0.3.3" source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/" -checksum = "dcf243f46bd41b3880a27278177a3f9996f95ab231d9a04345ad9dd381c3a54a" +checksum = "91722b37549ded270f39556194ca03d03e08bd70674d239ec845765ed9e42b7d" dependencies = [ "chrono", "flate2", diff --git a/openapi-internal.yaml b/openapi-internal.yaml index f92b03b..9040e92 100644 --- a/openapi-internal.yaml +++ b/openapi-internal.yaml @@ -121,7 +121,7 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" - /operations/{OperationID}/failed: + /operations/{OperationID}/status/operation-failed: post: summary: (Internal endpoint) Fail an operation and write a StatusMessage operationId: actionOperationFailed diff --git a/pkg/internal/oas_client_gen.go b/pkg/internal/oas_client_gen.go index d57735f..0248a3f 100644 --- a/pkg/internal/oas_client_gen.go +++ b/pkg/internal/oas_client_gen.go @@ -50,7 +50,7 @@ type Invoker interface { // // (Internal endpoint) Fail an operation and write a StatusMessage. // - // POST /operations/{OperationID}/failed + // POST /operations/{OperationID}/status/operation-failed ActionOperationFailed(ctx context.Context, params ActionOperationFailedParams) error // ActionSubmissionAccepted invokes actionSubmissionAccepted operation. // @@ -468,7 +468,7 @@ func (c *Client) sendActionMapfixValidated(ctx context.Context, params ActionMap // // (Internal endpoint) Fail an operation and write a StatusMessage. // -// POST /operations/{OperationID}/failed +// POST /operations/{OperationID}/status/operation-failed func (c *Client) ActionOperationFailed(ctx context.Context, params ActionOperationFailedParams) error { _, err := c.sendActionOperationFailed(ctx, params) return err @@ -478,7 +478,7 @@ func (c *Client) sendActionOperationFailed(ctx context.Context, params ActionOpe otelAttrs := []attribute.KeyValue{ otelogen.OperationID("actionOperationFailed"), semconv.HTTPRequestMethodKey.String("POST"), - semconv.HTTPRouteKey.String("/operations/{OperationID}/failed"), + semconv.HTTPRouteKey.String("/operations/{OperationID}/status/operation-failed"), } // Run stopwatch. @@ -530,7 +530,7 @@ func (c *Client) sendActionOperationFailed(ctx context.Context, params ActionOpe } pathParts[1] = encoded } - pathParts[2] = "/failed" + pathParts[2] = "/status/operation-failed" uri.AddPathParts(u, pathParts[:]...) stage = "EncodeQueryParams" diff --git a/pkg/internal/oas_handlers_gen.go b/pkg/internal/oas_handlers_gen.go index 96b8c4b..b382980 100644 --- a/pkg/internal/oas_handlers_gen.go +++ b/pkg/internal/oas_handlers_gen.go @@ -485,14 +485,14 @@ func (s *Server) handleActionMapfixValidatedRequest(args [1]string, argsEscaped // // (Internal endpoint) Fail an operation and write a StatusMessage. // -// POST /operations/{OperationID}/failed +// POST /operations/{OperationID}/status/operation-failed func (s *Server) handleActionOperationFailedRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("actionOperationFailed"), semconv.HTTPRequestMethodKey.String("POST"), - semconv.HTTPRouteKey.String("/operations/{OperationID}/failed"), + semconv.HTTPRouteKey.String("/operations/{OperationID}/status/operation-failed"), } // Start a span for this request. diff --git a/pkg/internal/oas_router_gen.go b/pkg/internal/oas_router_gen.go index 0531e26..ecc609b 100644 --- a/pkg/internal/oas_router_gen.go +++ b/pkg/internal/oas_router_gen.go @@ -242,9 +242,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { break } switch elem[0] { - case '/': // Prefix: "/failed" + case '/': // Prefix: "/status/operation-failed" - if l := len("/failed"); len(elem) >= l && elem[0:l] == "/failed" { + if l := len("/status/operation-failed"); len(elem) >= l && elem[0:l] == "/status/operation-failed" { elem = elem[l:] } else { break @@ -817,9 +817,9 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { break } switch elem[0] { - case '/': // Prefix: "/failed" + case '/': // Prefix: "/status/operation-failed" - if l := len("/failed"); len(elem) >= l && elem[0:l] == "/failed" { + if l := len("/status/operation-failed"); len(elem) >= l && elem[0:l] == "/status/operation-failed" { elem = elem[l:] } else { break @@ -832,7 +832,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { r.name = ActionOperationFailedOperation r.summary = "(Internal endpoint) Fail an operation and write a StatusMessage" r.operationID = "actionOperationFailed" - r.pathPattern = "/operations/{OperationID}/failed" + r.pathPattern = "/operations/{OperationID}/status/operation-failed" r.args = args r.count = 1 return r, true diff --git a/pkg/internal/oas_server_gen.go b/pkg/internal/oas_server_gen.go index b19609a..4fd886c 100644 --- a/pkg/internal/oas_server_gen.go +++ b/pkg/internal/oas_server_gen.go @@ -30,7 +30,7 @@ type Handler interface { // // (Internal endpoint) Fail an operation and write a StatusMessage. // - // POST /operations/{OperationID}/failed + // POST /operations/{OperationID}/status/operation-failed ActionOperationFailed(ctx context.Context, params ActionOperationFailedParams) error // ActionSubmissionAccepted implements actionSubmissionAccepted operation. // diff --git a/pkg/internal/oas_unimplemented_gen.go b/pkg/internal/oas_unimplemented_gen.go index 1adcfdf..e231989 100644 --- a/pkg/internal/oas_unimplemented_gen.go +++ b/pkg/internal/oas_unimplemented_gen.go @@ -44,7 +44,7 @@ func (UnimplementedHandler) ActionMapfixValidated(ctx context.Context, params Ac // // (Internal endpoint) Fail an operation and write a StatusMessage. // -// POST /operations/{OperationID}/failed +// POST /operations/{OperationID}/status/operation-failed func (UnimplementedHandler) ActionOperationFailed(ctx context.Context, params ActionOperationFailedParams) error { return ht.ErrNotImplemented } diff --git a/pkg/service_internal/mapfixes.go b/pkg/service_internal/mapfixes.go index b792f72..e0f7375 100644 --- a/pkg/service_internal/mapfixes.go +++ b/pkg/service_internal/mapfixes.go @@ -3,6 +3,7 @@ package service_internal import ( "context" "errors" + "fmt" "git.itzana.me/strafesnet/maps-service/pkg/datastore" internal "git.itzana.me/strafesnet/maps-service/pkg/internal" @@ -125,6 +126,16 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *internal.MapfixCr if err != nil { return nil, err } + + // mark the operation as completed and provide the path + pmap := datastore.Optional() + pmap.Add("status_id", model.OperationStatusCompleted) + pmap.Add("path", fmt.Sprintf("/mapfixes/%d", mapfix.ID)) + err = svc.DB.Operations().Update(ctx, request.OperationID, pmap) + if err != nil { + return nil, err + } + return &internal.MapfixID{ MapfixID: mapfix.ID, }, nil diff --git a/pkg/service_internal/operations.go b/pkg/service_internal/operations.go index 2deeffd..87ccccc 100644 --- a/pkg/service_internal/operations.go +++ b/pkg/service_internal/operations.go @@ -12,7 +12,7 @@ import ( // // Fail the specified OperationID with a StatusMessage. // -// POST /operations/{OperationID}/failed +// POST /operations/{OperationID}/status/operation-failed func (svc *Service) ActionOperationFailed(ctx context.Context, params internal.ActionOperationFailedParams) (error) { pmap := datastore.Optional() pmap.Add("status_id", model.OperationStatusFailed) diff --git a/pkg/service_internal/submissions.go b/pkg/service_internal/submissions.go index 3322230..b445c64 100644 --- a/pkg/service_internal/submissions.go +++ b/pkg/service_internal/submissions.go @@ -3,6 +3,7 @@ package service_internal import ( "context" "errors" + "fmt" "git.itzana.me/strafesnet/maps-service/pkg/datastore" internal "git.itzana.me/strafesnet/maps-service/pkg/internal" @@ -124,6 +125,16 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *internal.Subm if err != nil { return nil, err } + + // mark the operation as completed and provide the path + pmap := datastore.Optional() + pmap.Add("status_id", model.OperationStatusCompleted) + pmap.Add("path", fmt.Sprintf("/submissions/%d", submission.ID)) + err = svc.DB.Operations().Update(ctx, request.OperationID, pmap) + if err != nil { + return nil, err + } + return &internal.SubmissionID{ SubmissionID: submission.ID, }, nil diff --git a/validation/Cargo.toml b/validation/Cargo.toml index 28ce533..ee5dffa 100644 --- a/validation/Cargo.toml +++ b/validation/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" submissions-api = { path = "api", features = ["internal"], default-features = false, registry = "strafesnet" } async-nats = "0.40.0" futures = "0.3.31" -rbx_asset = { version = "0.2.5", registry = "strafesnet" } +rbx_asset = { version = "0.3.3", registry = "strafesnet" } rbx_binary = { version = "0.7.4", registry = "strafesnet"} rbx_dom_weak = { version = "2.9.0", registry = "strafesnet"} rbx_reflection_database = { version = "0.2.12", registry = "strafesnet"} diff --git a/validation/src/create.rs b/validation/src/create.rs index 1513904..747a824 100644 --- a/validation/src/create.rs +++ b/validation/src/create.rs @@ -5,8 +5,11 @@ use crate::rbx_util::{get_mapinfo,read_dom,MapInfo,ReadDomError,GetMapInfoError, pub enum Error{ ModelVersionsPage(rbx_asset::cookie::PageError), EmptyVersionsPage, - WrongCreatorType, + CreatorTypeMustBeUser(rbx_asset::cookie::CreatorType), + ModelDetails(rbx_asset::cookie::GetError), + ModelInfoDownload(rbx_asset::cookie::GetAssetV2Error), ModelFileDownload(rbx_asset::cookie::GetError), + NoLocations, ModelFileDecode(ReadDomError), GetMapInfo(GetMapInfoError), ParseGameID(ParseGameIDError), @@ -32,28 +35,32 @@ pub struct CreateResult{ } impl crate::message_handler::MessageHandler{ pub async fn create_inner(&self,create_info:CreateRequest)->Result<CreateResult,Error>{ - // discover the latest asset version - let asset_versions_page=self.cookie_context.get_asset_versions_page(rbx_asset::cookie::AssetVersionsPageRequest{ - asset_id:create_info.ModelID, - cursor:None - }).await.map_err(Error::ModelVersionsPage)?; + // discover asset creator + let creator_fut=async{ + self.cookie_context.get_asset_details( + rbx_asset::cookie::GetAssetDetailsRequest{asset_id:create_info.ModelID} + ).await.map_err(Error::ModelDetails) + }; - // grab version info - let first_version=asset_versions_page.data.first().ok_or(Error::EmptyVersionsPage)?; + // download the map model + let asset_fut=async{ + let asset_info=self.cookie_context.get_asset_v2(rbx_asset::cookie::GetAssetRequest{ + asset_id:create_info.ModelID, + version:None, + }).await.map_err(Error::ModelInfoDownload)?; - if first_version.creatorType!="User"{ - return Err(Error::WrongCreatorType); + let location=asset_info.info.locations.first().ok_or(Error::NoLocations)?; + let data=self.cookie_context.get_asset_v2_download(location).await.map_err(Error::ModelFileDownload)?; + + Ok((asset_info.version,data)) + }; + + let (details,(asset_version,model_data))=tokio::try_join!(creator_fut,asset_fut)?; + + if details.Creator.CreatorType!=rbx_asset::cookie::CreatorType::User{ + return Err(Error::CreatorTypeMustBeUser(details.Creator.CreatorType)); } - let asset_creator_id=first_version.creatorTargetId; - let asset_version=first_version.assetVersionNumber; - - // download the map model version - let model_data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{ - asset_id:create_info.ModelID, - version:Some(asset_version), - }).await.map_err(Error::ModelFileDownload)?; - // decode dom (slow!) let dom=read_dom(&mut std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?; @@ -67,7 +74,7 @@ impl crate::message_handler::MessageHandler{ let game_id=game_id.map_err(Error::ParseGameID)?; Ok(CreateResult{ - AssetOwner:asset_creator_id as i64, + AssetOwner:details.Creator.Id as i64, DisplayName:display_name.unwrap_or_default().to_owned(), Creator:creator.unwrap_or_default().to_owned(), GameID:game_id as i32, diff --git a/validation/src/create_mapfix.rs b/validation/src/create_mapfix.rs index 6dbe905..982fd91 100644 --- a/validation/src/create_mapfix.rs +++ b/validation/src/create_mapfix.rs @@ -4,8 +4,8 @@ use crate::create::CreateRequest; #[allow(dead_code)] #[derive(Debug)] pub enum Error{ + Create(crate::create::Error), ApiActionMapfixCreate(submissions_api::Error), - ApiActionOperationFailed(submissions_api::Error), } impl std::fmt::Display for Error{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ @@ -15,31 +15,36 @@ impl std::fmt::Display for Error{ impl std::error::Error for Error{} impl crate::message_handler::MessageHandler{ - pub async fn create_mapfix(&self,create_info:CreateMapfixRequest)->Result<(),Error>{ - let create_result=self.create_inner(CreateRequest{ + async fn create_mapfix_inner(&self,create_info:CreateMapfixRequest)->Result<(),Error>{ + // call deduplicated inner code + let create_request=self.create_inner(CreateRequest{ ModelID:create_info.ModelID, - }).await; + }).await.map_err(Error::Create)?; - match create_result{ - Ok(create_request)=>{ - // call create on api - self.api.create_mapfix(submissions_api::types::CreateMapfixRequest{ - OperationID:create_info.OperationID, - AssetOwner:create_request.AssetOwner, - DisplayName:create_request.DisplayName.as_str(), - Creator:create_request.Creator.as_str(), - GameID:create_request.GameID, - AssetID:create_info.ModelID, - AssetVersion:create_request.AssetVersion, - TargetAssetID:create_info.TargetAssetID, - }).await.map_err(Error::ApiActionMapfixCreate)?; - }, - Err(e)=>{ - self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{ - OperationID:create_info.OperationID, - StatusMessage:format!("{e}"), - }).await.map_err(Error::ApiActionOperationFailed)?; - }, + // call create on api + self.api.create_mapfix(submissions_api::types::CreateMapfixRequest{ + OperationID:create_info.OperationID, + AssetOwner:create_request.AssetOwner, + DisplayName:create_request.DisplayName.as_str(), + Creator:create_request.Creator.as_str(), + GameID:create_request.GameID, + AssetID:create_info.ModelID, + AssetVersion:create_request.AssetVersion, + TargetAssetID:create_info.TargetAssetID, + }).await.map_err(Error::ApiActionMapfixCreate)?; + + Ok(()) + } + pub async fn create_mapfix(&self,create_info:CreateMapfixRequest)->Result<(),submissions_api::Error>{ + let operation_id=create_info.OperationID; + + let create_result=self.create_mapfix_inner(create_info).await; + + if let Err(e)=create_result{ + self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{ + OperationID:operation_id, + StatusMessage:format!("{e}"), + }).await?; } Ok(()) diff --git a/validation/src/create_submission.rs b/validation/src/create_submission.rs index 31f2a0e..4949366 100644 --- a/validation/src/create_submission.rs +++ b/validation/src/create_submission.rs @@ -4,8 +4,8 @@ use crate::create::CreateRequest; #[allow(dead_code)] #[derive(Debug)] pub enum Error{ + Create(crate::create::Error), ApiActionSubmissionCreate(submissions_api::Error), - ApiActionOperationFailed(submissions_api::Error), } impl std::fmt::Display for Error{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ @@ -15,30 +15,33 @@ impl std::fmt::Display for Error{ impl std::error::Error for Error{} impl crate::message_handler::MessageHandler{ - pub async fn create_submission(&self,create_info:CreateSubmissionRequest)->Result<(),Error>{ - let create_result=self.create_inner(CreateRequest{ + async fn create_submission_inner(&self,create_info:CreateSubmissionRequest)->Result<(),Error>{ + let create_request=self.create_inner(CreateRequest{ ModelID:create_info.ModelID, - }).await; + }).await.map_err(Error::Create)?; + // call create on api + self.api.create_submission(submissions_api::types::CreateSubmissionRequest{ + OperationID:create_info.OperationID, + AssetOwner:create_request.AssetOwner, + DisplayName:create_request.DisplayName.as_str(), + Creator:create_request.Creator.as_str(), + GameID:create_request.GameID, + AssetID:create_info.ModelID, + AssetVersion:create_request.AssetVersion, + }).await.map_err(Error::ApiActionSubmissionCreate)?; - match create_result{ - Ok(create_request)=>{ - // call create on api - self.api.create_submission(submissions_api::types::CreateSubmissionRequest{ - OperationID:create_info.OperationID, - AssetOwner:create_request.AssetOwner, - DisplayName:create_request.DisplayName.as_str(), - Creator:create_request.Creator.as_str(), - GameID:create_request.GameID, - AssetID:create_info.ModelID, - AssetVersion:create_request.AssetVersion, - }).await.map_err(Error::ApiActionSubmissionCreate)?; - }, - Err(e)=>{ - self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{ - OperationID:create_info.OperationID, - StatusMessage:format!("{e}"), - }).await.map_err(Error::ApiActionOperationFailed)?; - }, + Ok(()) + } + pub async fn create_submission(&self,create_info:CreateSubmissionRequest)->Result<(),submissions_api::Error>{ + let operation_id=create_info.OperationID; + + let create_result=self.create_submission_inner(create_info).await; + + if let Err(e)=create_result{ + self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{ + OperationID:operation_id, + StatusMessage:format!("{e}"), + }).await?; } Ok(()) diff --git a/validation/src/message_handler.rs b/validation/src/message_handler.rs index bff0f10..fe79ee5 100644 --- a/validation/src/message_handler.rs +++ b/validation/src/message_handler.rs @@ -5,8 +5,8 @@ pub enum HandleMessageError{ DoubleAck(async_nats::Error), Json(serde_json::Error), UnknownSubject(String), - CreateMapfix(crate::create_mapfix::Error), - CreateSubmission(crate::create_submission::Error), + CreateMapfix(submissions_api::Error), + CreateSubmission(submissions_api::Error), UploadMapfix(crate::upload_mapfix::Error), UploadSubmission(crate::upload_submission::Error), ValidateMapfix(crate::validate_mapfix::Error),