feat: some experimentation with api filtering

concurrent-repos
Jef Roosens 2024-05-21 08:49:46 +02:00
parent e684cfb84e
commit e1642d939b
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
6 changed files with 68 additions and 16 deletions

View File

@ -1,5 +1,7 @@
mod pagination; mod pagination;
use sea_orm::{sea_query::IntoCondition, *};
use axum::extract::{Path, Query, State}; use axum::extract::{Path, Query, State};
use axum::routing::get; use axum::routing::get;
use axum::Json; use axum::Json;
@ -7,7 +9,7 @@ use axum::Router;
use pagination::PaginatedResponse; use pagination::PaginatedResponse;
use crate::db; use crate::db::{self, *};
pub fn router() -> Router<crate::Global> { pub fn router() -> Router<crate::Global> {
Router::new() Router::new()
@ -20,16 +22,27 @@ pub fn router() -> Router<crate::Global> {
async fn get_repos( async fn get_repos(
State(global): State<crate::Global>, State(global): State<crate::Global>,
Query(pagination): Query<pagination::Query>, Query(pagination): Query<pagination::Query>,
Query(filter): Query<db::query::repo::Filter>,
) -> crate::Result<Json<PaginatedResponse<db::repo::Model>>> { ) -> crate::Result<Json<PaginatedResponse<db::repo::Model>>> {
let (total_pages, repos) = global let page = pagination.page.unwrap_or(1) - 1;
.db let per_page = pagination.per_page.unwrap_or(25);
.repo
.page( let paginator = Repo::find()
pagination.per_page.unwrap_or(25), .filter(filter)
pagination.page.unwrap_or(1) - 1, .order_by_asc(package::Column::Id)
) .paginate(&global.db, pagination.per_page.unwrap_or(25));
let items = paginator
.fetch_page(pagination.page.unwrap_or(1) - 1)
.await?; .await?;
Ok(Json(pagination.res(total_pages, repos))) let total_pages = paginator.num_pages().await?;
Ok(Json(PaginatedResponse {
page,
per_page,
total_pages,
count: items.len(),
items,
}))
} }
async fn get_single_repo( async fn get_single_repo(
@ -49,6 +62,7 @@ async fn get_single_repo(
async fn get_packages( async fn get_packages(
State(global): State<crate::Global>, State(global): State<crate::Global>,
Query(pagination): Query<pagination::Query>, Query(pagination): Query<pagination::Query>,
Query(filter): Query<db::query::package::Filter>,
) -> crate::Result<Json<PaginatedResponse<db::package::Model>>> { ) -> crate::Result<Json<PaginatedResponse<db::package::Model>>> {
let (total_pages, pkgs) = global let (total_pages, pkgs) = global
.db .db
@ -56,6 +70,7 @@ async fn get_packages(
.page( .page(
pagination.per_page.unwrap_or(25), pagination.per_page.unwrap_or(25),
pagination.page.unwrap_or(1) - 1, pagination.page.unwrap_or(1) - 1,
filter,
) )
.await?; .await?;

View File

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub const DEFAULT_PAGE: u64 = 0; pub const DEFAULT_PAGE: u64 = 1;
pub const DEFAULT_PER_PAGE: u64 = 25; pub const DEFAULT_PER_PAGE: u64 = 25;
#[derive(Deserialize)] #[derive(Deserialize)]

View File

@ -1,7 +1,7 @@
mod conn; mod conn;
pub mod entities; pub mod entities;
mod migrator; mod migrator;
mod query; pub mod query;
use sea_orm::{ConnectOptions, Database, DatabaseConnection, DeriveActiveEnum, EnumIter}; use sea_orm::{ConnectOptions, Database, DatabaseConnection, DeriveActiveEnum, EnumIter};
use sea_orm_migration::MigratorTrait; use sea_orm_migration::MigratorTrait;
@ -14,6 +14,7 @@ type Result<T> = std::result::Result<T, sea_orm::DbErr>;
#[derive(EnumIter, DeriveActiveEnum, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] #[derive(EnumIter, DeriveActiveEnum, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
#[sea_orm(rs_type = "i32", db_type = "Integer")] #[sea_orm(rs_type = "i32", db_type = "Integer")]
#[serde(rename_all = "lowercase")]
pub enum PackageRelatedEnum { pub enum PackageRelatedEnum {
#[sea_orm(num_value = 0)] #[sea_orm(num_value = 0)]
Conflicts, Conflicts,

View File

@ -1,5 +1,5 @@
mod package; pub mod package;
mod repo; pub mod repo;
pub use package::PackageQuery; pub use package::PackageQuery;
pub use repo::RepoQuery; pub use repo::RepoQuery;

View File

@ -1,4 +1,5 @@
use sea_orm::*; use sea_orm::{sea_query::IntoCondition, *};
use serde::Deserialize;
use crate::db::*; use crate::db::*;
@ -7,6 +8,25 @@ pub struct PackageQuery {
conn: DatabaseConnection, conn: DatabaseConnection,
} }
#[derive(Deserialize)]
pub struct Filter {
repo: Option<i32>,
arch: Option<String>,
name: Option<String>,
}
impl IntoCondition for Filter {
fn into_condition(self) -> Condition {
Condition::all()
.add_option(self.repo.map(|repo| package::Column::RepoId.eq(repo)))
.add_option(self.arch.map(|arch| package::Column::Arch.eq(arch)))
.add_option(
self.name
.map(|name| package::Column::Name.like(format!("%{}%", name))),
)
}
}
impl PackageQuery { impl PackageQuery {
pub fn new(conn: DatabaseConnection) -> Self { pub fn new(conn: DatabaseConnection) -> Self {
Self { conn } Self { conn }
@ -16,8 +36,10 @@ impl PackageQuery {
&self, &self,
per_page: u64, per_page: u64,
page: u64, page: u64,
filter: Filter,
) -> super::Result<(u64, Vec<package::Model>)> { ) -> super::Result<(u64, Vec<package::Model>)> {
let paginator = Package::find() let paginator = Package::find()
.filter(filter)
.order_by_asc(package::Column::Id) .order_by_asc(package::Column::Id)
.paginate(&self.conn, per_page); .paginate(&self.conn, per_page);
let packages = paginator.fetch_page(page).await?; let packages = paginator.fetch_page(page).await?;
@ -132,7 +154,10 @@ impl PackageQuery {
package_id: Set(pkg_entry.id), package_id: Set(pkg_entry.id),
r#type: Set(t), r#type: Set(t),
name: Set(s.to_string()), name: Set(s.to_string()),
})); }))
.on_empty_do_nothing()
.exec(&self.conn)
.await?;
PackageFile::insert_many(pkg.files.iter().map(|s| package_file::ActiveModel { PackageFile::insert_many(pkg.files.iter().map(|s| package_file::ActiveModel {
package_id: Set(pkg_entry.id), package_id: Set(pkg_entry.id),

View File

@ -1,4 +1,4 @@
use sea_orm::*; use sea_orm::{sea_query::IntoCondition, *};
use crate::db::*; use crate::db::*;
@ -7,6 +7,17 @@ pub struct RepoQuery {
conn: DatabaseConnection, conn: DatabaseConnection,
} }
#[derive(Deserialize)]
pub struct Filter {
name: Option<String>,
}
impl IntoCondition for Filter {
fn into_condition(self) -> Condition {
Condition::all().add_option(self.name.map(|name| package::Column::Name.like(name)))
}
}
impl RepoQuery { impl RepoQuery {
pub fn new(conn: DatabaseConnection) -> Self { pub fn new(conn: DatabaseConnection) -> Self {
Self { conn } Self { conn }