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:
Aeledfyr
2020-10-16 15:28:53 -05:00
committed by GitHub
parent 5c8ffe961e
commit 520b12e56b
3 changed files with 133 additions and 108 deletions

View File

@@ -519,26 +519,6 @@
"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": {
"query": "\n SELECT id FROM versions\n WHERE mod_id = $1\n ",
"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": {
"query": "\n DELETE FROM files\n WHERE files.version_id = $1\n ",
"describe": {

View File

@@ -181,7 +181,7 @@ async fn mod_create_inner(
) -> Result<HttpResponse, CreateError> {
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 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_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);
continue;
}
@@ -233,10 +307,11 @@ async fn mod_create_inner(
continue;
}
let version_data = create_data
let (version_index, version_data) = create_data
.initial_versions
.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(|| {
CreateError::InvalidInput(format!(
"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
// 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)
let created_version = if let Some(created_version) = created_versions.get_mut(version_index)
{
created_version
} else {
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);
created_versions.last_mut().unwrap()
// This shouldn't be reachable, but better safe than sorry
return Err(CreateError::InvalidInput(format!(
"File `{}` (field {}) isn't specified in the versions data",
file_name, name
)));
};
// 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 {
create_data
} else {
return Err(CreateError::InvalidInput(String::from(
return Err(CreateError::MissingValueError(String::from(
"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)
.iter()
.map(|m| m.user_id)
@@ -561,10 +580,11 @@ fn check_length(
max_length: usize,
string: &str,
) -> 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!(
"The {} must be between {} and {} characters; got {}.",
var_name, string, min_length, max_length
var_name, min_length, max_length, length
)))
} else {
Ok(())

View File

@@ -15,7 +15,7 @@ use sqlx::postgres::PgPool;
#[derive(Serialize, Deserialize, Clone)]
pub struct InitialVersionData {
pub mod_id: ModId,
pub mod_id: Option<ModId>,
pub file_parts: Vec<String>,
pub version_number: String,
pub version_title: String,
@@ -101,7 +101,11 @@ async fn version_create_inner(
let version_create_data: InitialVersionData = serde_json::from_slice(&data)?;
initial_version_data = Some(version_create_data);
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!(
"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 body_url = format!(
"data/{}/changelogs/{}/body.md",
version_create_data.mod_id, version_id
version_create_data.mod_id.unwrap(),
version_id
);
let uploaded_text = file_host
@@ -171,10 +176,10 @@ async fn version_create_inner(
let release_channel = models::ChannelId(
sqlx::query!(
"
SELECT id
FROM release_channels
WHERE channel = $1
",
SELECT id
FROM release_channels
WHERE channel = $1
",
version_create_data.release_channel.to_string()
)
.fetch_one(&mut *transaction)
@@ -200,7 +205,7 @@ async fn version_create_inner(
version_builder = Some(VersionBuilder {
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(),
name: version_create_data.version_title.clone(),
version_number: version_create_data.version_number.clone(),