submissions-api: Add Releaser Endpoints #194

Merged
Quaternions merged 21 commits from pr1 into staging 2025-06-11 04:11:22 +00:00
10 changed files with 212 additions and 13 deletions

3
Cargo.lock generated
View File

@@ -1903,8 +1903,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "submissions-api"
version = "0.7.2"
version = "0.8.0"
dependencies = [
"chrono",
"reqwest",
"serde",
"serde_json",

View File

@@ -1,6 +1,6 @@
[package]
name = "submissions-api"
version = "0.7.2"
version = "0.8.0"
edition = "2021"
publish = ["strafesnet"]
repository = "https://git.itzana.me/StrafesNET/maps-service"
@@ -11,6 +11,7 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = { version = "0.4.41", features = ["serde"] }
reqwest = { version = "0", features = ["json"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

View File

@@ -150,6 +150,84 @@ impl Context{
self.0.delete(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
pub async fn get_submissions(&self,config:GetSubmissionsRequest<'_>)->Result<SubmissionsResponse,Error>{
let url_raw=format!("{}/submissions",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
let mut query_pairs=url.query_pairs_mut();
query_pairs.append_pair("Page",config.Page.to_string().as_str());
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
if let Some(sort)=config.Sort{
query_pairs.append_pair("Sort",(sort as u8).to_string().as_str());
}
if let Some(display_name)=config.DisplayName{
query_pairs.append_pair("DisplayName",display_name);
}
if let Some(creator)=config.Creator{
query_pairs.append_pair("Creator",creator);
}
if let Some(game_id)=config.GameID{
query_pairs.append_pair("GameID",(game_id as u8).to_string().as_str());
}
if let Some(submitter)=config.Submitter{
query_pairs.append_pair("Submitter",submitter.to_string().as_str());
}
if let Some(asset_id)=config.AssetID{
query_pairs.append_pair("AssetID",asset_id.to_string().as_str());
}
if let Some(uploaded_asset_id)=config.UploadedAssetID{
query_pairs.append_pair("UploadedAssetID",uploaded_asset_id.to_string().as_str());
}
if let Some(status_id)=config.StatusID{
query_pairs.append_pair("StatusID",(status_id as u8).to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_maps(&self,config:GetMapsRequest<'_>)->Result<Vec<MapResponse>,Error>{
let url_raw=format!("{}/maps",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
let mut query_pairs=url.query_pairs_mut();
query_pairs.append_pair("Page",config.Page.to_string().as_str());
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
if let Some(sort)=config.Sort{
query_pairs.append_pair("Sort",(sort as u8).to_string().as_str());
}
if let Some(display_name)=config.DisplayName{
query_pairs.append_pair("DisplayName",display_name);
}
if let Some(creator)=config.Creator{
query_pairs.append_pair("Creator",creator);
}
if let Some(game_id)=config.GameID{
query_pairs.append_pair("GameID",(game_id as u8).to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn release_submissions(&self,config:ReleaseRequest<'_>)->Result<(),Error>{
let url_raw=format!("{}/release-submissions",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
let body=serde_json::to_string(config.schedule).map_err(Error::JSON)?;
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
}

View File

@@ -169,7 +169,7 @@ impl Context{
("ModelVersion",config.ModelVersion.to_string().as_str())
("DisplayName",config.DisplayName.as_str())
("Creator",config.Creator.as_str())
("GameID",config.GameID.to_string().as_str())
("GameID",(config.GameID as u8).to_string().as_str())
);
action!("submissions",action_submission_validated,config,SubmissionID,"status/validator-validated",config.0,);
action!("submissions",update_submission_validated_model,config,UpdateSubmissionModelRequest,"validated-model",config.SubmissionID,
@@ -201,7 +201,7 @@ impl Context{
("ModelVersion",config.ModelVersion.to_string().as_str())
("DisplayName",config.DisplayName.as_str())
("Creator",config.Creator.as_str())
("GameID",config.GameID.to_string().as_str())
("GameID",(config.GameID as u8).to_string().as_str())
);
action!("mapfixes",action_mapfix_validated,config,MapfixID,"status/validator-validated",config.0,);
action!("mapfixes",update_mapfix_validated_model,config,UpdateMapfixModelRequest,"validated-model",config.MapfixID,

View File

@@ -67,6 +67,14 @@ pub async fn response_ok(response:reqwest::Response)->Result<reqwest::Response,R
}
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq,Ord,PartialOrd,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
#[repr(u8)]
pub enum GameID{
Bhop=1,
Surf=2,
KreedzClimb=3,
FlyTrials=5,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
@@ -75,7 +83,7 @@ pub struct CreateMapfixRequest<'a>{
pub AssetOwner:i64,
pub DisplayName:&'a str,
pub Creator:&'a str,
pub GameID:i32,
pub GameID:GameID,
pub AssetID:u64,
pub AssetVersion:u64,
pub TargetAssetID:u64,
@@ -94,7 +102,7 @@ pub struct CreateSubmissionRequest<'a>{
pub AssetOwner:i64,
pub DisplayName:&'a str,
pub Creator:&'a str,
pub GameID:i32,
pub GameID:GameID,
pub AssetID:u64,
pub AssetVersion:u64,
pub Status:u32,
@@ -235,6 +243,97 @@ pub struct UpdateSubmissionModelRequest{
pub ModelVersion:u64,
}
#[derive(Clone,Debug)]
pub enum Sort{
Disabled=0,
DisplayNameAscending=1,
DisplayNameDescending=2,
DateAscending=3,
DateDescending=4,
}
#[derive(Clone,Debug,serde_repr::Deserialize_repr)]
#[repr(u8)]
pub enum SubmissionStatus{
// Phase: Creation
UnderConstruction=0,
ChangesRequested=1,
// Phase: Review
Submitting=2,
Submitted=3,
// Phase: Testing
AcceptedUnvalidated=4, // pending script review, can re-trigger validation
Validating=5,
Validated=6,
Uploading=7,
Uploaded=8, // uploaded to the group, but pending release
// Phase: Final SubmissionStatus
Rejected=9,
Released=10,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct GetSubmissionsRequest<'a>{
pub Page:u32,
pub Limit:u32,
pub Sort:Option<Sort>,
pub DisplayName:Option<&'a str>,
pub Creator:Option<&'a str>,
pub GameID:Option<GameID>,
pub Submitter:Option<u64>,
pub AssetID:Option<u64>,
pub UploadedAssetID:Option<u64>,
pub StatusID:Option<SubmissionStatus>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct SubmissionResponse{
pub ID:i64,
pub DisplayName:String,
pub Creator:String,
pub GameID:GameID,
pub CreatedAt:i64,
pub UpdatedAt:i64,
pub Submitter:u64,
pub AssetID:u64,
pub AssetVersion:u64,
pub UploadedAssetID:u64,
pub StatusID:SubmissionStatus,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct SubmissionsResponse{
pub Total:u64,
pub Submissions:Vec<SubmissionResponse>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct GetMapsRequest<'a>{
pub Page:u32,
pub Limit:u32,
pub Sort:Option<Sort>,
pub DisplayName:Option<&'a str>,
pub Creator:Option<&'a str>,
pub GameID:Option<GameID>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct MapResponse{
pub ID:i64,
pub DisplayName:String,
pub Creator:String,
pub GameID:GameID,
pub Date:i64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionSubmittedRequest{
@@ -242,7 +341,7 @@ pub struct ActionSubmissionSubmittedRequest{
pub ModelVersion:u64,
pub DisplayName:String,
pub Creator:String,
pub GameID:u32,
pub GameID:GameID,
}
#[allow(nonstandard_style)]
@@ -266,7 +365,7 @@ pub struct ActionSubmissionAcceptedRequest{
pub ErrorMessage:String,
}
#[derive(Clone,Copy,Debug,serde::Deserialize)]
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
pub struct SubmissionID(pub i64);
#[allow(nonstandard_style)]
@@ -284,7 +383,7 @@ pub struct ActionMapfixSubmittedRequest{
pub ModelVersion:u64,
pub DisplayName:String,
pub Creator:String,
pub GameID:u32,
pub GameID:GameID,
}
#[allow(nonstandard_style)]
@@ -316,3 +415,14 @@ pub struct ActionOperationFailedRequest{
pub OperationID:i32,
pub StatusMessage:String,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct ReleaseInfo{
pub SubmissionID:SubmissionID,
pub Date:chrono::DateTime<chrono::Utc>,
}
pub struct ReleaseRequest<'a>{
pub schedule:&'a [ReleaseInfo],
}

View File

@@ -27,7 +27,7 @@ impl crate::message_handler::MessageHandler{
ModelVersion:version,
DisplayName:map_info.display_name,
Creator:map_info.creator,
GameID:map_info.game_id as u32,
GameID:map_info.game_id.into(),
}
).await.map_err(Error::ApiActionMapfixCheck)?,
// update the mapfix model status to request changes

View File

@@ -28,7 +28,7 @@ impl crate::message_handler::MessageHandler{
ModelVersion:version,
DisplayName:map_info.display_name,
Creator:map_info.creator,
GameID:map_info.game_id as u32,
GameID:map_info.game_id.into(),
}
).await.map_err(Error::ApiActionSubmissionCheck)?,
// update the submission model status to request changes

View File

@@ -28,7 +28,7 @@ impl crate::message_handler::MessageHandler{
DisplayName:create_request.DisplayName.as_deref().unwrap_or_default(),
Creator:create_request.Creator.as_deref().unwrap_or_default(),
// not great TODO: make this great
GameID:create_request.GameID.unwrap_or(crate::rbx_util::GameID::Bhop) as i32,
GameID:create_request.GameID.unwrap_or(crate::rbx_util::GameID::Bhop).into(),
AssetID:create_info.ModelID,
AssetVersion:create_request.AssetVersion,
TargetAssetID:create_info.TargetAssetID,

View File

@@ -42,7 +42,7 @@ impl crate::message_handler::MessageHandler{
AssetOwner:create_request.AssetOwner as i64,
DisplayName:display_name,
Creator:creator,
GameID:game_id as i32,
GameID:game_id.into(),
AssetID:create_info.ModelID,
AssetVersion:create_request.AssetVersion,
Status:create_info.Status,

View File

@@ -37,6 +37,15 @@ pub enum GameID{
Surf=2,
FlyTrials=5,
}
impl From<GameID> for submissions_api::types::GameID{
fn from(value:GameID)->Self{
match value{
GameID::Bhop=>submissions_api::types::GameID::Bhop,
GameID::Surf=>submissions_api::types::GameID::Surf,
GameID::FlyTrials=>submissions_api::types::GameID::FlyTrials,
}
}
}
#[derive(Debug)]
pub struct ParseGameIDError;
impl std::str::FromStr for GameID{