From cf1fdb40992791347a87cc0d41bf5133cd8b340b Mon Sep 17 00:00:00 2001 From: Quaternions Date: Sun, 15 Dec 2024 00:55:11 -0800 Subject: [PATCH] submissions-api-rs: v0.2.0 split internal & external --- submissions-api-rs/Cargo.lock | 2 +- submissions-api-rs/Cargo.toml | 5 +- submissions-api-rs/src/context.rs | 23 +++++ submissions-api-rs/src/external.rs | 86 +++++++++++++++++ submissions-api-rs/src/internal.rs | 48 +++++++++ submissions-api-rs/src/lib.rs | 150 ++--------------------------- validation/Cargo.lock | 4 +- validation/Cargo.toml | 2 +- validation/src/main.rs | 8 +- validation/src/message_handler.rs | 9 +- validation/src/publish_fix.rs | 8 +- validation/src/publish_new.rs | 6 +- validation/src/validator.rs | 27 +++--- 13 files changed, 208 insertions(+), 170 deletions(-) create mode 100644 submissions-api-rs/src/context.rs create mode 100644 submissions-api-rs/src/external.rs create mode 100644 submissions-api-rs/src/internal.rs diff --git a/submissions-api-rs/Cargo.lock b/submissions-api-rs/Cargo.lock index 6dac768..1fa7849 100644 --- a/submissions-api-rs/Cargo.lock +++ b/submissions-api-rs/Cargo.lock @@ -952,7 +952,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "submissions-api" -version = "0.1.1" +version = "0.2.0" dependencies = [ "reqwest", "serde", diff --git a/submissions-api-rs/Cargo.toml b/submissions-api-rs/Cargo.toml index b43aa4d..9f6d33d 100644 --- a/submissions-api-rs/Cargo.toml +++ b/submissions-api-rs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "submissions-api" -version = "0.1.1" +version = "0.2.0" edition = "2021" publish = ["strafesnet"] repository = "https://git.itzana.me/StrafesNET/maps-service" @@ -15,3 +15,6 @@ reqwest = { version = "0", features = ["json"] } serde = { version = "1", features = ["derive"] } serde_json = "1" url = "2" + +[features] +internal = [] diff --git a/submissions-api-rs/src/context.rs b/submissions-api-rs/src/context.rs new file mode 100644 index 0000000..a5e55cd --- /dev/null +++ b/submissions-api-rs/src/context.rs @@ -0,0 +1,23 @@ +#[derive(Clone)] +pub struct Context{ + pub base_url:String, + client:reqwest::Client, +} + +impl Context{ + pub fn new(mut base_url:String)->reqwest::Result{ + base_url+="/v1"; + Ok(Self{ + base_url, + client:reqwest::Client::new(), + }) + } + pub async fn get(&self,url:impl reqwest::IntoUrl)->Result{ + self.client.get(url) + .send().await + } + pub async fn post(&self,url:impl reqwest::IntoUrl)->Result{ + self.client.post(url) + .send().await + } +} diff --git a/submissions-api-rs/src/external.rs b/submissions-api-rs/src/external.rs new file mode 100644 index 0000000..3f431c4 --- /dev/null +++ b/submissions-api-rs/src/external.rs @@ -0,0 +1,86 @@ +use crate::Error; + +#[derive(serde::Deserialize)] +pub struct ScriptID(pub(crate) i64); + +#[allow(nonstandard_style)] +pub struct GetScriptRequest{ + pub ScriptID:ScriptID, +} +#[allow(nonstandard_style)] +#[derive(serde::Deserialize)] +pub struct ScriptResponse{ + pub ID:i64, + pub Hash:String, + pub Source:String, + pub SubmissionID:i64, +} + +#[derive(serde::Deserialize)] +#[repr(i32)] +pub enum Policy{ + None=0, // not yet reviewed + Allowed=1, + Blocked=2, + Delete=3, + Replace=4, +} + +pub struct ScriptPolicyHashRequest{ + pub hash:String, +} +#[allow(nonstandard_style)] +#[derive(serde::Deserialize)] +pub struct ScriptPolicyResponse{ + pub ID:i64, + pub FromScriptHash:String, + pub ToScriptID:ScriptID, + pub Policy:Policy +} + +#[allow(nonstandard_style)] +pub struct UpdateSubmissionModelRequest{ + pub SubmissionID:i64, + pub ModelID:u64, + pub ModelVersion:u64, +} + +#[derive(Clone)] +pub struct Context(crate::context::Context); + +impl Context{ + pub fn new(base_url:String)->reqwest::Result{ + Ok(Self(crate::context::Context::new(base_url)?)) + } + pub async fn get_script(&self,config:GetScriptRequest)->Result{ + let url_raw=format!("{}/scripts/{}",self.0.base_url,config.ScriptID.0); + let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?; + + self.0.get(url).await.map_err(Error::Reqwest)? + .error_for_status().map_err(Error::Reqwest)? + .json().await.map_err(Error::Reqwest) + } + pub async fn get_script_policy_from_hash(&self,config:ScriptPolicyHashRequest)->Result{ + let url_raw=format!("{}/script-policy/hash/{}",self.0.base_url,config.hash); + let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?; + + self.0.get(url).await.map_err(Error::Reqwest)? + .error_for_status().map_err(Error::Reqwest)? + .json().await.map_err(Error::Reqwest) + } + pub async fn update_submission_model(&self,config:UpdateSubmissionModelRequest)->Result<(),Error>{ + let url_raw=format!("{}/submissions/{}/model",self.0.base_url,config.SubmissionID); + let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?; + + { + url.query_pairs_mut() + .append_pair("ModelID",config.ModelID.to_string().as_str()) + .append_pair("ModelVersion",config.ModelVersion.to_string().as_str()); + } + + self.0.post(url).await.map_err(Error::Reqwest)? + .error_for_status().map_err(Error::Reqwest)?; + + Ok(()) + } +} diff --git a/submissions-api-rs/src/internal.rs b/submissions-api-rs/src/internal.rs new file mode 100644 index 0000000..532e631 --- /dev/null +++ b/submissions-api-rs/src/internal.rs @@ -0,0 +1,48 @@ +use crate::Error; + +#[allow(nonstandard_style)] +pub struct ActionSubmissionUploadedRequest{ + pub SubmissionID:i64, + pub TargetAssetID:Option, +} + +pub struct SubmissionID(pub i64); + +#[derive(Clone)] +pub struct Context(crate::context::Context); + +// there are lots of action endpoints and they all follow the same pattern +macro_rules! action{ + ($fname:ident,$action:expr)=>{ + pub async fn $fname(&self,config:SubmissionID)->Result<(),Error>{ + let url_raw=format!(concat!("{}/submissions/{}/status/",$action),self.0.base_url,config.0); + let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?; + + self.0.post(url).await.map_err(Error::Reqwest)? + .error_for_status().map_err(Error::Reqwest)?; + + Ok(()) + } + }; +} +impl Context{ + pub fn new(base_url:String)->reqwest::Result{ + Ok(Self(crate::context::Context::new(base_url)?)) + } + pub async fn action_submission_uploaded(&self,config:ActionSubmissionUploadedRequest)->Result<(),Error>{ + let url_raw=format!("{}/submissions/{}/status/uploaded",self.0.base_url,config.SubmissionID); + let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?; + + if let Some(target_asset_id)=config.TargetAssetID{ + url.query_pairs_mut() + .append_pair("TargetAssetID",target_asset_id.to_string().as_str()); + } + + self.0.post(url).await.map_err(Error::Reqwest)? + .error_for_status().map_err(Error::Reqwest)?; + + Ok(()) + } + action!(action_submission_validated,"validator-validated"); + action!(action_submission_released,"releaser-released"); +} diff --git a/submissions-api-rs/src/lib.rs b/submissions-api-rs/src/lib.rs index 6639031..1af5267 100644 --- a/submissions-api-rs/src/lib.rs +++ b/submissions-api-rs/src/lib.rs @@ -1,3 +1,13 @@ +mod context; + +#[cfg(feature="internal")] +pub mod internal; + +pub mod external; + +//lazy reexport +pub type ReqwestError=reqwest::Error; + #[derive(Debug)] pub enum Error{ ParseError(url::ParseError), @@ -9,143 +19,3 @@ impl std::fmt::Display for Error{ } } impl std::error::Error for Error{} - -#[derive(serde::Deserialize)] -pub struct ScriptID(i64); - -#[allow(nonstandard_style)] -pub struct GetScriptRequest{ - pub ScriptID:ScriptID, -} -#[allow(nonstandard_style)] -#[derive(serde::Deserialize)] -pub struct ScriptResponse{ - pub ID:i64, - pub Hash:String, - pub Source:String, - pub SubmissionID:i64, -} - -#[derive(serde::Deserialize)] -#[repr(i32)] -pub enum Policy{ - None=0, // not yet reviewed - Allowed=1, - Blocked=2, - Delete=3, - Replace=4, -} - -pub struct ScriptPolicyHashRequest{ - pub hash:String, -} -#[allow(nonstandard_style)] -#[derive(serde::Deserialize)] -pub struct ScriptPolicyResponse{ - pub ID:i64, - pub FromScriptHash:String, - pub ToScriptID:ScriptID, - pub Policy:Policy -} - -#[allow(nonstandard_style)] -pub struct UpdateSubmissionModelRequest{ - pub SubmissionID:i64, - pub ModelID:u64, - pub ModelVersion:u64, -} - -pub struct SubmissionID(pub i64); - -#[allow(nonstandard_style)] -pub struct ActionSubmissionUploadedRequest{ - pub SubmissionID:i64, - pub TargetAssetID:Option, -} - -#[derive(Clone)] -pub struct Context{ - base_url:String, - client:reqwest::Client, -} - -pub type ReqwestError=reqwest::Error; - -// there are lots of action endpoints and they all follow the same pattern -macro_rules! action{ - ($fname:ident,$action:expr)=>{ - pub async fn $fname(&self,config:SubmissionID)->Result<(),Error>{ - let url_raw=format!(concat!("{}/submissions/{}/status/",$action),self.base_url,config.0); - let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?; - - self.post(url).await.map_err(Error::Reqwest)? - .error_for_status().map_err(Error::Reqwest)?; - - Ok(()) - } - }; -} -impl Context{ - pub fn new(mut base_url:String)->reqwest::Result{ - base_url+="/v1"; - Ok(Self{ - base_url, - client:reqwest::Client::new(), - }) - } - async fn get(&self,url:impl reqwest::IntoUrl)->Result{ - self.client.get(url) - .send().await - } - async fn post(&self,url:impl reqwest::IntoUrl)->Result{ - self.client.post(url) - .send().await - } - pub async fn get_script(&self,config:GetScriptRequest)->Result{ - let url_raw=format!("{}/scripts/{}",self.base_url,config.ScriptID.0); - let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?; - - self.get(url).await.map_err(Error::Reqwest)? - .error_for_status().map_err(Error::Reqwest)? - .json().await.map_err(Error::Reqwest) - } - pub async fn get_script_policy_from_hash(&self,config:ScriptPolicyHashRequest)->Result{ - let url_raw=format!("{}/script-policy/hash/{}",self.base_url,config.hash); - let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?; - - self.get(url).await.map_err(Error::Reqwest)? - .error_for_status().map_err(Error::Reqwest)? - .json().await.map_err(Error::Reqwest) - } - pub async fn update_submission_model(&self,config:UpdateSubmissionModelRequest)->Result<(),Error>{ - let url_raw=format!("{}/submissions/{}/model",self.base_url,config.SubmissionID); - let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?; - - { - url.query_pairs_mut() - .append_pair("ModelID",config.ModelID.to_string().as_str()) - .append_pair("ModelVersion",config.ModelVersion.to_string().as_str()); - } - - self.post(url).await.map_err(Error::Reqwest)? - .error_for_status().map_err(Error::Reqwest)?; - - Ok(()) - } - pub async fn action_submission_uploaded(&self,config:ActionSubmissionUploadedRequest)->Result<(),Error>{ - let url_raw=format!("{}/submissions/{}/status/uploaded",self.base_url,config.SubmissionID); - let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::ParseError)?; - - if let Some(target_asset_id)=config.TargetAssetID{ - url.query_pairs_mut() - .append_pair("TargetAssetID",target_asset_id.to_string().as_str()); - } - - self.post(url).await.map_err(Error::Reqwest)? - .error_for_status().map_err(Error::Reqwest)?; - - Ok(()) - } - action!(action_submission_validated,"validator-validated"); - action!(action_submission_released,"releaser-released"); -} diff --git a/validation/Cargo.lock b/validation/Cargo.lock index ca2e0a5..baa517a 100644 --- a/validation/Cargo.lock +++ b/validation/Cargo.lock @@ -1804,9 +1804,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "submissions-api" -version = "0.1.1" +version = "0.2.0" source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/" -checksum = "7a56a1a29c7608cab695b426e286a435752321ed718c03199c858b2ab13525e3" +checksum = "9d08d54792de7f1ddabbad0202c66536e14e8965fc3a6f9230179a0270440a08" dependencies = [ "reqwest", "serde", diff --git a/validation/Cargo.toml b/validation/Cargo.toml index d96d68c..4aa4cec 100644 --- a/validation/Cargo.toml +++ b/validation/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -submissions-api = { version = "0.1.1", registry = "strafesnet" } +submissions-api = { version = "0.2.0", features = ["internal"], registry = "strafesnet" } async-nats = "0.38.0" futures = "0.3.31" rbx_asset = { version = "0.2.5", registry = "strafesnet" } diff --git a/validation/src/main.rs b/validation/src/main.rs index 28d87b0..4c0a4f0 100644 --- a/validation/src/main.rs +++ b/validation/src/main.rs @@ -10,6 +10,7 @@ mod message_handler; #[derive(Debug)] pub enum StartupError{ API(submissions_api::ReqwestError), + APIInternal(submissions_api::ReqwestError), NatsConnect(async_nats::ConnectError), NatsGetStream(async_nats::jetstream::context::GetStreamError), NatsConsumer(async_nats::jetstream::stream::ConsumerError), @@ -34,7 +35,10 @@ async fn main()->Result<(),StartupError>{ // maps-service api let api_host=std::env::var("API_HOST").expect("API_HOST env required"); - let api=submissions_api::Context::new(api_host).map_err(StartupError::API)?; + let api=submissions_api::external::Context::new(api_host).map_err(StartupError::API)?; + + let api_host_internal=std::env::var("API_HOST_INTERNAL").expect("API_HOST_INTERNAL env required"); + let api_internal=submissions_api::internal::Context::new(api_host_internal).map_err(StartupError::APIInternal)?; // nats let nats_host=std::env::var("NATS_HOST").expect("NATS_HOST env required"); @@ -52,7 +56,7 @@ async fn main()->Result<(),StartupError>{ .messages().await.map_err(StartupError::NatsStream) }; - let message_handler=message_handler::MessageHandler::new(cookie_context,api); + let message_handler=message_handler::MessageHandler::new(cookie_context,api,api_internal); // Create a signal listener for SIGTERM let mut sig_term=tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()).expect("Failed to create SIGTERM signal listener"); diff --git a/validation/src/message_handler.rs b/validation/src/message_handler.rs index 951c130..3d76870 100644 --- a/validation/src/message_handler.rs +++ b/validation/src/message_handler.rs @@ -26,12 +26,13 @@ pub struct MessageHandler{ impl MessageHandler{ pub fn new( cookie_context:rbx_asset::cookie::CookieContext, - api:submissions_api::Context, + api:submissions_api::external::Context, + api_internal:submissions_api::internal::Context, )->Self{ Self{ - publish_new:crate::publish_new::Publisher::new(cookie_context.clone(),api.clone()), - publish_fix:crate::publish_fix::Publisher::new(cookie_context.clone(),api.clone()), - validator:crate::validator::Validator::new(cookie_context,api), + publish_new:crate::publish_new::Publisher::new(cookie_context.clone(),api_internal.clone()), + publish_fix:crate::publish_fix::Publisher::new(cookie_context.clone(),api_internal.clone()), + validator:crate::validator::Validator::new(cookie_context,api,api_internal), } } pub async fn handle_message_result(&self,message_result:MessageResult)->Result<(),HandleMessageError>{ diff --git a/validation/src/publish_fix.rs b/validation/src/publish_fix.rs index f0e5f2f..c7d9a3d 100644 --- a/validation/src/publish_fix.rs +++ b/validation/src/publish_fix.rs @@ -17,16 +17,16 @@ impl std::error::Error for PublishError{} pub struct Publisher{ roblox_cookie:rbx_asset::cookie::CookieContext, - api:submissions_api::Context, + api_internal:submissions_api::internal::Context, } impl Publisher{ pub const fn new( roblox_cookie:rbx_asset::cookie::CookieContext, - api:submissions_api::Context, + api_internal:submissions_api::internal::Context, )->Self{ Self{ roblox_cookie, - api, + api_internal, } } pub async fn publish(&self,message:async_nats::jetstream::Message)->Result<(),PublishError>{ @@ -53,7 +53,7 @@ impl Publisher{ // that's it, the database entry does not need to be changed. // mark submission as uploaded, TargetAssetID is unchanged - self.api.action_submission_uploaded(submissions_api::ActionSubmissionUploadedRequest{ + self.api_internal.action_submission_uploaded(submissions_api::internal::ActionSubmissionUploadedRequest{ SubmissionID:publish_info.SubmissionID, TargetAssetID:None, }).await.map_err(PublishError::ApiActionSubmissionUploaded)?; diff --git a/validation/src/publish_new.rs b/validation/src/publish_new.rs index 121b57d..30be17b 100644 --- a/validation/src/publish_new.rs +++ b/validation/src/publish_new.rs @@ -18,12 +18,12 @@ impl std::error::Error for PublishError{} pub struct Publisher{ roblox_cookie:rbx_asset::cookie::CookieContext, - api:submissions_api::Context, + api:submissions_api::internal::Context, } impl Publisher{ pub const fn new( roblox_cookie:rbx_asset::cookie::CookieContext, - api:submissions_api::Context, + api:submissions_api::internal::Context, )->Self{ Self{ roblox_cookie, @@ -51,7 +51,7 @@ impl Publisher{ },model_data).await.map_err(PublishError::Create)?; // note the asset id of the created model for later release, and mark the submission as uploaded - self.api.action_submission_uploaded(submissions_api::ActionSubmissionUploadedRequest{ + self.api.action_submission_uploaded(submissions_api::internal::ActionSubmissionUploadedRequest{ SubmissionID:publish_info.SubmissionID, TargetAssetID:Some(upload_response.AssetId), }).await.map_err(PublishError::ApiActionSubmissionPublish)?; diff --git a/validation/src/validator.rs b/validation/src/validator.rs index e96e3c3..bcb3ae5 100644 --- a/validation/src/validator.rs +++ b/validation/src/validator.rs @@ -37,17 +37,20 @@ impl std::error::Error for ValidateError{} pub struct Validator{ roblox_cookie:rbx_asset::cookie::CookieContext, - api:submissions_api::Context, + api:submissions_api::external::Context, + api_internal:submissions_api::internal::Context, } impl Validator{ pub const fn new( roblox_cookie:rbx_asset::cookie::CookieContext, - api:submissions_api::Context, + api:submissions_api::external::Context, + api_internal:submissions_api::internal::Context, )->Self{ Self{ roblox_cookie, api, + api_internal, } } pub async fn validate(&self,message:async_nats::jetstream::Message)->Result<(),ValidateError>{ @@ -86,18 +89,18 @@ impl Validator{ let hash=std::hash::Hasher::finish(&hasher); // fetch the script policy - let script_policy=self.api.get_script_policy_from_hash(submissions_api::ScriptPolicyHashRequest{ + let script_policy=self.api.get_script_policy_from_hash(submissions_api::external::ScriptPolicyHashRequest{ hash:format!("{:x}",hash), }).await.map_err(ValidateError::ApiGetScriptPolicy)?; // write the policy to the script_map, fetching the replacement code if necessary *replacement=match script_policy.Policy{ - submissions_api::Policy::None=>Policy::None, - submissions_api::Policy::Allowed=>Policy::Allowed, - submissions_api::Policy::Blocked=>Policy::Blocked, - submissions_api::Policy::Delete=>Policy::Delete, - submissions_api::Policy::Replace=>{ - let script=self.api.get_script(submissions_api::GetScriptRequest{ + submissions_api::external::Policy::None=>Policy::None, + submissions_api::external::Policy::Allowed=>Policy::Allowed, + submissions_api::external::Policy::Blocked=>Policy::Blocked, + submissions_api::external::Policy::Delete=>Policy::Delete, + submissions_api::external::Policy::Replace=>{ + let script=self.api.get_script(submissions_api::external::GetScriptRequest{ ScriptID:script_policy.ToScriptID, }).await.map_err(ValidateError::ApiGetScript)?; Policy::Replace(script.Source) @@ -166,7 +169,7 @@ impl Validator{ }; // update the submission to use the validated model - self.api.update_submission_model(submissions_api::UpdateSubmissionModelRequest{ + self.api.update_submission_model(submissions_api::external::UpdateSubmissionModelRequest{ SubmissionID:validate_info.SubmissionID, ModelID:model_id, ModelVersion:1, //TODO @@ -174,8 +177,8 @@ impl Validator{ }; // update the submission model status to validated - self.api.action_submission_validated( - submissions_api::SubmissionID(validate_info.SubmissionID) + self.api_internal.action_submission_validated( + submissions_api::internal::SubmissionID(validate_info.SubmissionID) ).await.map_err(ValidateError::ApiActionSubmissionValidate)?; Ok(())