start of collections endpoints

This commit is contained in:
dal 2025-03-18 08:14:29 -06:00
parent e790547c2f
commit bd2cbf781c
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
13 changed files with 117 additions and 107 deletions

View File

@ -0,0 +1,32 @@
use anyhow::Result;
use diesel::{ExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;
use uuid::Uuid;
use crate::models::Collection;
use crate::pool::get_pg_pool;
use crate::schema::collections;
/// Fetches a single collection by ID that hasn't been deleted
///
/// # Arguments
/// * `id` - The UUID of the collection to fetch
///
/// # Returns
/// * `Result<Option<Collection>>` - The collection if found and not deleted
pub async fn fetch_collection(id: &Uuid) -> Result<Option<Collection>> {
let mut conn = get_pg_pool().get().await?;
let result = match collections::table
.filter(collections::id.eq(id))
.filter(collections::deleted_at.is_null())
.first::<Collection>(&mut conn)
.await
{
Ok(result) => Some(result),
Err(diesel::NotFound) => None,
Err(e) => return Err(e.into()),
};
Ok(result)
}

View File

@ -1,2 +1,3 @@
pub mod metric_files;
pub mod dashboard_files;
pub mod collections;

View File

@ -38,7 +38,7 @@ pub async fn create_collection_handler(
created_by: *user_id,
updated_by: *user_id,
deleted_at: None,
organization_id,
organization_id: *organization_id,
};
let insert_task_user_id = *user_id;
@ -97,7 +97,7 @@ pub async fn create_collection_handler(
// Update search index
let collection_id_for_search = collection_id;
let collection_name = collection.name.clone();
let organization_id_for_search = organization_id;
let organization_id_for_search = *organization_id;
let collection_search_handle = tokio::spawn(async move {
let mut conn = match get_pg_pool().get().await {
@ -143,8 +143,6 @@ pub async fn create_collection_handler(
collection,
assets: None,
permission: AssetPermissionRole::Owner,
individual_permissions: None,
team_permissions: None,
organization_permissions: false,
})
}

View File

@ -25,15 +25,6 @@ pub async fn delete_collection_handler(
ids: Vec<Uuid>,
) -> Result<DeleteCollectionResponse> {
// Filter out collections where the user only has viewer permission
let filtered_ids_to_delete: Vec<Uuid> = ids
.into_iter()
.filter(|id| match roles.get(id) {
Some(role) if *role != AssetPermissionRole::Viewer => true,
_ => false,
})
.collect();
// Get database connection
let mut conn = match get_pg_pool().get().await {
Ok(conn) => conn,
@ -44,7 +35,7 @@ pub async fn delete_collection_handler(
// Soft delete the collections
match update(collections::table)
.filter(collections::id.eq_any(&filtered_ids_to_delete))
.filter(collections::id.eq_any(&ids))
.set(collections::deleted_at.eq(Some(Utc::now())))
.execute(&mut conn)
.await
@ -57,6 +48,6 @@ pub async fn delete_collection_handler(
// Return the IDs of the deleted collections
Ok(DeleteCollectionResponse {
ids: filtered_ids_to_delete,
ids,
})
}

View File

@ -1,4 +1,5 @@
use anyhow::Result;
use anyhow::{anyhow, Result};
use database::{collections::fetch_collection, enums::AssetPermissionRole};
use uuid::Uuid;
use crate::collections::types::{CollectionState, GetCollectionRequest};
@ -16,7 +17,15 @@ pub async fn get_collection_handler(
req: GetCollectionRequest,
) -> Result<CollectionState> {
// Reuse the existing collection_utils function
let collection = database::utils::collections::get_collection_by_id(user_id, &req.id).await?;
Ok(collection)
let collection = match fetch_collection(&req.id).await? {
Some(collection) => collection,
None => return Err(anyhow!("Collection not found")),
};
Ok(CollectionState {
collection,
assets: None,
permission: AssetPermissionRole::Owner,
organization_permissions: false,
})
}

View File

@ -1,17 +1,17 @@
// Collections handlers module
// mod create_collection_handler;
// mod delete_collection_handler;
// mod get_collection_handler;
mod create_collection_handler;
mod delete_collection_handler;
mod get_collection_handler;
mod list_collections_handler;
mod types;
// mod update_collection_handler;
mod update_collection_handler;
// Re-export types
pub use types::*;
// Re-export handlers
// pub use create_collection_handler::create_collection_handler;
// pub use delete_collection_handler::delete_collection_handler;
// pub use get_collection_handler::get_collection_handler;
pub use create_collection_handler::create_collection_handler;
pub use delete_collection_handler::delete_collection_handler;
pub use get_collection_handler::get_collection_handler;
pub use list_collections_handler::list_collections_handler;
// pub use update_collection_handler::update_collection_handler;
pub use update_collection_handler::update_collection_handler;

View File

@ -96,17 +96,12 @@ pub struct UpdateCollectionAssetsRequest {
pub type_: AssetType,
}
// #[derive(Debug, Clone, Deserialize, Serialize)]
// pub struct UpdateCollectionRequest {
// pub id: Uuid,
// #[serde(flatten)]
// pub collection: Option<UpdateCollectionObject>,
// pub assets: Option<Vec<UpdateCollectionAssetsRequest>>,
// pub team_permissions: Option<Vec<database::utils::sharing::asset_sharing::ShareWithTeamsReqObject>>,
// pub user_permissions: Option<Vec<database::utils::sharing::asset_sharing::ShareWithUsersReqObject>>,
// pub remove_teams: Option<Vec<Uuid>>,
// pub remove_users: Option<Vec<Uuid>>,
// }
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpdateCollectionRequest {
pub id: Uuid,
pub collection: Option<UpdateCollectionObject>,
pub assets: Option<Vec<UpdateCollectionAssetsRequest>>,
}
// Delete collection types
#[derive(Serialize, Deserialize, Debug, Clone)]

View File

@ -1,12 +1,13 @@
use anyhow::{anyhow, Result};
use chrono::{DateTime, Utc};
use database::{
enums::{AssetPermissionRole, AssetType},
collections::fetch_collection,
enums::AssetPermissionRole,
models::CollectionToAsset,
pool::get_pg_pool,
schema::{collections, collections_to_assets},
};
use diesel::{dsl::not, update, AsChangeset, BoolExpressionMethods, ExpressionMethods};
use diesel::{dsl::not, update, BoolExpressionMethods, ExpressionMethods};
use diesel_async::RunQueryDsl;
use std::sync::Arc;
use tokio;
@ -58,19 +59,6 @@ pub async fn update_collection_handler(
};
// Wait for all update operations to complete
if let Some(update_collection_permissions_handle) = update_collection_permissions_handle {
match update_collection_permissions_handle.await {
Ok(Ok(_)) => (),
Ok(Err(e)) => {
tracing::error!("Error updating collection permissions: {}", e);
return Err(anyhow!("Error updating collection permissions: {}", e));
}
Err(e) => {
tracing::error!("Error updating collection permissions: {}", e);
return Err(anyhow!("Error updating collection permissions: {}", e));
}
}
}
if let Some(update_collection_record_handle) = update_collection_record_handle {
match update_collection_record_handle.await {
@ -101,14 +89,23 @@ pub async fn update_collection_handler(
}
// Get the updated collection
let collection = database::utils::collections::get_collection_by_id(user_id.as_ref(), &req.id).await?;
Ok(collection)
let collection = match fetch_collection(&req.id).await? {
Some(collection) => collection,
None => return Err(anyhow!("Collection not found")),
};
Ok(CollectionState {
collection,
assets: None,
permission: AssetPermissionRole::Owner,
organization_permissions: false,
})
}
/// Update collection record in the database
///
/// # Arguments
/// # Arguments
/// * `user_id` - The ID of the user updating the collection
/// * `collection_id` - The ID of the collection to update
/// * `collection` - The collection update object
@ -176,7 +173,7 @@ async fn update_collection_record(
let query = diesel::sql_query(
"UPDATE asset_search
SET content = $1, updated_at = NOW()
WHERE asset_id = $2 AND asset_type = 'collection'"
WHERE asset_id = $2 AND asset_type = 'collection'",
)
.bind::<diesel::sql_types::Text, _>(collection_name)
.bind::<diesel::sql_types::Uuid, _>(*collection_id);
@ -248,7 +245,7 @@ async fn update_collection_assets(
updated_by: *user_id,
})
.collect();
match diesel::insert_into(collections_to_assets::table)
.values(&new_asset_records)
.on_conflict((

View File

@ -1,44 +1,30 @@
use axum::{
extract::State,
http::StatusCode,
Json,
};
use handlers::collections::{create_collection_handler, CreateCollectionRequest, CollectionState};
use axum::{extract::State, http::StatusCode, Extension, Json};
use handlers::collections::{create_collection_handler, CollectionState, CreateCollectionRequest};
use middleware::AuthenticatedUser;
use uuid::Uuid;
use database::utils::user::get_user_organization_id;
/// Create a new collection
///
/// This endpoint creates a new collection with the provided details.
pub async fn create_collection(
user: AuthenticatedUser,
Extension(user): Extension<AuthenticatedUser>,
Json(req): Json<CreateCollectionRequest>,
) -> Result<Json<CollectionState>, (StatusCode, String)> {
// Get the user's organization ID
let org_id = match get_user_organization_id(&user.id).await {
Ok(id) => id,
Err(e) => {
tracing::error!("Error getting user organization ID: {}", e);
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Error getting user organization: {}", e),
));
}
let user_organization = match user.organizations.first() {
Some(org) => org,
None => return Err((StatusCode::NOT_FOUND, "User not found".to_string())),
};
// Call the handler
match create_collection_handler(&user.id, &org_id, req).await {
match create_collection_handler(&user.id, &user_organization.id, req).await {
Ok(collection) => Ok(Json(collection)),
Err(e) => {
tracing::error!("Error creating collection: {}", e);
// Return appropriate error response based on the error
if e.to_string().contains("permission") {
Err((
StatusCode::FORBIDDEN,
format!("Permission denied: {}", e),
))
Err((StatusCode::FORBIDDEN, format!("Permission denied: {}", e)))
} else {
Err((
StatusCode::INTERNAL_SERVER_ERROR,

View File

@ -1,11 +1,9 @@
use axum::{
http::StatusCode,
Json,
use axum::{http::StatusCode, Extension, Json};
use handlers::collections::{
delete_collection_handler, DeleteCollectionRequest, DeleteCollectionResponse,
};
use handlers::collections::{delete_collection_handler, DeleteCollectionRequest, DeleteCollectionResponse};
use middleware::AuthenticatedUser;
use uuid::Uuid;
use database::utils::user::get_user_organization_id;
/// Delete a collection
///
@ -14,12 +12,17 @@ pub async fn delete_collection(
Extension(user): Extension<AuthenticatedUser>,
Json(req): Json<DeleteCollectionRequest>,
) -> Result<Json<DeleteCollectionResponse>, (StatusCode, String)> {
let user_organization = match user.organizations.first() {
Some(org) => org,
None => return Err((StatusCode::NOT_FOUND, "User not found".to_string())),
};
// Call the handler
match delete_collection_handler(&user.id, &user.organization_id, req.ids).await {
match delete_collection_handler(&user.id, &user_organization.id, req.ids).await {
Ok(response) => Ok(Json(response)),
Err(e) => {
tracing::error!("Error deleting collection: {}", e);
// Return appropriate error response based on the error
if e.to_string().contains("not found") {
Err((
@ -27,10 +30,7 @@ pub async fn delete_collection(
format!("Collection not found: {}", e),
))
} else if e.to_string().contains("permission") {
Err((
StatusCode::FORBIDDEN,
format!("Permission denied: {}", e),
))
Err((StatusCode::FORBIDDEN, format!("Permission denied: {}", e)))
} else {
Err((
StatusCode::INTERNAL_SERVER_ERROR,

View File

@ -1,12 +1,11 @@
use axum::{
extract::{Path, State},
extract::Path,
http::StatusCode,
Json,
Extension, Json,
};
use handlers::collections::{get_collection_handler, CollectionState};
use handlers::collections::{get_collection_handler, CollectionState, GetCollectionRequest};
use middleware::AuthenticatedUser;
use uuid::Uuid;
use axum::extract::Extension;
/// Get a collection by ID
///
@ -15,8 +14,10 @@ pub async fn get_collection(
Extension(user): Extension<AuthenticatedUser>,
Path(id): Path<Uuid>,
) -> Result<Json<CollectionState>, (StatusCode, String)> {
let request = GetCollectionRequest { id };
// Call the handler
match get_collection_handler(&user.id, &id).await {
match get_collection_handler(&user.id, request).await {
Ok(collection) => Ok(Json(collection)),
Err(e) => {
tracing::error!("Error getting collection: {}", e);

View File

@ -1,19 +1,19 @@
use axum::{
routing::get,
routing::{get, post, put, delete},
Router,
};
mod list_collections;
// mod get_collection;
// mod create_collection;
// mod update_collection;
// mod delete_collection;
mod get_collection;
mod create_collection;
mod update_collection;
mod delete_collection;
pub fn router() -> Router {
Router::new()
.route("/", get(list_collections::list_collections))
// .route("/", post(create_collection::create_collection))
// .route("/:id", get(get_collection::get_collection))
// .route("/:id", put(update_collection::update_collection))
// .route("/:id", delete(delete_collection::delete_collection))
.route("/", post(create_collection::create_collection))
.route("/:id", get(get_collection::get_collection))
.route("/:id", put(update_collection::update_collection))
.route("/:id", delete(delete_collection::delete_collection))
}

View File

@ -1,6 +1,6 @@
use axum::{
http::StatusCode,
Json,
Extension, Json,
};
use handlers::collections::{update_collection_handler, UpdateCollectionRequest, CollectionState};
use middleware::AuthenticatedUser;