update api to comply with REST

add delete build endpoint
parse some info to new build view
This commit is contained in:
2024-02-17 19:50:15 +01:00
committed by Lukas-Heiligenbrunner
parent 05448a6217
commit a4f1179c94
16 changed files with 309 additions and 211 deletions

View File

@ -1,19 +1,7 @@
use crate::api::list::build_output;
use crate::api::list::okapi_add_operation_for_get_build_;
use crate::api::list::okapi_add_operation_for_stats_;
use crate::api::list::search;
use crate::api::list::{get_build, okapi_add_operation_for_list_builds_};
use crate::api::list::{list_builds, okapi_add_operation_for_search_};
use crate::api::list::{okapi_add_operation_for_build_output_, stats};
use crate::api::package::okapi_add_operation_for_package_add_endpoint_;
use crate::api::package::okapi_add_operation_for_package_del_;
use crate::api::package::okapi_add_operation_for_package_list_;
use crate::api::package::okapi_add_operation_for_package_update_endpoint_;
use crate::api::package::package_add_endpoint;
use crate::api::package::{get_package, package_del, package_list};
use crate::api::package::{okapi_add_operation_for_get_package_, package_update_endpoint};
use crate::api::remove::okapi_add_operation_for_version_del_;
use crate::api::remove::version_del;
use crate::api::build::*;
use crate::api::list::*;
use crate::api::package::*;
use crate::api::version::*;
use rocket::Route;
use rocket_okapi::openapi_get_routes;
@ -25,6 +13,7 @@ pub fn build_api() -> Vec<Route> {
package_del,
version_del,
build_output,
delete_build,
list_builds,
stats,
get_build,

169
backend/src/api/build.rs Normal file
View File

@ -0,0 +1,169 @@
use crate::db::migration::{JoinType, Order};
use crate::db::prelude::Builds;
use crate::db::{builds, packages, versions};
use rocket::response::status::NotFound;
use rocket::serde::json::Json;
use rocket::serde::{Deserialize, Serialize};
use rocket::{delete, get, State};
use rocket_okapi::okapi::schemars;
use rocket_okapi::{openapi, JsonSchema};
use sea_orm::ColumnTrait;
use sea_orm::QueryFilter;
use sea_orm::{
DatabaseConnection, EntityTrait, FromQueryResult, ModelTrait, QueryOrder, QuerySelect,
RelationTrait,
};
#[derive(FromQueryResult, Deserialize, JsonSchema, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct ListPackageModel {
id: i32,
name: String,
status: i32,
outofdate: bool,
latest_version: Option<String>,
latest_version_id: Option<i32>,
latest_aur_version: String,
}
#[openapi(tag = "build")]
#[get("/build/<buildid>/output?<startline>")]
pub async fn build_output(
db: &State<DatabaseConnection>,
buildid: i32,
startline: Option<i32>,
) -> Result<String, NotFound<String>> {
let db = db as &DatabaseConnection;
let build = Builds::find_by_id(buildid)
.one(db)
.await
.map_err(|e| NotFound(e.to_string()))?
.ok_or(NotFound("couldn't find id".to_string()))?;
return match build.ouput {
None => Err(NotFound("No Output".to_string())),
Some(v) => match startline {
None => Ok(v),
Some(startline) => {
let output: Vec<String> = v.split("\n").map(|x| x.to_string()).collect();
let len = output.len();
let len_missing = len as i32 - startline;
let output = output
.iter()
.rev()
.take(if len_missing > 0 {
len_missing as usize
} else {
0
})
.rev()
.map(|x1| x1.clone())
.collect::<Vec<_>>();
let output = output.join("\n");
Ok(output)
}
},
};
}
#[derive(FromQueryResult, Deserialize, JsonSchema, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct ListBuildsModel {
id: i32,
pkg_id: i32,
pkg_name: String,
version: String,
status: i32,
start_time: Option<u32>,
end_time: Option<u32>,
}
#[openapi(tag = "build")]
#[get("/builds?<pkgid>&<limit>")]
pub async fn list_builds(
db: &State<DatabaseConnection>,
pkgid: Option<i32>,
limit: Option<u64>,
) -> Result<Json<Vec<ListBuildsModel>>, NotFound<String>> {
let db = db as &DatabaseConnection;
let basequery = Builds::find()
.join_rev(JoinType::InnerJoin, packages::Relation::Builds.def())
.join_rev(JoinType::InnerJoin, versions::Relation::Builds.def())
.select_only()
.column_as(builds::Column::Id, "id")
.column(builds::Column::Status)
.column_as(packages::Column::Name, "pkg_name")
.column_as(packages::Column::Id, "pkg_id")
.column(versions::Column::Version)
.column(builds::Column::EndTime)
.column(builds::Column::StartTime)
.order_by(builds::Column::StartTime, Order::Desc)
.limit(limit);
let build = match pkgid {
None => basequery.into_model::<ListBuildsModel>().all(db),
Some(pkgid) => basequery
.filter(builds::Column::PkgId.eq(pkgid))
.into_model::<ListBuildsModel>()
.all(db),
}
.await
.map_err(|e| NotFound(e.to_string()))?;
Ok(Json(build))
}
#[openapi(tag = "build")]
#[get("/build/<buildid>")]
pub async fn get_build(
db: &State<DatabaseConnection>,
buildid: i32,
) -> Result<Json<ListBuildsModel>, NotFound<String>> {
let db = db as &DatabaseConnection;
let result = Builds::find()
.join_rev(JoinType::InnerJoin, packages::Relation::Builds.def())
.join_rev(JoinType::InnerJoin, versions::Relation::Builds.def())
.filter(builds::Column::Id.eq(buildid))
.select_only()
.column_as(builds::Column::Id, "id")
.column(builds::Column::Status)
.column_as(packages::Column::Name, "pkg_name")
.column_as(packages::Column::Id, "pkg_id")
.column(versions::Column::Version)
.column(builds::Column::EndTime)
.column(builds::Column::StartTime)
.into_model::<ListBuildsModel>()
.one(db)
.await
.map_err(|e| NotFound(e.to_string()))?
.ok_or(NotFound("no item with id found".to_string()))?;
Ok(Json(result))
}
#[openapi(tag = "build")]
#[delete("/build/<buildid>")]
pub async fn delete_build(
db: &State<DatabaseConnection>,
buildid: i32,
) -> Result<(), NotFound<String>> {
let db = db as &DatabaseConnection;
let build = Builds::find_by_id(buildid)
.one(db)
.await
.map_err(|e| NotFound(e.to_string()))?
.ok_or(NotFound("Id not found".to_string()))?;
build
.delete(db)
.await
.map_err(|e| NotFound(e.to_string()))?;
Ok(())
}

View File

@ -1,7 +1,6 @@
use crate::aur::aur::query_aur;
use crate::db::migration::JoinType;
use crate::db::prelude::{Builds, Packages};
use crate::db::{builds, packages, versions};
use crate::db::{builds};
use crate::utils::dir_size::dir_size;
use rocket::response::status::NotFound;
use rocket::serde::json::Json;
@ -10,8 +9,8 @@ use rocket::{get, State};
use rocket_okapi::okapi::schemars;
use rocket_okapi::{openapi, JsonSchema};
use sea_orm::{ColumnTrait, QueryFilter};
use sea_orm::{DatabaseConnection, EntityTrait, FromQueryResult, QuerySelect, RelationTrait};
use sea_orm::{Order, PaginatorTrait, QueryOrder};
use sea_orm::{DatabaseConnection, EntityTrait, FromQueryResult};
use sea_orm::{PaginatorTrait};
#[derive(Serialize, JsonSchema)]
#[serde(crate = "rocket::serde")]
@ -20,7 +19,7 @@ pub struct ApiPackage {
version: String,
}
#[openapi(tag = "test")]
#[openapi(tag = "aur")]
#[get("/search?<query>")]
pub async fn search(query: &str) -> Result<Json<Vec<ApiPackage>>, String> {
return match query_aur(query).await {
@ -38,135 +37,6 @@ pub async fn search(query: &str) -> Result<Json<Vec<ApiPackage>>, String> {
};
}
#[derive(FromQueryResult, Deserialize, JsonSchema, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct ListPackageModel {
id: i32,
name: String,
status: i32,
outofdate: bool,
latest_version: Option<String>,
latest_version_id: Option<i32>,
latest_aur_version: String,
}
#[openapi(tag = "test")]
#[get("/builds/output?<buildid>&<startline>")]
pub async fn build_output(
db: &State<DatabaseConnection>,
buildid: i32,
startline: Option<i32>,
) -> Result<String, NotFound<String>> {
let db = db as &DatabaseConnection;
let build = Builds::find_by_id(buildid)
.one(db)
.await
.map_err(|e| NotFound(e.to_string()))?
.ok_or(NotFound("couldn't find id".to_string()))?;
return match build.ouput {
None => Err(NotFound("No Output".to_string())),
Some(v) => match startline {
None => Ok(v),
Some(startline) => {
let output: Vec<String> = v.split("\n").map(|x| x.to_string()).collect();
let len = output.len();
let len_missing = len as i32 - startline;
let output = output
.iter()
.rev()
.take(if len_missing > 0 {
len_missing as usize
} else {
0
})
.rev()
.map(|x1| x1.clone())
.collect::<Vec<_>>();
let output = output.join("\n");
Ok(output)
}
},
};
}
#[derive(FromQueryResult, Deserialize, JsonSchema, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct ListBuildsModel {
id: i32,
pkg_name: String,
version: String,
status: i32,
start_time: Option<u32>,
end_time: Option<u32>,
}
#[openapi(tag = "test")]
#[get("/builds?<pkgid>&<limit>")]
pub async fn list_builds(
db: &State<DatabaseConnection>,
pkgid: Option<i32>,
limit: Option<u64>,
) -> Result<Json<Vec<ListBuildsModel>>, NotFound<String>> {
let db = db as &DatabaseConnection;
let basequery = Builds::find()
.join_rev(JoinType::InnerJoin, packages::Relation::Builds.def())
.join_rev(JoinType::InnerJoin, versions::Relation::Builds.def())
.select_only()
.column_as(builds::Column::Id, "id")
.column(builds::Column::Status)
.column_as(packages::Column::Name, "pkg_name")
.column(versions::Column::Version)
.column(builds::Column::EndTime)
.column(builds::Column::StartTime)
.order_by(builds::Column::StartTime, Order::Desc)
.limit(limit);
let build = match pkgid {
None => basequery.into_model::<ListBuildsModel>().all(db),
Some(pkgid) => basequery
.filter(builds::Column::PkgId.eq(pkgid))
.into_model::<ListBuildsModel>()
.all(db),
}
.await
.map_err(|e| NotFound(e.to_string()))?;
Ok(Json(build))
}
#[openapi(tag = "test")]
#[get("/builds/<buildid>")]
pub async fn get_build(
db: &State<DatabaseConnection>,
buildid: i32,
) -> Result<Json<ListBuildsModel>, NotFound<String>> {
let db = db as &DatabaseConnection;
let result = Builds::find()
.join_rev(JoinType::InnerJoin, packages::Relation::Builds.def())
.join_rev(JoinType::InnerJoin, versions::Relation::Builds.def())
.filter(builds::Column::Id.eq(buildid))
.select_only()
.column_as(builds::Column::Id, "id")
.column(builds::Column::Status)
.column_as(packages::Column::Name, "pkg_name")
.column(versions::Column::Version)
.column(builds::Column::EndTime)
.column(builds::Column::StartTime)
.into_model::<ListBuildsModel>()
.one(db)
.await
.map_err(|e| NotFound(e.to_string()))?
.ok_or(NotFound("no item with id found".to_string()))?;
Ok(Json(result))
}
#[derive(FromQueryResult, Deserialize, JsonSchema, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct ListStats {
@ -179,15 +49,15 @@ pub struct ListStats {
total_packages: u32,
}
#[openapi(tag = "test")]
#[openapi(tag = "stats")]
#[get("/stats")]
pub async fn stats(db: &State<DatabaseConnection>) -> Result<Json<ListStats>, NotFound<String>> {
let db = db as &DatabaseConnection;
return match get_stats(db).await {
Ok(v) => Ok(Json(v)),
Err(e) => Err(NotFound(e.to_string())),
};
get_stats(db)
.await
.map_err(|e| NotFound(e.to_string()))
.map(Json)
}
async fn get_stats(db: &DatabaseConnection) -> anyhow::Result<ListStats> {

View File

@ -1,6 +1,7 @@
pub mod backend;
mod build;
#[cfg(feature = "static")]
pub mod embed;
mod list;
mod package;
mod remove;
mod version;

View File

@ -1,4 +1,4 @@
use crate::api::list::ListPackageModel;
use crate::api::build::ListPackageModel;
use crate::builder::types::Action;
use crate::db::migration::{JoinType, Order};
use crate::db::prelude::Packages;
@ -9,7 +9,7 @@ use crate::package::update::package_update;
use rocket::response::status::{BadRequest, NotFound};
use rocket::serde::json::Json;
use rocket::serde::Deserialize;
use rocket::{get, post, State};
use rocket::{delete, get, post, State};
use rocket_okapi::okapi::schemars;
use rocket_okapi::{openapi, JsonSchema};
use sea_orm::DatabaseConnection;
@ -23,7 +23,7 @@ pub struct AddBody {
}
#[openapi(tag = "Packages")]
#[post("/packages/add", data = "<input>")]
#[post("/package", data = "<input>")]
pub async fn package_add_endpoint(
db: &State<DatabaseConnection>,
input: Json<AddBody>,
@ -41,20 +41,21 @@ pub struct UpdateBody {
}
#[openapi(tag = "Packages")]
#[post("/packages/<id>/update", data = "<input>")]
#[post("/package/<id>/update", data = "<input>")]
pub async fn package_update_endpoint(
db: &State<DatabaseConnection>,
id: i32,
input: Json<UpdateBody>,
tx: &State<Sender<Action>>,
) -> Result<(), BadRequest<String>> {
) -> Result<Json<i32>, BadRequest<String>> {
package_update(db, id, input.force, tx)
.await
.map(|e| Json(e))
.map_err(|e| BadRequest(Some(e.to_string())))
}
#[openapi(tag = "Packages")]
#[post("/package/delete/<id>")]
#[delete("/package/<id>")]
pub async fn package_del(db: &State<DatabaseConnection>, id: i32) -> Result<(), String> {
let db = db as &DatabaseConnection;

View File

@ -1,10 +1,10 @@
use crate::repo::repo::remove_version;
use rocket::{post, State};
use rocket::{delete, State};
use rocket_okapi::openapi;
use sea_orm::DatabaseConnection;
#[openapi(tag = "test")]
#[post("/versions/delete/<id>")]
#[openapi(tag = "version")]
#[delete("/version/<id>/delete")]
pub async fn version_del(db: &State<DatabaseConnection>, id: i32) -> Result<(), String> {
let db = db as &DatabaseConnection;

View File

@ -19,12 +19,13 @@ pub async fn init(db: DatabaseConnection, tx: Sender<Action>) {
if let Ok(_result) = tx.subscribe().recv().await {
match _result {
// add a package to parallel build
Action::Build(name, version, url, version_model) => {
Action::Build(name, version, url, version_model, build_model) => {
let _ = queue_package(
name,
version,
url,
version_model,
build_model,
db.clone(),
semaphore.clone(),
)
@ -40,25 +41,10 @@ async fn queue_package(
version: String,
url: String,
version_model: versions::ActiveModel,
mut build_model: builds::ActiveModel,
db: DatabaseConnection,
semaphore: Arc<Semaphore>,
) -> anyhow::Result<()> {
// set build status to pending
let build = builds::ActiveModel {
pkg_id: version_model.package_id.clone(),
version_id: version_model.id.clone(),
ouput: Set(None),
status: Set(Some(3)),
start_time: Set(Some(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as u32,
)),
..Default::default()
};
let mut new_build = build.save(&db).await.unwrap();
let permits = Arc::clone(&semaphore);
// spawn new thread for each pkg build
@ -67,10 +53,10 @@ async fn queue_package(
let _permit = permits.acquire().await.unwrap();
// set build status to building
new_build.status = Set(Some(0));
new_build = new_build.save(&db).await.unwrap();
build_model.status = Set(Some(0));
build_model = build_model.save(&db).await.unwrap();
let _ = build_package(new_build, db, version_model, version, name, url).await;
let _ = build_package(build_model, db, version_model, version, name, url).await;
});
Ok(())
}

View File

@ -1,6 +1,12 @@
use crate::db::versions;
use crate::db::{builds, versions};
#[derive(Clone)]
pub enum Action {
Build(String, String, String, versions::ActiveModel),
Build(
String,
String,
String,
versions::ActiveModel,
builds::ActiveModel,
),
}

View File

@ -1,11 +1,12 @@
use crate::aur::aur::get_info_by_name;
use crate::builder::types::Action;
use crate::db::prelude::Packages;
use crate::db::{packages, versions};
use crate::db::{builds, packages, versions};
use anyhow::anyhow;
use sea_orm::ColumnTrait;
use sea_orm::QueryFilter;
use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set, TransactionTrait};
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::sync::broadcast::Sender;
pub async fn package_add(
@ -49,11 +50,28 @@ pub async fn package_add(
new_package.latest_version_id = Set(Some(new_version.id.clone().unwrap()));
new_package.save(&txn).await?;
// set build status to pending
let build = builds::ActiveModel {
pkg_id: new_version.package_id.clone(),
version_id: new_version.id.clone(),
ouput: Set(None),
status: Set(Some(3)),
start_time: Set(Some(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as u32,
)),
..Default::default()
};
let new_build = build.save(&txn).await?;
let _ = tx.send(Action::Build(
pkg.name,
pkg.version,
pkg.url_path.unwrap(),
new_version,
new_build,
));
txn.commit().await?;

View File

@ -1,11 +1,12 @@
use crate::aur::aur::get_info_by_name;
use crate::builder::types::Action;
use crate::db::prelude::{Packages, Versions};
use crate::db::{packages, versions};
use crate::db::{builds, packages, versions};
use anyhow::anyhow;
use sea_orm::ColumnTrait;
use sea_orm::QueryFilter;
use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set, TransactionTrait};
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::sync::broadcast::Sender;
pub async fn package_update(
@ -13,7 +14,7 @@ pub async fn package_update(
pkg_id: i32,
force: bool,
tx: &Sender<Action>,
) -> anyhow::Result<()> {
) -> anyhow::Result<i32> {
let txn = db.begin().await?;
let mut pkg_model: packages::ActiveModel = Packages::find_by_id(pkg_id)
@ -54,16 +55,34 @@ pub async fn package_update(
pkg_model.status = Set(3);
pkg_model.latest_version_id = Set(Some(version_model.id.clone().unwrap()));
pkg_model.save(&txn).await.expect("todo error message");
pkg_model.save(&txn).await?;
// set build status to pending
let build = builds::ActiveModel {
pkg_id: version_model.package_id.clone(),
version_id: version_model.id.clone(),
ouput: Set(None),
status: Set(Some(3)),
start_time: Set(Some(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as u32,
)),
..Default::default()
};
let new_build = build.save(&txn).await?;
let build_id = new_build.id.clone().unwrap();
let _ = tx.send(Action::Build(
pkg.name,
pkg.version,
pkg.url_path.unwrap(),
version_model,
new_build,
));
txn.commit().await?;
Ok(())
Ok(build_id)
}