You've already forked AstralRinth
forked from didirus/AstralRinth
Make mod creation always create initial versions & don't require mod id for mod creation versions (#79)
* Make mod creation always create initial versions, other fixes * Fix sqlx prepare Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com>
This commit is contained in:
@@ -519,26 +519,6 @@
|
|||||||
"nullable": []
|
"nullable": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"40597b84607e77809c13ffa9c6b0b1674bd6378a4737a8f6118e91ae2ede7e4a": {
|
|
||||||
"query": "\n SELECT id\n FROM release_channels\n WHERE channel = $1\n ",
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"ordinal": 0,
|
|
||||||
"name": "id",
|
|
||||||
"type_info": "Int4"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nullable": [
|
|
||||||
false
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"4411f2aefd43881450da34db81e826110ac86c3a6cef9fd6a3e9e341508d1f09": {
|
"4411f2aefd43881450da34db81e826110ac86c3a6cef9fd6a3e9e341508d1f09": {
|
||||||
"query": "\n SELECT id FROM versions\n WHERE mod_id = $1\n ",
|
"query": "\n SELECT id FROM versions\n WHERE mod_id = $1\n ",
|
||||||
"describe": {
|
"describe": {
|
||||||
@@ -1498,6 +1478,26 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"d88b160963bffbf3297de4418a38f4c914819fff94b912fab451b892c7494370": {
|
||||||
|
"query": "\n SELECT id\n FROM release_channels\n WHERE channel = $1\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Int4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"d8b4e7e382c77a05395124d5a6a27cccb687d0e2c31b76d49b03aa364d099d42": {
|
"d8b4e7e382c77a05395124d5a6a27cccb687d0e2c31b76d49b03aa364d099d42": {
|
||||||
"query": "\n DELETE FROM files\n WHERE files.version_id = $1\n ",
|
"query": "\n DELETE FROM files\n WHERE files.version_id = $1\n ",
|
||||||
"describe": {
|
"describe": {
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ async fn mod_create_inner(
|
|||||||
) -> Result<HttpResponse, CreateError> {
|
) -> Result<HttpResponse, CreateError> {
|
||||||
let cdn_url = dotenv::var("CDN_URL")?;
|
let cdn_url = dotenv::var("CDN_URL")?;
|
||||||
|
|
||||||
let mod_id = models::generate_mod_id(transaction).await?.into();
|
let mod_id: ModId = models::generate_mod_id(transaction).await?.into();
|
||||||
let user = get_user_from_headers(req.headers(), &mut *transaction).await?;
|
let user = get_user_from_headers(req.headers(), &mut *transaction).await?;
|
||||||
|
|
||||||
let mut created_versions: Vec<models::version_item::VersionBuilder> = vec![];
|
let mut created_versions: Vec<models::version_item::VersionBuilder> = vec![];
|
||||||
@@ -208,6 +208,80 @@ async fn mod_create_inner(
|
|||||||
check_length("mod_name", 3, 255, &*create_data.mod_name)?;
|
check_length("mod_name", 3, 255, &*create_data.mod_name)?;
|
||||||
check_length("mod_description", 3, 2048, &*create_data.mod_description)?;
|
check_length("mod_description", 3, 2048, &*create_data.mod_description)?;
|
||||||
|
|
||||||
|
for version_data in &create_data.initial_versions {
|
||||||
|
if version_data.mod_id.is_some() {
|
||||||
|
return Err(CreateError::InvalidInput(String::from(
|
||||||
|
"Found mod id in initial version for new mod",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let version_id: VersionId = models::generate_version_id(transaction).await?.into();
|
||||||
|
|
||||||
|
let body_url = format!("data/{}/changelogs/{}/body.md", mod_id, version_id);
|
||||||
|
|
||||||
|
let uploaded_text = file_host
|
||||||
|
.upload_file(
|
||||||
|
"text/plain",
|
||||||
|
&body_url,
|
||||||
|
version_data.version_body.clone().into_bytes(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
uploaded_files.push(UploadedFile {
|
||||||
|
file_id: uploaded_text.file_id.clone(),
|
||||||
|
file_name: uploaded_text.file_name.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let release_channel = models::ChannelId(
|
||||||
|
sqlx::query!(
|
||||||
|
"
|
||||||
|
SELECT id
|
||||||
|
FROM release_channels
|
||||||
|
WHERE channel = $1
|
||||||
|
",
|
||||||
|
version_data.release_channel.to_string()
|
||||||
|
)
|
||||||
|
.fetch_one(&mut *transaction)
|
||||||
|
.await?
|
||||||
|
.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut game_versions = Vec::with_capacity(version_data.game_versions.len());
|
||||||
|
for v in &version_data.game_versions {
|
||||||
|
let id = models::categories::GameVersion::get_id(&v.0, &mut *transaction)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| CreateError::InvalidGameVersion(v.0.clone()))?;
|
||||||
|
game_versions.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut loaders = Vec::with_capacity(version_data.loaders.len());
|
||||||
|
for l in &version_data.loaders {
|
||||||
|
let id = models::categories::Loader::get_id(&l.0, &mut *transaction)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| CreateError::InvalidLoader(l.0.clone()))?;
|
||||||
|
loaders.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let version = models::version_item::VersionBuilder {
|
||||||
|
version_id: version_id.into(),
|
||||||
|
mod_id: mod_id.into(),
|
||||||
|
author_id: user.id.into(),
|
||||||
|
name: version_data.version_title.clone(),
|
||||||
|
version_number: version_data.version_number.clone(),
|
||||||
|
changelog_url: Some(format!("{}/{}", cdn_url, body_url)),
|
||||||
|
files: Vec::with_capacity(1),
|
||||||
|
dependencies: version_data
|
||||||
|
.dependencies
|
||||||
|
.iter()
|
||||||
|
.map(|x| (*x).into())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
game_versions,
|
||||||
|
loaders,
|
||||||
|
release_channel,
|
||||||
|
};
|
||||||
|
|
||||||
|
created_versions.push(version);
|
||||||
|
}
|
||||||
|
|
||||||
mod_create_data = Some(create_data);
|
mod_create_data = Some(create_data);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -233,10 +307,11 @@ async fn mod_create_inner(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let version_data = create_data
|
let (version_index, version_data) = create_data
|
||||||
.initial_versions
|
.initial_versions
|
||||||
.iter()
|
.iter()
|
||||||
.find(|x| x.file_parts.iter().any(|n| n == name))
|
.enumerate()
|
||||||
|
.find(|(_, x)| x.file_parts.iter().any(|n| n == name))
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
CreateError::InvalidInput(format!(
|
CreateError::InvalidInput(format!(
|
||||||
"File `{}` (field {}) isn't specified in the versions data",
|
"File `{}` (field {}) isn't specified in the versions data",
|
||||||
@@ -244,83 +319,15 @@ async fn mod_create_inner(
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// If a version has already been created for this version, add the
|
let created_version = if let Some(created_version) = created_versions.get_mut(version_index)
|
||||||
// file to it instead of creating a new version.
|
|
||||||
// Versions must have at least one jar file to be uploaded
|
|
||||||
|
|
||||||
let created_version = if let Some(created_version) = created_versions
|
|
||||||
.iter_mut()
|
|
||||||
.find(|x| x.version_number == version_data.version_number)
|
|
||||||
{
|
{
|
||||||
created_version
|
created_version
|
||||||
} else {
|
} else {
|
||||||
let version_id: VersionId = models::generate_version_id(transaction).await?.into();
|
// This shouldn't be reachable, but better safe than sorry
|
||||||
|
return Err(CreateError::InvalidInput(format!(
|
||||||
let body_url = format!("data/{}/changelogs/{}/body.md", mod_id, version_id);
|
"File `{}` (field {}) isn't specified in the versions data",
|
||||||
|
file_name, name
|
||||||
let uploaded_text = file_host
|
)));
|
||||||
.upload_file(
|
|
||||||
"text/plain",
|
|
||||||
&body_url,
|
|
||||||
version_data.version_body.clone().into_bytes(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
uploaded_files.push(UploadedFile {
|
|
||||||
file_id: uploaded_text.file_id.clone(),
|
|
||||||
file_name: uploaded_text.file_name.clone(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let release_channel = models::ChannelId(
|
|
||||||
sqlx::query!(
|
|
||||||
"
|
|
||||||
SELECT id
|
|
||||||
FROM release_channels
|
|
||||||
WHERE channel = $1
|
|
||||||
",
|
|
||||||
version_data.release_channel.to_string()
|
|
||||||
)
|
|
||||||
.fetch_one(&mut *transaction)
|
|
||||||
.await?
|
|
||||||
.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut game_versions = Vec::with_capacity(version_data.game_versions.len());
|
|
||||||
for v in &version_data.game_versions {
|
|
||||||
let id = models::categories::GameVersion::get_id(&v.0, &mut *transaction)
|
|
||||||
.await?
|
|
||||||
.ok_or_else(|| CreateError::InvalidGameVersion(v.0.clone()))?;
|
|
||||||
game_versions.push(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut loaders = Vec::with_capacity(version_data.loaders.len());
|
|
||||||
for l in &version_data.loaders {
|
|
||||||
let id = models::categories::Loader::get_id(&l.0, &mut *transaction)
|
|
||||||
.await?
|
|
||||||
.ok_or_else(|| CreateError::InvalidLoader(l.0.clone()))?;
|
|
||||||
loaders.push(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
let version = models::version_item::VersionBuilder {
|
|
||||||
version_id: version_id.into(),
|
|
||||||
mod_id: mod_id.into(),
|
|
||||||
author_id: user.id.into(),
|
|
||||||
name: version_data.version_title.clone(),
|
|
||||||
version_number: version_data.version_number.clone(),
|
|
||||||
changelog_url: Some(format!("{}/{}", cdn_url, body_url)),
|
|
||||||
files: Vec::with_capacity(1),
|
|
||||||
dependencies: version_data
|
|
||||||
.dependencies
|
|
||||||
.iter()
|
|
||||||
.map(|x| (*x).into())
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
game_versions,
|
|
||||||
loaders,
|
|
||||||
release_channel,
|
|
||||||
};
|
|
||||||
|
|
||||||
created_versions.push(version);
|
|
||||||
created_versions.last_mut().unwrap()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Upload the new jar file
|
// Upload the new jar file
|
||||||
@@ -342,11 +349,23 @@ async fn mod_create_inner(
|
|||||||
let create_data = if let Some(create_data) = mod_create_data {
|
let create_data = if let Some(create_data) = mod_create_data {
|
||||||
create_data
|
create_data
|
||||||
} else {
|
} else {
|
||||||
return Err(CreateError::InvalidInput(String::from(
|
return Err(CreateError::MissingValueError(String::from(
|
||||||
"Multipart upload missing `data` field",
|
"Multipart upload missing `data` field",
|
||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (version_data, builder) in create_data
|
||||||
|
.initial_versions
|
||||||
|
.iter()
|
||||||
|
.zip(created_versions.iter())
|
||||||
|
{
|
||||||
|
if version_data.file_parts.len() != builder.files.len() {
|
||||||
|
return Err(CreateError::InvalidInput(String::from(
|
||||||
|
"Some files were specified in initial_versions but not uploaded",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let ids: Vec<UserId> = (&create_data.team_members)
|
let ids: Vec<UserId> = (&create_data.team_members)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|m| m.user_id)
|
.map(|m| m.user_id)
|
||||||
@@ -561,10 +580,11 @@ fn check_length(
|
|||||||
max_length: usize,
|
max_length: usize,
|
||||||
string: &str,
|
string: &str,
|
||||||
) -> Result<(), CreateError> {
|
) -> Result<(), CreateError> {
|
||||||
if string.len() > max_length || string.len() < min_length {
|
let length = string.len();
|
||||||
|
if length > max_length || length < min_length {
|
||||||
Err(CreateError::InvalidInput(format!(
|
Err(CreateError::InvalidInput(format!(
|
||||||
"The {} must be between {} and {} characters; got {}.",
|
"The {} must be between {} and {} characters; got {}.",
|
||||||
var_name, string, min_length, max_length
|
var_name, min_length, max_length, length
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use sqlx::postgres::PgPool;
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct InitialVersionData {
|
pub struct InitialVersionData {
|
||||||
pub mod_id: ModId,
|
pub mod_id: Option<ModId>,
|
||||||
pub file_parts: Vec<String>,
|
pub file_parts: Vec<String>,
|
||||||
pub version_number: String,
|
pub version_number: String,
|
||||||
pub version_title: String,
|
pub version_title: String,
|
||||||
@@ -101,7 +101,11 @@ async fn version_create_inner(
|
|||||||
let version_create_data: InitialVersionData = serde_json::from_slice(&data)?;
|
let version_create_data: InitialVersionData = serde_json::from_slice(&data)?;
|
||||||
initial_version_data = Some(version_create_data);
|
initial_version_data = Some(version_create_data);
|
||||||
let version_create_data = initial_version_data.as_ref().unwrap();
|
let version_create_data = initial_version_data.as_ref().unwrap();
|
||||||
let mod_id: models::ModId = version_create_data.mod_id.into();
|
if version_create_data.mod_id.is_none() {
|
||||||
|
return Err(CreateError::MissingValueError("Missing mod id".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mod_id: models::ModId = version_create_data.mod_id.unwrap().into();
|
||||||
|
|
||||||
let results = sqlx::query!(
|
let results = sqlx::query!(
|
||||||
"SELECT EXISTS(SELECT 1 FROM mods WHERE id=$1)",
|
"SELECT EXISTS(SELECT 1 FROM mods WHERE id=$1)",
|
||||||
@@ -152,7 +156,8 @@ async fn version_create_inner(
|
|||||||
let version_id: VersionId = models::generate_version_id(transaction).await?.into();
|
let version_id: VersionId = models::generate_version_id(transaction).await?.into();
|
||||||
let body_url = format!(
|
let body_url = format!(
|
||||||
"data/{}/changelogs/{}/body.md",
|
"data/{}/changelogs/{}/body.md",
|
||||||
version_create_data.mod_id, version_id
|
version_create_data.mod_id.unwrap(),
|
||||||
|
version_id
|
||||||
);
|
);
|
||||||
|
|
||||||
let uploaded_text = file_host
|
let uploaded_text = file_host
|
||||||
@@ -171,10 +176,10 @@ async fn version_create_inner(
|
|||||||
let release_channel = models::ChannelId(
|
let release_channel = models::ChannelId(
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"
|
"
|
||||||
SELECT id
|
SELECT id
|
||||||
FROM release_channels
|
FROM release_channels
|
||||||
WHERE channel = $1
|
WHERE channel = $1
|
||||||
",
|
",
|
||||||
version_create_data.release_channel.to_string()
|
version_create_data.release_channel.to_string()
|
||||||
)
|
)
|
||||||
.fetch_one(&mut *transaction)
|
.fetch_one(&mut *transaction)
|
||||||
@@ -200,7 +205,7 @@ async fn version_create_inner(
|
|||||||
|
|
||||||
version_builder = Some(VersionBuilder {
|
version_builder = Some(VersionBuilder {
|
||||||
version_id: version_id.into(),
|
version_id: version_id.into(),
|
||||||
mod_id: version_create_data.mod_id.into(),
|
mod_id: version_create_data.mod_id.unwrap().into(),
|
||||||
author_id: user.id.into(),
|
author_id: user.id.into(),
|
||||||
name: version_create_data.version_title.clone(),
|
name: version_create_data.version_title.clone(),
|
||||||
version_number: version_create_data.version_number.clone(),
|
version_number: version_create_data.version_number.clone(),
|
||||||
|
|||||||
Reference in New Issue
Block a user