Compare commits

...

4 Commits

12 changed files with 159 additions and 121 deletions

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"time" "time"
"git.itzana.me/strafesnet/go-grpc/maps"
"git.itzana.me/strafesnet/maps-service/pkg/api" "git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore" "git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model" "git.itzana.me/strafesnet/maps-service/pkg/model"
@ -63,6 +64,18 @@ func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTrigger
return nil, ErrCreationPhaseMapfixesLimit return nil, ErrCreationPhaseMapfixesLimit
} }
} }
// Check if TargetAssetID actually exists
{
_, err := svc.Client.Get(ctx, &maps.IdMessage{
ID: request.TargetAssetID,
})
if err != nil {
// TODO: match specific does not exist grpc error
return nil, err
}
}
operation, err := svc.DB.Operations().Create(ctx, model.Operation{ operation, err := svc.DB.Operations().Create(ctx, model.Operation{
Owner: int64(userId), Owner: int64(userId),
StatusID: model.OperationStatusCreated, StatusID: model.OperationStatusCreated,

@ -0,0 +1,30 @@
use crate::nats_types::CreateMapfixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Get(rbx_asset::cookie::GetError),
ApiActionMapfixCreate(submissions_api::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl crate::message_handler::MessageHandler{
pub async fn create_mapfix(&self,create_info:CreateMapfixRequest)->Result<(),Error>{
// download the map model version
let model_data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:create_info.ModelID,
version:None,
}).await.map_err(Error::Get)?;
// parse create fields out of asset
// call create on api
Ok(())
}
}

@ -0,0 +1,30 @@
use crate::nats_types::CreateSubmissionRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Get(rbx_asset::cookie::GetError),
ApiActionSubmissionCreate(submissions_api::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl crate::message_handler::MessageHandler{
pub async fn create_submission(&self,create_info:CreateSubmissionRequest)->Result<(),Error>{
// download the map model version
let model_data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:create_info.ModelID,
version:None,
}).await.map_err(Error::Get)?;
// parse create fields out of asset
// call create on api
Ok(())
}
}

@ -3,7 +3,8 @@ use futures::StreamExt;
mod message_handler; mod message_handler;
mod nats_types; mod nats_types;
mod types; mod types;
mod uploader; mod create_mapfix;
mod create_submission;
mod upload_mapfix; mod upload_mapfix;
mod upload_submission; mod upload_submission;
mod validator; mod validator;
@ -84,7 +85,7 @@ async fn main()->Result<(),StartupError>{
Err(e)=>println!("[Validation] There was an error, oopsie! {e}"), Err(e)=>println!("[Validation] There was an error, oopsie! {e}"),
} }
// explicitly call drop to make the move semantics and permit release more obvious // explicitly call drop to make the move semantics and permit release more obvious
core::mem::drop(permit); drop(permit);
}); });
} }
}; };

@ -5,10 +5,12 @@ pub enum HandleMessageError{
DoubleAck(async_nats::Error), DoubleAck(async_nats::Error),
Json(serde_json::Error), Json(serde_json::Error),
UnknownSubject(String), UnknownSubject(String),
UploadMapfix(crate::upload_mapfix::UploadError), CreateMapfix(crate::create_mapfix::Error),
UploadSubmission(crate::upload_submission::UploadError), CreateSubmission(crate::create_submission::Error),
ValidateMapfix(crate::validate_mapfix::ValidateMapfixError), UploadMapfix(crate::upload_mapfix::Error),
ValidateSubmission(crate::validate_submission::ValidateSubmissionError), UploadSubmission(crate::upload_submission::Error),
ValidateMapfix(crate::validate_mapfix::Error),
ValidateSubmission(crate::validate_submission::Error),
} }
impl std::fmt::Display for HandleMessageError{ impl std::fmt::Display for HandleMessageError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@ -24,10 +26,9 @@ fn from_slice<'a,T:serde::de::Deserialize<'a>>(slice:&'a [u8])->Result<T,HandleM
} }
pub struct MessageHandler{ pub struct MessageHandler{
upload_mapfix:crate::upload_mapfix::Uploader, pub(crate) cookie_context:rbx_asset::cookie::CookieContext,
upload_submission:crate::upload_submission::Uploader, pub(crate) group_id:Option<u64>,
validate_mapfix:crate::validate_mapfix::Validator, pub(crate) api:submissions_api::internal::Context,
validate_submission:crate::validate_submission::Validator,
} }
impl MessageHandler{ impl MessageHandler{
@ -37,20 +38,21 @@ impl MessageHandler{
api:submissions_api::internal::Context, api:submissions_api::internal::Context,
)->Self{ )->Self{
Self{ Self{
upload_mapfix:crate::upload_mapfix::Uploader::new(crate::uploader::Uploader::new(cookie_context.clone(),group_id,api.clone())), cookie_context,
upload_submission:crate::upload_submission::Uploader::new(crate::uploader::Uploader::new(cookie_context.clone(),group_id,api.clone())), group_id,
validate_mapfix:crate::validate_mapfix::Validator::new(crate::validator::Validator::new(cookie_context.clone(),api.clone())), api,
validate_submission:crate::validate_submission::Validator::new(crate::validator::Validator::new(cookie_context,api)),
} }
} }
pub async fn handle_message_result(&self,message_result:MessageResult)->Result<(),HandleMessageError>{ pub async fn handle_message_result(&self,message_result:MessageResult)->Result<(),HandleMessageError>{
let message=message_result.map_err(HandleMessageError::Messages)?; let message=message_result.map_err(HandleMessageError::Messages)?;
message.double_ack().await.map_err(HandleMessageError::DoubleAck)?; message.double_ack().await.map_err(HandleMessageError::DoubleAck)?;
match message.subject.as_str(){ match message.subject.as_str(){
"maptest.mapfixes.upload"=>self.upload_mapfix.upload(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadMapfix), "maptest.mapfixes.create"=>self.create_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::CreateMapfix),
"maptest.submissions.upload"=>self.upload_submission.upload(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadSubmission), "maptest.submissions.create"=>self.create_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::CreateSubmission),
"maptest.mapfixes.validate"=>self.validate_mapfix.validate(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateMapfix), "maptest.mapfixes.upload"=>self.upload_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadMapfix),
"maptest.submissions.validate"=>self.validate_submission.validate(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateSubmission), "maptest.submissions.upload"=>self.upload_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadSubmission),
"maptest.mapfixes.validate"=>self.validate_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateMapfix),
"maptest.submissions.validate"=>self.validate_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateSubmission),
other=>Err(HandleMessageError::UnknownSubject(other.to_owned())) other=>Err(HandleMessageError::UnknownSubject(other.to_owned()))
} }
} }

@ -4,6 +4,22 @@
// Requests are sent from maps-service to validator // Requests are sent from maps-service to validator
// Validation invokes the REST api to update the submissions // Validation invokes the REST api to update the submissions
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct CreateSubmissionRequest{
// operation_id is passed back in the response message
pub OperationID:i64,
pub ModelID:u64,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct CreateMapfixRequest{
pub OperationID:i64,
pub ModelID:u64,
pub TargetAssetID:u64,
}
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct ValidateSubmissionRequest{ pub struct ValidateSubmissionRequest{

@ -2,48 +2,43 @@ use crate::nats_types::UploadMapfixRequest;
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
pub enum UploadError{ pub enum Error{
Get(rbx_asset::cookie::GetError), Get(rbx_asset::cookie::GetError),
Json(serde_json::Error), Json(serde_json::Error),
Upload(rbx_asset::cookie::UploadError), Upload(rbx_asset::cookie::UploadError),
ApiActionMapfixUploaded(submissions_api::Error), ApiActionMapfixUploaded(submissions_api::Error),
} }
impl std::fmt::Display for UploadError{ impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}") write!(f,"{self:?}")
} }
} }
impl std::error::Error for UploadError{} impl std::error::Error for Error{}
pub struct Uploader(crate::uploader::Uploader); impl crate::message_handler::MessageHandler{
impl Uploader{ pub async fn upload_mapfix(&self,upload_info:UploadMapfixRequest)->Result<(),Error>{
pub const fn new(inner:crate::uploader::Uploader)->Self{
Self(inner)
}
pub async fn upload(&self,upload_info:UploadMapfixRequest)->Result<(),UploadError>{
let Self(uploader)=self;
// download the map model version // download the map model version
let model_data=uploader.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{ let model_data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:upload_info.ModelID, asset_id:upload_info.ModelID,
version:Some(upload_info.ModelVersion), version:Some(upload_info.ModelVersion),
}).await.map_err(UploadError::Get)?; }).await.map_err(Error::Get)?;
// upload the map to the strafesnet group // upload the map to the strafesnet group
let _upload_response=uploader.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{ let _upload_response=self.cookie_context.upload(rbx_asset::cookie::UploadRequest{
assetid:upload_info.TargetAssetID, assetid:upload_info.TargetAssetID,
groupId:uploader.group_id, groupId:self.group_id,
name:None, name:None,
description:None, description:None,
ispublic:None, ispublic:None,
allowComments:None, allowComments:None,
},model_data).await.map_err(UploadError::Upload)?; },model_data).await.map_err(Error::Upload)?;
// that's it, the database entry does not need to be changed. // that's it, the database entry does not need to be changed.
// mark mapfix as uploaded, TargetAssetID is unchanged // mark mapfix as uploaded, TargetAssetID is unchanged
uploader.api.action_mapfix_uploaded(submissions_api::types::ActionMapfixUploadedRequest{ self.api.action_mapfix_uploaded(submissions_api::types::ActionMapfixUploadedRequest{
MapfixID:upload_info.MapfixID, MapfixID:upload_info.MapfixID,
}).await.map_err(UploadError::ApiActionMapfixUploaded)?; }).await.map_err(Error::ApiActionMapfixUploaded)?;
Ok(()) Ok(())
} }

@ -2,47 +2,42 @@ use crate::nats_types::UploadSubmissionRequest;
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
pub enum UploadError{ pub enum Error{
Get(rbx_asset::cookie::GetError), Get(rbx_asset::cookie::GetError),
Json(serde_json::Error), Json(serde_json::Error),
Create(rbx_asset::cookie::CreateError), Create(rbx_asset::cookie::CreateError),
SystemTime(std::time::SystemTimeError), SystemTime(std::time::SystemTimeError),
ApiActionSubmissionUploaded(submissions_api::Error), ApiActionSubmissionUploaded(submissions_api::Error),
} }
impl std::fmt::Display for UploadError{ impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}") write!(f,"{self:?}")
} }
} }
impl std::error::Error for UploadError{} impl std::error::Error for Error{}
pub struct Uploader(crate::uploader::Uploader); impl crate::message_handler::MessageHandler{
impl Uploader{ pub async fn upload_submission(&self,upload_info:UploadSubmissionRequest)->Result<(),Error>{
pub const fn new(inner:crate::uploader::Uploader)->Self{
Self(inner)
}
pub async fn upload(&self,upload_info:UploadSubmissionRequest)->Result<(),UploadError>{
let Self(uploader)=self;
// download the map model version // download the map model version
let model_data=uploader.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{ let model_data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:upload_info.ModelID, asset_id:upload_info.ModelID,
version:Some(upload_info.ModelVersion), version:Some(upload_info.ModelVersion),
}).await.map_err(UploadError::Get)?; }).await.map_err(Error::Get)?;
// upload the map to the strafesnet group // upload the map to the strafesnet group
let upload_response=uploader.roblox_cookie.create(rbx_asset::cookie::CreateRequest{ let upload_response=self.cookie_context.create(rbx_asset::cookie::CreateRequest{
name:upload_info.ModelName.clone(), name:upload_info.ModelName.clone(),
description:"".to_owned(), description:"".to_owned(),
ispublic:false, ispublic:false,
allowComments:false, allowComments:false,
groupId:uploader.group_id, groupId:self.group_id,
},model_data).await.map_err(UploadError::Create)?; },model_data).await.map_err(Error::Create)?;
// note the asset id of the created model for later release, and mark the submission as uploaded // note the asset id of the created model for later release, and mark the submission as uploaded
uploader.api.action_submission_uploaded(submissions_api::types::ActionSubmissionUploadedRequest{ self.api.action_submission_uploaded(submissions_api::types::ActionSubmissionUploadedRequest{
SubmissionID:upload_info.SubmissionID, SubmissionID:upload_info.SubmissionID,
UploadedAssetID:upload_response.AssetId, UploadedAssetID:upload_response.AssetId,
}).await.map_err(UploadError::ApiActionSubmissionUploaded)?; }).await.map_err(Error::ApiActionSubmissionUploaded)?;
Ok(()) Ok(())
} }

@ -1,18 +0,0 @@
pub struct Uploader{
pub(crate) roblox_cookie:rbx_asset::cookie::CookieContext,
pub(crate) group_id:Option<u64>,
pub(crate) api:submissions_api::internal::Context,
}
impl Uploader{
pub const fn new(
roblox_cookie:rbx_asset::cookie::CookieContext,
group_id:Option<u64>,
api:submissions_api::internal::Context,
)->Self{
Self{
roblox_cookie,
group_id,
api,
}
}
}

@ -2,41 +2,35 @@ use crate::nats_types::ValidateMapfixRequest;
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
pub enum ValidateMapfixError{ pub enum Error{
ApiActionMapfixValidate(submissions_api::Error), ApiActionMapfixValidate(submissions_api::Error),
} }
impl std::fmt::Display for ValidateMapfixError{ impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}") write!(f,"{self:?}")
} }
} }
impl std::error::Error for ValidateMapfixError{} impl std::error::Error for Error{}
pub struct Validator(crate::validator::Validator);
impl Validator{
pub const fn new(inner:crate::validator::Validator)->Self{
Self(inner)
}
pub async fn validate(&self,validate_info:ValidateMapfixRequest)->Result<(),ValidateMapfixError>{
let Self(validator)=self;
impl crate::message_handler::MessageHandler{
pub async fn validate_mapfix(&self,validate_info:ValidateMapfixRequest)->Result<(),Error>{
let mapfix_id=validate_info.MapfixID; let mapfix_id=validate_info.MapfixID;
let validate_result=validator.validate(validate_info.into()).await; let validate_result=self.validate_inner(validate_info.into()).await;
// update the mapfix depending on the result // update the mapfix depending on the result
match &validate_result{ match &validate_result{
Ok(())=>{ Ok(())=>{
// update the mapfix model status to validated // update the mapfix model status to validated
validator.api.action_mapfix_validated( self.api.action_mapfix_validated(
submissions_api::types::MapfixID(mapfix_id) submissions_api::types::MapfixID(mapfix_id)
).await.map_err(ValidateMapfixError::ApiActionMapfixValidate)?; ).await.map_err(Error::ApiActionMapfixValidate)?;
}, },
Err(e)=>{ Err(e)=>{
// update the mapfix model status to accepted // update the mapfix model status to accepted
validator.api.action_mapfix_accepted(submissions_api::types::ActionMapfixAcceptedRequest{ self.api.action_mapfix_accepted(submissions_api::types::ActionMapfixAcceptedRequest{
MapfixID:mapfix_id, MapfixID:mapfix_id,
StatusMessage:format!("{e}"), StatusMessage:format!("{e}"),
}).await.map_err(ValidateMapfixError::ApiActionMapfixValidate)?; }).await.map_err(Error::ApiActionMapfixValidate)?;
}, },
} }

@ -2,41 +2,35 @@ use crate::nats_types::ValidateSubmissionRequest;
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
pub enum ValidateSubmissionError{ pub enum Error{
ApiActionSubmissionValidate(submissions_api::Error), ApiActionSubmissionValidate(submissions_api::Error),
} }
impl std::fmt::Display for ValidateSubmissionError{ impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}") write!(f,"{self:?}")
} }
} }
impl std::error::Error for ValidateSubmissionError{} impl std::error::Error for Error{}
pub struct Validator(crate::validator::Validator);
impl Validator{
pub const fn new(inner:crate::validator::Validator)->Self{
Self(inner)
}
pub async fn validate(&self,validate_info:ValidateSubmissionRequest)->Result<(),ValidateSubmissionError>{
let Self(validator)=self;
impl crate::message_handler::MessageHandler{
pub async fn validate_submission(&self,validate_info:ValidateSubmissionRequest)->Result<(),Error>{
let submission_id=validate_info.SubmissionID; let submission_id=validate_info.SubmissionID;
let validate_result=validator.validate(validate_info.into()).await; let validate_result=self.validate_inner(validate_info.into()).await;
// update the submission depending on the result // update the submission depending on the result
match &validate_result{ match &validate_result{
Ok(())=>{ Ok(())=>{
// update the submission model status to validated // update the submission model status to validated
validator.api.action_submission_validated( self.api.action_submission_validated(
submissions_api::types::SubmissionID(submission_id) submissions_api::types::SubmissionID(submission_id)
).await.map_err(ValidateSubmissionError::ApiActionSubmissionValidate)?; ).await.map_err(Error::ApiActionSubmissionValidate)?;
}, },
Err(e)=>{ Err(e)=>{
// update the submission model status to accepted // update the submission model status to accepted
validator.api.action_submission_accepted(submissions_api::types::ActionSubmissionAcceptedRequest{ self.api.action_submission_accepted(submissions_api::types::ActionSubmissionAcceptedRequest{
SubmissionID:submission_id, SubmissionID:submission_id,
StatusMessage:format!("{e}"), StatusMessage:format!("{e}"),
}).await.map_err(ValidateSubmissionError::ApiActionSubmissionValidate)?; }).await.map_err(Error::ApiActionSubmissionValidate)?;
}, },
} }

@ -86,24 +86,10 @@ impl From<crate::nats_types::ValidateSubmissionRequest> for ValidateRequest{
} }
} }
pub struct Validator{ impl crate::message_handler::MessageHandler{
pub(crate) roblox_cookie:rbx_asset::cookie::CookieContext, pub async fn validate_inner(&self,validate_info:ValidateRequest)->Result<(),ValidateError>{
pub(crate) api:submissions_api::internal::Context,
}
impl Validator{
pub const fn new(
roblox_cookie:rbx_asset::cookie::CookieContext,
api:submissions_api::internal::Context,
)->Self{
Self{
roblox_cookie,
api,
}
}
pub async fn validate(&self,validate_info:ValidateRequest)->Result<(),ValidateError>{
// download map // download map
let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{ let data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:validate_info.ModelID, asset_id:validate_info.ModelID,
version:Some(validate_info.ModelVersion), version:Some(validate_info.ModelVersion),
}).await.map_err(ValidateError::ModelFileDownload)?; }).await.map_err(ValidateError::ModelFileDownload)?;
@ -238,7 +224,7 @@ impl Validator{
// upload a model lol // upload a model lol
let model_id=if let Some(model_id)=validate_info.ValidatedModelID{ let model_id=if let Some(model_id)=validate_info.ValidatedModelID{
// upload to existing id // upload to existing id
let response=self.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{ let response=self.cookie_context.upload(rbx_asset::cookie::UploadRequest{
assetid:model_id, assetid:model_id,
name:None, name:None,
description:None, description:None,
@ -254,7 +240,7 @@ impl Validator{
return Err(ValidateError::ModelFileChildRefIsNil); return Err(ValidateError::ModelFileChildRefIsNil);
}; };
// create new model // create new model
let response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{ let response=self.cookie_context.create(rbx_asset::cookie::CreateRequest{
name:map_instance.name.clone(), name:map_instance.name.clone(),
description:"".to_owned(), description:"".to_owned(),
ispublic:true, ispublic:true,