feat(api): implemented /project/info
This commit is contained in:
parent
76f371b6ee
commit
8fd807e450
|
@ -4,21 +4,23 @@ Returns the list of public projects the user is part of.
|
|||
## Urlencoded Parameters
|
||||
You have to set one parameter.
|
||||
Setting none or two parameters will result in a _400 Bad Request_ Response.
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-----------------------------------------------------------------------|
|
||||
| name | The projectname of the user on which the operation will be performed. |
|
||||
| id | The projectid of the user on which the operation will be performed. |
|
||||
|-----------|--------------------|
|
||||
| name | The projects name. |
|
||||
| id | The projects id. |
|
||||
|
||||
## Responses
|
||||
### 200 - Success
|
||||
__Content - JSON:__
|
||||
|
||||
| Field | Description |
|
||||
|-------------|---------------------------------------------------------------------------|
|
||||
| id | The projects unique id. |
|
||||
| name | The projects unique name. |
|
||||
| description | The projects description. |
|
||||
| created | The datetime when the project was created. Represented as UNIX timestamp. |
|
||||
| members | A list of (username, userid) pairs. |
|
||||
| members | A list of (username, userid, flags) pairs. |
|
||||
### 400 - Error: Bad Request
|
||||
The request was malformed.
|
||||
### 403 - Error: Forbidden
|
||||
|
|
|
@ -30,8 +30,8 @@ pub struct Account {
|
|||
impl From<i64> for ID {
|
||||
fn from(value: i64) -> Self {
|
||||
Self {
|
||||
id: (value as u64 & 0x0000FFFFFFFFFFFF),
|
||||
flags: (value as u64 & 0xFFFF000000000000) as u16,
|
||||
id: (value as u64 & 0x0000_FFFF_FFFF_FFFF),
|
||||
flags: ((value as u64 & 0xFFFF_0000_0000_0000) >> 48) as u16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ impl ID {
|
|||
}
|
||||
|
||||
pub fn combined(&self) -> i64 {
|
||||
(self.id | ((self.flags as u64) << 46)) as i64
|
||||
(self.id | ((self.flags as u64) << 48)) as i64
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ pub async fn start(port: u16, pool: PgPool) -> Result<()> {
|
|||
.service(account::calls::tokens_delete)
|
||||
.service(account::calls::tokens_get)
|
||||
.service(project::calls::create)
|
||||
.service(project::calls::info)
|
||||
.app_data(web::Data::new(ApiState { pool: pool.clone() }))
|
||||
})
|
||||
.bind(("0.0.0.0", port))?
|
||||
|
|
|
@ -14,7 +14,37 @@ async fn create(data: web::Data<ApiState>, auth: BearerAuth, body: web::Json<dat
|
|||
data::CreateResponse::Blocked => HttpResponse::Forbidden().finish(),
|
||||
},
|
||||
Err(e) => {
|
||||
error!("While handling register request: {e}");
|
||||
error!("While handling create request: {e}");
|
||||
HttpResponse::InternalServerError().finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/project/info")]
|
||||
async fn info(data: web::Data<ApiState>, query: web::Query<data::InfoQuery>) -> impl Responder {
|
||||
|
||||
let request = if query.id.is_none() || query.name.is_none() {
|
||||
if let Some(id) = query.id {
|
||||
data::InfoRequest::Id(id)
|
||||
} else if let Some(name) = query.into_inner().name {
|
||||
data::InfoRequest::Name(name)
|
||||
} else {
|
||||
// No parameters were supplied
|
||||
return HttpResponse::BadRequest().finish();
|
||||
}
|
||||
} else {
|
||||
// Too many parameters were supplied
|
||||
return HttpResponse::BadRequest().finish();
|
||||
};
|
||||
|
||||
match handlers::info(&data.pool, request).await {
|
||||
Ok(resp) => match resp {
|
||||
data::InfoResponse::Success(b) => HttpResponse::Ok().json(web::Json(b)),
|
||||
data::InfoResponse::Blocked => HttpResponse::Forbidden().finish(),
|
||||
data::InfoResponse::NotFound => HttpResponse::NotFound().finish(),
|
||||
},
|
||||
Err(e) => {
|
||||
error!("While handling info request: {e}");
|
||||
HttpResponse::InternalServerError().finish()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,3 +18,31 @@ pub enum CreateResponse {
|
|||
Blocked,
|
||||
Conflict,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct InfoQuery {
|
||||
pub name: Option<String>,
|
||||
pub id: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InfoRequest {
|
||||
Name(String),
|
||||
Id(i64),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct InfoSuccess {
|
||||
pub id: i64,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub created: i64,
|
||||
pub members: Vec<(String, i64, u16)>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InfoResponse {
|
||||
Success(InfoSuccess),
|
||||
Blocked,
|
||||
NotFound,
|
||||
}
|
|
@ -4,8 +4,9 @@ use crate::{
|
|||
security::{is_sql_injection, AlphaExt},
|
||||
tokens::AuthToken,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use anyhow::{Error, Result};
|
||||
use sqlx::PgPool;
|
||||
use crate::accounts::Account;
|
||||
|
||||
pub async fn create(
|
||||
pool: &PgPool,
|
||||
|
@ -35,3 +36,38 @@ pub async fn create(
|
|||
id: project.id,
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn info(pool: &PgPool, request: data::InfoRequest) -> Result<data::InfoResponse> {
|
||||
let project = match match request {
|
||||
data::InfoRequest::Name(name) => {
|
||||
if is_sql_injection(&name) {
|
||||
return Ok(data::InfoResponse::Blocked);
|
||||
}
|
||||
Project::from_name(pool, &name).await?
|
||||
}
|
||||
data::InfoRequest::Id(id) => Project::from_id(pool, id).await?
|
||||
}
|
||||
{
|
||||
Some(p) => p,
|
||||
None => return Ok(data::InfoResponse::NotFound)
|
||||
};
|
||||
|
||||
let mut members = Vec::new();
|
||||
members.reserve(project.members.len());
|
||||
for &id in project.members.iter() {
|
||||
members.push((match Account::from_id(pool, id.id()).await? {
|
||||
Some(m) => m.username,
|
||||
None => return Err(Error::msg(format!("The account with id {} was not found", id.id()))),
|
||||
},
|
||||
id.id(),
|
||||
id.flags()));
|
||||
}
|
||||
|
||||
Ok(data::InfoResponse::Success(data::InfoSuccess {
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
description: project.description,
|
||||
created: project.created.timestamp(),
|
||||
members,
|
||||
}))
|
||||
}
|
|
@ -8,7 +8,7 @@ pub struct Project {
|
|||
pub name: String,
|
||||
pub description: String,
|
||||
pub created: chrono::NaiveDateTime,
|
||||
pub members: Vec<i64>,
|
||||
pub members: Vec<ID>,
|
||||
}
|
||||
|
||||
pub enum ProjectMemberFlags {
|
||||
|
@ -49,11 +49,38 @@ impl Project {
|
|||
}
|
||||
|
||||
pub async fn from_name(pool: &PgPool, name: &String) -> Result<Option<Self>> {
|
||||
match sqlx::query_as!(Project, r#"SELECT * FROM Projects WHERE name = $1;"#, name)
|
||||
match sqlx::query!(r#"SELECT * FROM Projects WHERE name = $1;"#, name)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
{
|
||||
Ok(project) => Ok(Some(project)),
|
||||
Ok(row) => {
|
||||
Ok(Some(Self {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
description: row.description,
|
||||
created: row.created,
|
||||
members: row.members.iter().map(|&raw_id| raw_id.into()).collect::<Vec<ID>>(),
|
||||
}))
|
||||
},
|
||||
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||
Err(e) => Err(Error::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn from_id(pool: &PgPool, id: i64) -> Result<Option<Self>> {
|
||||
match sqlx::query!(r#"SELECT * FROM Projects WHERE id = $1;"#, id)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
{
|
||||
Ok(row) => {
|
||||
Ok(Some(Self {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
description: row.description,
|
||||
created: row.created,
|
||||
members: row.members.iter().map(|&raw_id| raw_id.into()).collect::<Vec<ID>>(),
|
||||
}))
|
||||
},
|
||||
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||
Err(e) => Err(Error::new(e)),
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue