mirror of https://github.com/buster-so/buster.git
ok added in individual permissions to the dashboards, collections, chats
This commit is contained in:
parent
a70b7c0ca6
commit
1c9d3b6eed
|
@ -7,10 +7,11 @@ use serde_json::Value;
|
|||
use tokio;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::chats::types::ChatWithMessages;
|
||||
use crate::chats::types::{BusterShareIndividual, ChatWithMessages};
|
||||
use crate::messages::types::{ChatMessage, ChatUserMessage};
|
||||
use database::enums::{AssetPermissionRole, AssetType, IdentityType};
|
||||
use database::pool::get_pg_pool;
|
||||
use database::schema::{chats, messages, users};
|
||||
use database::schema::{asset_permissions, chats, messages, users};
|
||||
|
||||
#[derive(Queryable)]
|
||||
pub struct ChatWithUser {
|
||||
|
@ -37,6 +38,14 @@ pub struct MessageWithUser {
|
|||
pub user_attributes: Value,
|
||||
}
|
||||
|
||||
#[derive(Queryable)]
|
||||
struct AssetPermissionInfo {
|
||||
identity_id: Uuid,
|
||||
role: AssetPermissionRole,
|
||||
email: String,
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn get_chat_handler(chat_id: &Uuid, user_id: &Uuid) -> Result<ChatWithMessages> {
|
||||
// Run thread and messages queries concurrently
|
||||
let thread_future = {
|
||||
|
@ -99,8 +108,50 @@ pub async fn get_chat_handler(chat_id: &Uuid, user_id: &Uuid) -> Result<ChatWith
|
|||
})
|
||||
};
|
||||
|
||||
// Wait for both queries and handle errors
|
||||
let (thread, messages) = tokio::try_join!(
|
||||
// Run permission query concurrently as well
|
||||
let permissions_future = {
|
||||
let mut conn = match get_pg_pool().get().await {
|
||||
Ok(conn) => conn,
|
||||
Err(e) => return Err(anyhow!("Failed to get database connection: {}", e)),
|
||||
};
|
||||
|
||||
let chat_id = chat_id.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
// Query individual permissions for this chat
|
||||
let permissions = asset_permissions::table
|
||||
.inner_join(users::table.on(users::id.eq(asset_permissions::identity_id)))
|
||||
.filter(asset_permissions::asset_id.eq(chat_id))
|
||||
.filter(asset_permissions::asset_type.eq(AssetType::Chat))
|
||||
.filter(asset_permissions::identity_type.eq(IdentityType::User))
|
||||
.filter(asset_permissions::deleted_at.is_null())
|
||||
.select((
|
||||
asset_permissions::identity_id,
|
||||
asset_permissions::role,
|
||||
users::email,
|
||||
users::name,
|
||||
))
|
||||
.load::<AssetPermissionInfo>(&mut conn)
|
||||
.await;
|
||||
|
||||
// Query publicly_accessible and related fields
|
||||
let public_info = chats::table
|
||||
.filter(chats::id.eq(chat_id))
|
||||
.select((
|
||||
chats::publicly_accessible,
|
||||
chats::publicly_enabled_by,
|
||||
chats::public_expiry_date,
|
||||
))
|
||||
.first::<(bool, Option<Uuid>, Option<DateTime<Utc>>)>(&mut conn)
|
||||
.await;
|
||||
|
||||
// Return both results
|
||||
(permissions, public_info)
|
||||
})
|
||||
};
|
||||
|
||||
// Wait for all queries and handle errors
|
||||
let (thread, messages, permissions_and_public_info) = tokio::try_join!(
|
||||
async move {
|
||||
thread_future
|
||||
.await
|
||||
|
@ -115,9 +166,17 @@ pub async fn get_chat_handler(chat_id: &Uuid, user_id: &Uuid) -> Result<ChatWith
|
|||
.await
|
||||
.map_err(|e| anyhow!("Messages task failed: {}", e))?
|
||||
.map_err(|e| anyhow!("Failed to load messages: {}", e))
|
||||
},
|
||||
async move {
|
||||
permissions_future
|
||||
.await
|
||||
.map_err(|e| anyhow!("Permissions task failed: {}", e))
|
||||
}
|
||||
)?;
|
||||
|
||||
// Unpack the permissions and public info results
|
||||
let (permissions_result, public_info_result) = permissions_and_public_info;
|
||||
|
||||
// Transform messages into ThreadMessage format
|
||||
let thread_messages: Vec<ChatMessage> = messages
|
||||
.into_iter()
|
||||
|
@ -166,8 +225,54 @@ pub async fn get_chat_handler(chat_id: &Uuid, user_id: &Uuid) -> Result<ChatWith
|
|||
.and_then(|v| v.as_str())
|
||||
.map(String::from);
|
||||
|
||||
// Construct and return the ThreadWithMessages using new_with_messages
|
||||
Ok(ChatWithMessages::new_with_messages(
|
||||
// Process permissions data
|
||||
let individual_permissions = match permissions_result {
|
||||
Ok(permissions) => {
|
||||
if permissions.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
permissions
|
||||
.into_iter()
|
||||
.map(|p| BusterShareIndividual {
|
||||
email: p.email,
|
||||
role: p.role,
|
||||
name: p.name,
|
||||
})
|
||||
.collect::<Vec<BusterShareIndividual>>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
// Get public access info
|
||||
let (publicly_accessible, publicly_enabled_by, public_expiry_date) = match public_info_result {
|
||||
Ok((accessible, enabled_by_id, expiry)) => {
|
||||
// Get the user info for publicly_enabled_by if it exists
|
||||
let enabled_by_email = if let Some(enabled_by_id) = enabled_by_id {
|
||||
let mut conn = match get_pg_pool().get().await {
|
||||
Ok(conn) => conn,
|
||||
Err(_) => return Err(anyhow!("Failed to get database connection")),
|
||||
};
|
||||
|
||||
users::table
|
||||
.filter(users::id.eq(enabled_by_id))
|
||||
.select(users::email)
|
||||
.first::<String>(&mut conn)
|
||||
.await
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
(accessible, enabled_by_email, expiry)
|
||||
}
|
||||
Err(_) => (false, None, None),
|
||||
};
|
||||
|
||||
// Construct and return the ChatWithMessages with permissions
|
||||
let chat = ChatWithMessages::new_with_messages(
|
||||
thread.id,
|
||||
thread.title,
|
||||
thread_messages,
|
||||
|
@ -175,5 +280,13 @@ pub async fn get_chat_handler(chat_id: &Uuid, user_id: &Uuid) -> Result<ChatWith
|
|||
thread.user_id.to_string(),
|
||||
thread.user_name.unwrap_or_else(|| "Unknown".to_string()),
|
||||
created_by_avatar,
|
||||
);
|
||||
|
||||
// Add permissions
|
||||
Ok(chat.with_permissions(
|
||||
individual_permissions,
|
||||
publicly_accessible,
|
||||
public_expiry_date,
|
||||
publicly_enabled_by
|
||||
))
|
||||
}
|
||||
|
|
|
@ -2,9 +2,18 @@ use std::collections::HashMap;
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
use database::enums::AssetPermissionRole;
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
use crate::messages::types::ChatMessage;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct BusterShareIndividual {
|
||||
pub email: String,
|
||||
pub role: AssetPermissionRole,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct ChatWithMessages {
|
||||
pub id: Uuid,
|
||||
|
@ -18,6 +27,12 @@ pub struct ChatWithMessages {
|
|||
pub created_by_id: String,
|
||||
pub created_by_name: String,
|
||||
pub created_by_avatar: Option<String>,
|
||||
// Sharing fields
|
||||
pub individual_permissions: Option<Vec<BusterShareIndividual>>,
|
||||
pub publicly_accessible: bool,
|
||||
pub public_expiry_date: Option<DateTime<Utc>>,
|
||||
pub public_enabled_by: Option<String>,
|
||||
pub public_password: Option<String>,
|
||||
}
|
||||
|
||||
impl ChatWithMessages {
|
||||
|
@ -40,6 +55,11 @@ impl ChatWithMessages {
|
|||
created_by_id,
|
||||
created_by_name,
|
||||
created_by_avatar,
|
||||
individual_permissions: None,
|
||||
publicly_accessible: false,
|
||||
public_expiry_date: None,
|
||||
public_enabled_by: None,
|
||||
public_password: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,8 +95,28 @@ impl ChatWithMessages {
|
|||
created_by_id,
|
||||
created_by_name,
|
||||
created_by_avatar,
|
||||
// Default values for sharing fields
|
||||
individual_permissions: None,
|
||||
publicly_accessible: false,
|
||||
public_expiry_date: None,
|
||||
public_enabled_by: None,
|
||||
public_password: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_permissions(
|
||||
mut self,
|
||||
individual_permissions: Option<Vec<BusterShareIndividual>>,
|
||||
publicly_accessible: bool,
|
||||
public_expiry_date: Option<DateTime<Utc>>,
|
||||
public_enabled_by: Option<String>,
|
||||
) -> Self {
|
||||
self.individual_permissions = individual_permissions;
|
||||
self.publicly_accessible = publicly_accessible;
|
||||
self.public_expiry_date = public_expiry_date;
|
||||
self.public_enabled_by = public_enabled_by;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_message(&mut self, message: ChatMessage) {
|
||||
let message_id = message.id.to_string();
|
||||
|
@ -95,7 +135,4 @@ impl ChatWithMessages {
|
|||
self.messages.insert(message_id, message);
|
||||
self.updated_at = chrono::Utc::now().to_rfc3339();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -144,5 +144,10 @@ pub async fn create_collection_handler(
|
|||
assets: None,
|
||||
permission: AssetPermissionRole::Owner,
|
||||
organization_permissions: false,
|
||||
individual_permissions: None,
|
||||
publicly_accessible: false,
|
||||
public_expiry_date: None,
|
||||
public_enabled_by: None,
|
||||
public_password: None,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -20,8 +20,8 @@ use crate::collections::types::DeleteCollectionResponse;
|
|||
/// # Returns
|
||||
/// * `Result<DeleteCollectionResponse>` - The IDs of the collections that were successfully deleted
|
||||
pub async fn delete_collection_handler(
|
||||
user_id: &Uuid,
|
||||
organization_id: &Uuid,
|
||||
_user_id: &Uuid,
|
||||
_organization_id: &Uuid,
|
||||
ids: Vec<Uuid>,
|
||||
) -> Result<DeleteCollectionResponse> {
|
||||
|
||||
|
|
|
@ -1,8 +1,19 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use database::{collections::fetch_collection, enums::AssetPermissionRole};
|
||||
use chrono::{DateTime, Utc};
|
||||
use database::{collections::fetch_collection, enums::{AssetPermissionRole, AssetType, IdentityType}, pool::get_pg_pool, schema::{asset_permissions, collections, users}};
|
||||
use diesel::{ExpressionMethods, JoinOnDsl, QueryDsl, Queryable};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::collections::types::{CollectionState, GetCollectionRequest};
|
||||
use crate::collections::types::{BusterShareIndividual, CollectionState, GetCollectionRequest};
|
||||
|
||||
#[derive(Queryable)]
|
||||
struct AssetPermissionInfo {
|
||||
identity_id: Uuid,
|
||||
role: AssetPermissionRole,
|
||||
email: String,
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
/// Handler for getting a single collection by ID
|
||||
///
|
||||
|
@ -21,11 +32,82 @@ pub async fn get_collection_handler(
|
|||
Some(collection) => collection,
|
||||
None => return Err(anyhow!("Collection not found")),
|
||||
};
|
||||
|
||||
let mut conn = match get_pg_pool().get().await {
|
||||
Ok(conn) => conn,
|
||||
Err(e) => return Err(anyhow!("Failed to get database connection: {}", e)),
|
||||
};
|
||||
|
||||
// Query individual permissions for this collection
|
||||
let individual_permissions_query = asset_permissions::table
|
||||
.inner_join(users::table.on(users::id.eq(asset_permissions::identity_id)))
|
||||
.filter(asset_permissions::asset_id.eq(req.id))
|
||||
.filter(asset_permissions::asset_type.eq(AssetType::Collection))
|
||||
.filter(asset_permissions::identity_type.eq(IdentityType::User))
|
||||
.filter(asset_permissions::deleted_at.is_null())
|
||||
.select((
|
||||
asset_permissions::identity_id,
|
||||
asset_permissions::role,
|
||||
users::email,
|
||||
users::name,
|
||||
))
|
||||
.load::<AssetPermissionInfo>(&mut conn)
|
||||
.await;
|
||||
|
||||
// For collections, we'll default public fields to false/none
|
||||
// since the schema doesn't have these fields yet
|
||||
let public_info: Result<(bool, Option<Uuid>, Option<DateTime<Utc>>), anyhow::Error> = Ok((false, None, None));
|
||||
|
||||
// Convert AssetPermissionInfo to BusterShareIndividual
|
||||
let individual_permissions = match individual_permissions_query {
|
||||
Ok(permissions) => {
|
||||
if permissions.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
permissions
|
||||
.into_iter()
|
||||
.map(|p| BusterShareIndividual {
|
||||
email: p.email,
|
||||
role: p.role,
|
||||
name: p.name,
|
||||
})
|
||||
.collect::<Vec<BusterShareIndividual>>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
// Get public access info
|
||||
let (publicly_accessible, public_enabled_by, public_expiry_date) = match public_info {
|
||||
Ok((accessible, enabled_by_id, expiry)) => {
|
||||
// Get the user info for publicly_enabled_by if it exists
|
||||
let enabled_by_email = if let Some(enabled_by_id) = enabled_by_id {
|
||||
users::table
|
||||
.filter(users::id.eq(enabled_by_id))
|
||||
.select(users::email)
|
||||
.first::<String>(&mut conn)
|
||||
.await
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
(accessible, enabled_by_email, expiry)
|
||||
}
|
||||
Err(_) => (false, None, None),
|
||||
};
|
||||
|
||||
Ok(CollectionState {
|
||||
collection,
|
||||
assets: None,
|
||||
permission: AssetPermissionRole::Owner,
|
||||
organization_permissions: false,
|
||||
individual_permissions,
|
||||
publicly_accessible,
|
||||
public_expiry_date,
|
||||
public_enabled_by,
|
||||
public_password: None,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,6 +7,13 @@ use diesel::AsChangeset;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct BusterShareIndividual {
|
||||
pub email: String,
|
||||
pub role: AssetPermissionRole,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
// List collections types
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ListCollectionsFilter {
|
||||
|
@ -68,10 +75,14 @@ pub struct CollectionState {
|
|||
#[serde(flatten)]
|
||||
pub collection: Collection,
|
||||
pub permission: AssetPermissionRole,
|
||||
// pub individual_permissions: Option<Vec<database::utils::sharing::asset_sharing::IndividualPermission>>,
|
||||
// pub team_permissions: Option<Vec<database::utils::sharing::asset_sharing::TeamPermissions>>,
|
||||
pub organization_permissions: bool,
|
||||
pub assets: Option<Vec<CollectionAsset>>,
|
||||
// Sharing fields
|
||||
pub individual_permissions: Option<Vec<BusterShareIndividual>>,
|
||||
pub publicly_accessible: bool,
|
||||
pub public_expiry_date: Option<DateTime<Utc>>,
|
||||
pub public_enabled_by: Option<String>,
|
||||
pub public_password: Option<String>,
|
||||
}
|
||||
|
||||
// Create collection types
|
||||
|
@ -112,4 +123,4 @@ pub struct DeleteCollectionRequest {
|
|||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct DeleteCollectionResponse {
|
||||
pub ids: Vec<Uuid>,
|
||||
}
|
||||
}
|
|
@ -99,6 +99,11 @@ pub async fn update_collection_handler(
|
|||
assets: None,
|
||||
permission: AssetPermissionRole::Owner,
|
||||
organization_permissions: false,
|
||||
individual_permissions: None,
|
||||
publicly_accessible: false,
|
||||
public_expiry_date: None,
|
||||
public_enabled_by: None,
|
||||
public_password: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
use uuid::Uuid;
|
||||
use crate::common::{
|
||||
env::{create_env, TestEnv},
|
||||
http::client::TestClient,
|
||||
assertions::response::assert_api_ok,
|
||||
};
|
||||
use chrono::Utc;
|
||||
use database::enums::{AssetPermissionRole, AssetTypeEnum, IdentityTypeEnum};
|
||||
use diesel::sql_query;
|
||||
use diesel_async::RunQueryDsl;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_chat_with_sharing_info() {
|
||||
// Setup test environment
|
||||
let env = create_env().await;
|
||||
let client = TestClient::new(&env);
|
||||
|
||||
// Create test user and chat
|
||||
let user_id = Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap();
|
||||
let chat_id = create_test_chat(&env, user_id).await;
|
||||
|
||||
// Add sharing permissions
|
||||
add_test_permissions(&env, chat_id, user_id).await;
|
||||
|
||||
// Add public sharing
|
||||
enable_public_sharing(&env, chat_id, user_id).await;
|
||||
|
||||
// Test GET request
|
||||
let response = client
|
||||
.get(&format!("/api/v1/chats/{}", chat_id))
|
||||
.header("X-User-Id", user_id.to_string())
|
||||
.send()
|
||||
.await;
|
||||
|
||||
// Assert success and verify response
|
||||
let data = assert_api_ok(response).await;
|
||||
|
||||
// Check fields
|
||||
assert_eq!(data["id"], chat_id.to_string());
|
||||
|
||||
// Check sharing fields
|
||||
assert_eq!(data["publicly_accessible"], true);
|
||||
assert!(data["public_expiry_date"].is_string());
|
||||
assert_eq!(data["public_enabled_by"], "test@example.com");
|
||||
assert_eq!(data["individual_permissions"].as_array().unwrap().len(), 1);
|
||||
|
||||
let permission = &data["individual_permissions"][0];
|
||||
assert_eq!(permission["email"], "test2@example.com");
|
||||
assert_eq!(permission["role"], "viewer");
|
||||
assert_eq!(permission["name"], "Test User 2");
|
||||
}
|
||||
|
||||
// Helper functions to set up the test data
|
||||
async fn create_test_chat(env: &TestEnv, user_id: Uuid) -> Uuid {
|
||||
let mut conn = env.db_pool.get().await.unwrap();
|
||||
|
||||
// Insert test user
|
||||
sql_query("INSERT INTO users (id, email, name) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING")
|
||||
.bind::<diesel::sql_types::Uuid, _>(user_id)
|
||||
.bind::<diesel::sql_types::Text, _>("test@example.com")
|
||||
.bind::<diesel::sql_types::Text, _>("Test User")
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Insert another test user
|
||||
let user2_id = Uuid::parse_str("00000000-0000-0000-0000-000000000002").unwrap();
|
||||
sql_query("INSERT INTO users (id, email, name) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING")
|
||||
.bind::<diesel::sql_types::Uuid, _>(user2_id)
|
||||
.bind::<diesel::sql_types::Text, _>("test2@example.com")
|
||||
.bind::<diesel::sql_types::Text, _>("Test User 2")
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Insert test chat
|
||||
let chat_id = Uuid::parse_str("00000000-0000-0000-0000-000000000030").unwrap();
|
||||
let org_id = Uuid::parse_str("00000000-0000-0000-0000-000000000100").unwrap();
|
||||
|
||||
// Insert test organization if needed
|
||||
sql_query("INSERT INTO organizations (id, name) VALUES ($1, $2) ON CONFLICT DO NOTHING")
|
||||
.bind::<diesel::sql_types::Uuid, _>(org_id)
|
||||
.bind::<diesel::sql_types::Text, _>("Test Organization")
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Insert chat
|
||||
sql_query(r#"
|
||||
INSERT INTO chats (id, title, organization_id, created_by, updated_by, publicly_accessible)
|
||||
VALUES ($1, 'Test Chat', $2, $3, $3, false)
|
||||
ON CONFLICT DO NOTHING
|
||||
"#)
|
||||
.bind::<diesel::sql_types::Uuid, _>(chat_id)
|
||||
.bind::<diesel::sql_types::Uuid, _>(org_id)
|
||||
.bind::<diesel::sql_types::Uuid, _>(user_id)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
chat_id
|
||||
}
|
||||
|
||||
async fn add_test_permissions(env: &TestEnv, chat_id: Uuid, user_id: Uuid) {
|
||||
let mut conn = env.db_pool.get().await.unwrap();
|
||||
|
||||
// Get the second user
|
||||
let user2_id = Uuid::parse_str("00000000-0000-0000-0000-000000000002").unwrap();
|
||||
|
||||
// Add permission for user2 as viewer
|
||||
sql_query(r#"
|
||||
INSERT INTO asset_permissions (identity_id, identity_type, asset_id, asset_type, role, created_by, updated_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $6)
|
||||
ON CONFLICT DO NOTHING
|
||||
"#)
|
||||
.bind::<diesel::sql_types::Uuid, _>(user2_id)
|
||||
.bind::<diesel::sql_types::Text, _>(IdentityTypeEnum::User.to_string())
|
||||
.bind::<diesel::sql_types::Uuid, _>(chat_id)
|
||||
.bind::<diesel::sql_types::Text, _>(AssetTypeEnum::Chat.to_string())
|
||||
.bind::<diesel::sql_types::Text, _>(AssetPermissionRole::CanView.to_string())
|
||||
.bind::<diesel::sql_types::Uuid, _>(user_id)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn enable_public_sharing(env: &TestEnv, chat_id: Uuid, user_id: Uuid) {
|
||||
let mut conn = env.db_pool.get().await.unwrap();
|
||||
|
||||
// Set public access
|
||||
let expiry_date = Utc::now() + chrono::Duration::days(7);
|
||||
|
||||
sql_query(r#"
|
||||
UPDATE chats
|
||||
SET publicly_accessible = true, publicly_enabled_by = $1, public_expiry_date = $2
|
||||
WHERE id = $3
|
||||
"#)
|
||||
.bind::<diesel::sql_types::Uuid, _>(user_id)
|
||||
.bind::<diesel::sql_types::Timestamptz, _>(expiry_date)
|
||||
.bind::<diesel::sql_types::Uuid, _>(chat_id)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
pub mod sharing;
|
||||
pub mod sharing;
|
||||
pub mod get_chat_test;
|
|
@ -0,0 +1,129 @@
|
|||
use uuid::Uuid;
|
||||
use crate::common::{
|
||||
env::{create_env, TestEnv},
|
||||
http::client::TestClient,
|
||||
assertions::response::assert_api_ok,
|
||||
};
|
||||
use chrono::Utc;
|
||||
use database::enums::{AssetPermissionRole, AssetTypeEnum, IdentityTypeEnum};
|
||||
use diesel::sql_query;
|
||||
use diesel_async::RunQueryDsl;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_collection_with_sharing_info() {
|
||||
// Setup test environment
|
||||
let env = create_env().await;
|
||||
let client = TestClient::new(&env);
|
||||
|
||||
// Create test user and collection
|
||||
let user_id = Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap();
|
||||
let collection_id = create_test_collection(&env, user_id).await;
|
||||
|
||||
// Add sharing permissions
|
||||
add_test_permissions(&env, collection_id, user_id).await;
|
||||
|
||||
// Add public sharing
|
||||
enable_public_sharing(&env, collection_id, user_id).await;
|
||||
|
||||
// Test GET request
|
||||
let response = client
|
||||
.get(&format!("/api/v1/collections/{}", collection_id))
|
||||
.header("X-User-Id", user_id.to_string())
|
||||
.send()
|
||||
.await;
|
||||
|
||||
// Assert success and verify response
|
||||
let data = assert_api_ok(response).await;
|
||||
|
||||
// Check fields
|
||||
assert_eq!(data["id"], collection_id.to_string());
|
||||
|
||||
// Check sharing fields - collections don't have public fields yet
|
||||
assert_eq!(data["publicly_accessible"], false);
|
||||
assert!(data["public_expiry_date"].is_null());
|
||||
assert!(data["public_enabled_by"].is_null());
|
||||
assert_eq!(data["individual_permissions"].as_array().unwrap().len(), 1);
|
||||
|
||||
let permission = &data["individual_permissions"][0];
|
||||
assert_eq!(permission["email"], "test2@example.com");
|
||||
assert_eq!(permission["role"], "viewer");
|
||||
assert_eq!(permission["name"], "Test User 2");
|
||||
}
|
||||
|
||||
// Helper functions to set up the test data
|
||||
async fn create_test_collection(env: &TestEnv, user_id: Uuid) -> Uuid {
|
||||
let mut conn = env.db_pool.get().await.unwrap();
|
||||
|
||||
// Insert test user
|
||||
sql_query("INSERT INTO users (id, email, name) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING")
|
||||
.bind::<diesel::sql_types::Uuid, _>(user_id)
|
||||
.bind::<diesel::sql_types::Text, _>("test@example.com")
|
||||
.bind::<diesel::sql_types::Text, _>("Test User")
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Insert another test user
|
||||
let user2_id = Uuid::parse_str("00000000-0000-0000-0000-000000000002").unwrap();
|
||||
sql_query("INSERT INTO users (id, email, name) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING")
|
||||
.bind::<diesel::sql_types::Uuid, _>(user2_id)
|
||||
.bind::<diesel::sql_types::Text, _>("test2@example.com")
|
||||
.bind::<diesel::sql_types::Text, _>("Test User 2")
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Insert test collection
|
||||
let collection_id = Uuid::parse_str("00000000-0000-0000-0000-000000000040").unwrap();
|
||||
let org_id = Uuid::parse_str("00000000-0000-0000-0000-000000000100").unwrap();
|
||||
|
||||
// Insert test organization if needed
|
||||
sql_query("INSERT INTO organizations (id, name) VALUES ($1, $2) ON CONFLICT DO NOTHING")
|
||||
.bind::<diesel::sql_types::Uuid, _>(org_id)
|
||||
.bind::<diesel::sql_types::Text, _>("Test Organization")
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Insert collection
|
||||
sql_query(r#"
|
||||
INSERT INTO collections (id, name, description, organization_id, created_by)
|
||||
VALUES ($1, 'Test Collection', 'Test description', $2, $3)
|
||||
ON CONFLICT DO NOTHING
|
||||
"#)
|
||||
.bind::<diesel::sql_types::Uuid, _>(collection_id)
|
||||
.bind::<diesel::sql_types::Uuid, _>(org_id)
|
||||
.bind::<diesel::sql_types::Uuid, _>(user_id)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
collection_id
|
||||
}
|
||||
|
||||
async fn add_test_permissions(env: &TestEnv, collection_id: Uuid, user_id: Uuid) {
|
||||
let mut conn = env.db_pool.get().await.unwrap();
|
||||
|
||||
// Get the second user
|
||||
let user2_id = Uuid::parse_str("00000000-0000-0000-0000-000000000002").unwrap();
|
||||
|
||||
// Add permission for user2 as viewer
|
||||
sql_query(r#"
|
||||
INSERT INTO asset_permissions (identity_id, identity_type, asset_id, asset_type, role, created_by, updated_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $6)
|
||||
ON CONFLICT DO NOTHING
|
||||
"#)
|
||||
.bind::<diesel::sql_types::Uuid, _>(user2_id)
|
||||
.bind::<diesel::sql_types::Text, _>(IdentityTypeEnum::User.to_string())
|
||||
.bind::<diesel::sql_types::Uuid, _>(collection_id)
|
||||
.bind::<diesel::sql_types::Text, _>(AssetTypeEnum::Collection.to_string())
|
||||
.bind::<diesel::sql_types::Text, _>(AssetPermissionRole::CanView.to_string())
|
||||
.bind::<diesel::sql_types::Uuid, _>(user_id)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn enable_public_sharing(_env: &TestEnv, _collection_id: Uuid, _user_id: Uuid) {
|
||||
// Collections don't have public sharing fields yet, so this is a no-op
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
pub mod sharing;
|
||||
pub mod sharing;
|
||||
pub mod get_collection_test;
|
Loading…
Reference in New Issue