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
|
## Urlencoded Parameters
|
||||||
You have to set one parameter.
|
You have to set one parameter.
|
||||||
Setting none or two parameters will result in a _400 Bad Request_ Response.
|
Setting none or two parameters will result in a _400 Bad Request_ Response.
|
||||||
|
|
||||||
| Parameter | Description |
|
| Parameter | Description |
|
||||||
|-----------|-----------------------------------------------------------------------|
|
|-----------|--------------------|
|
||||||
| name | The projectname of the user on which the operation will be performed. |
|
| name | The projects name. |
|
||||||
| id | The projectid of the user on which the operation will be performed. |
|
| id | The projects id. |
|
||||||
|
|
||||||
## Responses
|
## Responses
|
||||||
### 200 - Success
|
### 200 - Success
|
||||||
__Content - JSON:__
|
__Content - JSON:__
|
||||||
|
|
||||||
| Field | Description |
|
| Field | Description |
|
||||||
|-------------|---------------------------------------------------------------------------|
|
|-------------|---------------------------------------------------------------------------|
|
||||||
| id | The projects unique id. |
|
| id | The projects unique id. |
|
||||||
| name | The projects unique name. |
|
| name | The projects unique name. |
|
||||||
| description | The projects description. |
|
| description | The projects description. |
|
||||||
| created | The datetime when the project was created. Represented as UNIX timestamp. |
|
| 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
|
### 400 - Error: Bad Request
|
||||||
The request was malformed.
|
The request was malformed.
|
||||||
### 403 - Error: Forbidden
|
### 403 - Error: Forbidden
|
||||||
|
|
|
@ -30,8 +30,8 @@ pub struct Account {
|
||||||
impl From<i64> for ID {
|
impl From<i64> for ID {
|
||||||
fn from(value: i64) -> Self {
|
fn from(value: i64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: (value as u64 & 0x0000FFFFFFFFFFFF),
|
id: (value as u64 & 0x0000_FFFF_FFFF_FFFF),
|
||||||
flags: (value as u64 & 0xFFFF000000000000) as u16,
|
flags: ((value as u64 & 0xFFFF_0000_0000_0000) >> 48) as u16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ impl ID {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn combined(&self) -> i64 {
|
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_delete)
|
||||||
.service(account::calls::tokens_get)
|
.service(account::calls::tokens_get)
|
||||||
.service(project::calls::create)
|
.service(project::calls::create)
|
||||||
|
.service(project::calls::info)
|
||||||
.app_data(web::Data::new(ApiState { pool: pool.clone() }))
|
.app_data(web::Data::new(ApiState { pool: pool.clone() }))
|
||||||
})
|
})
|
||||||
.bind(("0.0.0.0", port))?
|
.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(),
|
data::CreateResponse::Blocked => HttpResponse::Forbidden().finish(),
|
||||||
},
|
},
|
||||||
Err(e) => {
|
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()
|
HttpResponse::InternalServerError().finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,3 +18,31 @@ pub enum CreateResponse {
|
||||||
Blocked,
|
Blocked,
|
||||||
Conflict,
|
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},
|
security::{is_sql_injection, AlphaExt},
|
||||||
tokens::AuthToken,
|
tokens::AuthToken,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::{Error, Result};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
use crate::accounts::Account;
|
||||||
|
|
||||||
pub async fn create(
|
pub async fn create(
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
|
@ -35,3 +36,38 @@ pub async fn create(
|
||||||
id: project.id,
|
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 name: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub created: chrono::NaiveDateTime,
|
pub created: chrono::NaiveDateTime,
|
||||||
pub members: Vec<i64>,
|
pub members: Vec<ID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ProjectMemberFlags {
|
pub enum ProjectMemberFlags {
|
||||||
|
@ -49,11 +49,38 @@ impl Project {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn from_name(pool: &PgPool, name: &String) -> Result<Option<Self>> {
|
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)
|
.fetch_one(pool)
|
||||||
.await
|
.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(sqlx::Error::RowNotFound) => Ok(None),
|
||||||
Err(e) => Err(Error::new(e)),
|
Err(e) => Err(Error::new(e)),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue