Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API routes to request multiple of an item #70

Merged
merged 13 commits into from
Oct 5, 2020
100 changes: 100 additions & 0 deletions sqlx-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,26 @@
]
}
},
"1220d15a56dbf823eaa452fbafa17442ab0568bc81a31fa38e16e3df3278e5f9": {
"query": "SELECT EXISTS(SELECT 1 FROM users WHERE id = $1)",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "exists",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
null
]
}
},
"1524c0462be70077736ac70fcd037fbf75651456b692e2ce40fa2e3fc8123984": {
"query": "\n SELECT hashes.algorithm, hashes.hash FROM hashes\n WHERE hashes.file_id = $1\n ",
"describe": {
Expand Down Expand Up @@ -589,6 +609,66 @@
"nullable": []
}
},
"76bcdb4b219384447b91ed607645934a3fbb284910172b652964209d5df3799c": {
"query": "\n SELECT team_id FROM team_members\n WHERE user_id = $1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "team_id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false
]
}
},
"7ebc7741c6ab71adab4f037a3b0e8c71af31f77bbde569754f3fddbfa7e8a6fd": {
"query": "\n SELECT id FROM team_members\n WHERE user_id = $1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false
]
}
},
"7f227f6dda23b8811db41b6ea296b29d44dd67b83b067f9b0b572d140f951346": {
"query": "\n SELECT id FROM mods\n WHERE team_id = $1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false
]
}
},
"89fbff6249b248d3e150879aaea1662140bcb10d5104992c784285322c8b3b94": {
"query": "\n SELECT version FROM game_versions\n ",
"describe": {
Expand Down Expand Up @@ -1537,5 +1617,25 @@
null
]
}
},
"fdb2a6ea649bb23c69af5c756d6137e216603708ffccd4e9162fb1c9765a56aa": {
"query": "\n SELECT m.id FROM mods m\n INNER JOIN team_members tm ON tm.team_id = m.team_id\n WHERE tm.user_id = $1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false
]
}
}
}
24 changes: 23 additions & 1 deletion src/database/models/user_item.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::ids::UserId;
use super::ids::{ModId, UserId};

pub struct User {
pub id: UserId,
Expand Down Expand Up @@ -112,4 +112,26 @@ impl User {
Ok(None)
}
}

pub async fn get_mods<'a, E>(user_id: UserId, exec: E) -> Result<Vec<ModId>, sqlx::Error>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
{
use futures::stream::TryStreamExt;

let mods = sqlx::query!(
"
SELECT m.id FROM mods m
INNER JOIN team_members tm ON tm.team_id = m.team_id
WHERE tm.user_id = $1
",
user_id as UserId,
)
.fetch_many(exec)
.try_filter_map(|e| async { Ok(e.right().map(|m| ModId(m.id))) })
.try_collect::<Vec<ModId>>()
.await?;

Ok(mods)
}
}
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ async fn main() -> std::io::Result<()> {
.configure(routes::auth_config)
.configure(routes::tags_config)
.configure(routes::mods_config)
.configure(routes::versions_config)
.configure(routes::users_config),
)
.default_service(web::get().to(routes::not_found))
Expand Down
28 changes: 19 additions & 9 deletions src/routes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,39 @@ pub use self::not_found::not_found;

pub fn mods_config(cfg: &mut web::ServiceConfig) {
cfg.service(mods::mod_search);
cfg.service(mods::mods_get);
cfg.service(mod_creation::mod_create);

cfg.service(
web::scope("mod")
.service(mods::mod_get)
.service(mods::mod_delete)
.service(web::scope("{mod_id}").configure(versions_config)),
.service(
web::scope("{mod_id}")
.service(versions::version_list)
.service(version_creation::version_create),
Geometrically marked this conversation as resolved.
Show resolved Hide resolved
),
);
}

pub fn versions_config(cfg: &mut web::ServiceConfig) {
cfg.service(versions::version_list)
.service(version_creation::version_create)
.service(
web::scope("version")
.service(versions::version_get)
.service(versions::version_delete)
.service(version_creation::upload_file_to_version),
);
cfg.service(versions::versions_get);
cfg.service(
web::scope("version")
.service(versions::version_get)
.service(versions::version_delete)
.service(version_creation::upload_file_to_version),
);
}

pub fn users_config(cfg: &mut web::ServiceConfig) {
cfg.service(users::user_auth_get);

cfg.service(users::users_get);
cfg.service(
web::scope("user")
.service(users::user_get)
.service(users::mods_list)
.service(users::user_delete),
);
}
Expand All @@ -53,6 +59,8 @@ pub fn users_config(cfg: &mut web::ServiceConfig) {
pub enum ApiError {
#[error("Internal server error")]
DatabaseError(#[from] crate::database::models::DatabaseError),
#[error("Deserialization error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("Authentication Error")]
AuthenticationError,
}
Expand All @@ -62,6 +70,7 @@ impl actix_web::ResponseError for ApiError {
match self {
ApiError::DatabaseError(..) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
ApiError::AuthenticationError => actix_web::http::StatusCode::UNAUTHORIZED,
ApiError::JsonError(..) => actix_web::http::StatusCode::BAD_REQUEST,
}
}

Expand All @@ -71,6 +80,7 @@ impl actix_web::ResponseError for ApiError {
error: match self {
ApiError::DatabaseError(..) => "database_error",
ApiError::AuthenticationError => "unauthorized",
ApiError::JsonError(..) => "json_error",
},
description: &self.to_string(),
},
Expand Down
43 changes: 40 additions & 3 deletions src/routes/mods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::models;
use crate::models::mods::SearchRequest;
use crate::search::{search_for_mod, SearchError};
use actix_web::{delete, get, web, HttpRequest, HttpResponse};
use serde::{Deserialize, Serialize};
use sqlx::PgPool;

#[get("mod")]
Expand All @@ -15,13 +16,49 @@ pub async fn mod_search(
Ok(HttpResponse::Ok().json(results))
}

#[derive(Serialize, Deserialize)]
pub struct ModIds {
pub ids: String,
}

#[get("mods")]
pub async fn mods_get(
web::Query(ids): web::Query<ModIds>,
pool: web::Data<PgPool>,
) -> Result<HttpResponse, ApiError> {
let mut mods = vec![];

for id in serde_json::from_str::<Vec<models::ids::ModId>>(&*ids.ids)? {
let mod_data = get_mod_from_id(id, &*pool).await?;

if let Some(data) = mod_data {
mods.push(data)
}
}
Geometrically marked this conversation as resolved.
Show resolved Hide resolved

Ok(HttpResponse::Ok().json(mods))
}

#[get("{id}")]
pub async fn mod_get(
info: web::Path<(models::ids::ModId,)>,
pool: web::Data<PgPool>,
) -> Result<HttpResponse, ApiError> {
let id = info.0;
let mod_data = database::models::Mod::get_full(id.into(), &**pool)
let mod_data = get_mod_from_id(id, &*pool).await?;

if let Some(data) = mod_data {
Ok(HttpResponse::Ok().json(data))
} else {
Ok(HttpResponse::NotFound().body(""))
}
}

async fn get_mod_from_id(
id: models::ids::ModId,
pool: &PgPool,
) -> Result<Option<models::mods::Mod>, ApiError> {
let mod_data = database::models::Mod::get_full(id.into(), &*pool)
.await
.map_err(|e| ApiError::DatabaseError(e.into()))?;

Expand All @@ -43,9 +80,9 @@ pub async fn mod_get(
source_url: m.source_url,
wiki_url: m.wiki_url,
};
Ok(HttpResponse::Ok().json(response))
Ok(Some(response))
} else {
Ok(HttpResponse::NotFound().body(""))
Ok(None)
}
}

Expand Down
Loading