mirror of https://github.com/buster-so/buster.git
Merge pull request #550 from buster-so/dallin/bus-1431-auto-sharing-for-queries-from-slack-channels
Dallin/bus-1431-auto-sharing-for-queries-from-slack-channels
This commit is contained in:
commit
47f03ac1d5
|
@ -78,7 +78,7 @@ pub async fn list_chats_handler(
|
|||
request: ListChatsRequest,
|
||||
user: &AuthenticatedUser,
|
||||
) -> Result<Vec<ChatListItem>> {
|
||||
use database::schema::{asset_permissions, chats, messages, users};
|
||||
use database::schema::{asset_permissions, chats, messages, users, user_favorites};
|
||||
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
|
||||
|
@ -152,7 +152,29 @@ pub async fn list_chats_handler(
|
|||
// Get user's organization IDs
|
||||
let user_org_ids: Vec<Uuid> = user.organizations.iter().map(|org| org.id).collect();
|
||||
|
||||
// Get user's favorited chat IDs
|
||||
let favorited_chat_ids: Vec<Uuid> = if !request.admin_view {
|
||||
asset_permissions::table
|
||||
.filter(asset_permissions::identity_id.eq(user.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::asset_id)
|
||||
.union(
|
||||
user_favorites::table
|
||||
.filter(user_favorites::user_id.eq(user.id))
|
||||
.filter(user_favorites::asset_type.eq(AssetType::Chat))
|
||||
.filter(user_favorites::deleted_at.is_null())
|
||||
.select(user_favorites::asset_id)
|
||||
)
|
||||
.load::<Uuid>(&mut conn)
|
||||
.await?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
// Second query: Get workspace-shared chats that the user doesn't have direct access to
|
||||
// but has either contributed to or favorited
|
||||
let workspace_shared_chats = if !request.admin_view && !user_org_ids.is_empty() {
|
||||
chats::table
|
||||
.inner_join(users::table.on(chats::created_by.eq(users::id)))
|
||||
|
@ -173,6 +195,24 @@ pub async fn list_chats_handler(
|
|||
))
|
||||
)
|
||||
)
|
||||
// Only include if user has contributed (created or updated a message) or favorited
|
||||
.filter(
|
||||
// User has favorited the chat
|
||||
chats::id.eq_any(&favorited_chat_ids)
|
||||
.or(
|
||||
// User has created a message in the chat
|
||||
diesel::dsl::exists(
|
||||
messages::table
|
||||
.filter(messages::chat_id.eq(chats::id))
|
||||
.filter(messages::created_by.eq(user.id))
|
||||
.filter(messages::deleted_at.is_null())
|
||||
)
|
||||
)
|
||||
.or(
|
||||
// User has updated the chat
|
||||
chats::updated_by.eq(user.id)
|
||||
)
|
||||
)
|
||||
.filter(
|
||||
diesel::dsl::exists(
|
||||
messages::table
|
||||
|
|
|
@ -0,0 +1,309 @@
|
|||
use anyhow::Result;
|
||||
use database::{
|
||||
enums::{AssetType, WorkspaceSharing},
|
||||
models::{Chat, Message, UserFavorite},
|
||||
pool::get_pg_pool,
|
||||
schema::{chats, messages, user_favorites},
|
||||
tests::common::db::{TestDb, TestSetup},
|
||||
};
|
||||
use diesel::prelude::*;
|
||||
use diesel_async::RunQueryDsl;
|
||||
use handlers::chats::{list_chats_handler, ListChatsRequest};
|
||||
use middleware::UserOrganization;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_workspace_shared_chats_filtered_without_contribution() -> Result<()> {
|
||||
let setup = TestSetup::new(None).await?;
|
||||
let mut conn = setup.db.diesel_conn().await?;
|
||||
|
||||
// Create another user in the same organization
|
||||
let other_user = setup.db.create_user("other@example.com", "Other User").await?;
|
||||
|
||||
// Create a workspace-shared chat by the other user
|
||||
let shared_chat = Chat {
|
||||
id: Uuid::new_v4(),
|
||||
title: "Shared Chat".to_string(),
|
||||
organization_id: setup.organization.id,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
created_by: other_user.id,
|
||||
updated_by: other_user.id,
|
||||
publicly_accessible: false,
|
||||
publicly_enabled_by: None,
|
||||
public_expiry_date: None,
|
||||
most_recent_file_id: None,
|
||||
most_recent_file_type: None,
|
||||
most_recent_version_number: None,
|
||||
workspace_sharing: WorkspaceSharing::CanView,
|
||||
workspace_sharing_enabled_by: Some(other_user.id),
|
||||
workspace_sharing_enabled_at: Some(chrono::Utc::now()),
|
||||
};
|
||||
|
||||
diesel::insert_into(chats::table)
|
||||
.values(&shared_chat)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
// Create a message in the chat by the other user
|
||||
let message = Message {
|
||||
id: Uuid::new_v4(),
|
||||
request_message: Some("Test message".to_string()),
|
||||
response_messages: serde_json::json!({}),
|
||||
reasoning: serde_json::json!({}),
|
||||
title: "Test".to_string(),
|
||||
raw_llm_messages: serde_json::json!({}),
|
||||
final_reasoning_message: None,
|
||||
chat_id: shared_chat.id,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
created_by: other_user.id,
|
||||
feedback: None,
|
||||
is_completed: true,
|
||||
post_processing_message: None,
|
||||
};
|
||||
|
||||
diesel::insert_into(messages::table)
|
||||
.values(&message)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
// List chats for the test user - should NOT see the shared chat
|
||||
let request = ListChatsRequest {
|
||||
page: Some(1),
|
||||
page_size: 10,
|
||||
admin_view: false,
|
||||
};
|
||||
|
||||
let auth_user = middleware::AuthenticatedUser {
|
||||
id: setup.user.id,
|
||||
email: setup.user.email.clone(),
|
||||
name: setup.user.name.clone(),
|
||||
organizations: vec![UserOrganization {
|
||||
id: setup.organization.id,
|
||||
name: setup.organization.name.clone(),
|
||||
}],
|
||||
};
|
||||
|
||||
let result = list_chats_handler(request, &auth_user).await?;
|
||||
|
||||
// Should not see the shared chat since user hasn't contributed
|
||||
assert_eq!(result.len(), 0);
|
||||
|
||||
// Clean up
|
||||
setup.db.cleanup().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_workspace_shared_chats_visible_with_contribution() -> Result<()> {
|
||||
let setup = TestSetup::new(None).await?;
|
||||
let mut conn = setup.db.diesel_conn().await?;
|
||||
|
||||
// Create another user in the same organization
|
||||
let other_user = setup.db.create_user("other@example.com", "Other User").await?;
|
||||
|
||||
// Create a workspace-shared chat by the other user
|
||||
let shared_chat = Chat {
|
||||
id: Uuid::new_v4(),
|
||||
title: "Shared Chat With Contribution".to_string(),
|
||||
organization_id: setup.organization.id,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
created_by: other_user.id,
|
||||
updated_by: other_user.id,
|
||||
publicly_accessible: false,
|
||||
publicly_enabled_by: None,
|
||||
public_expiry_date: None,
|
||||
most_recent_file_id: None,
|
||||
most_recent_file_type: None,
|
||||
most_recent_version_number: None,
|
||||
workspace_sharing: WorkspaceSharing::CanView,
|
||||
workspace_sharing_enabled_by: Some(other_user.id),
|
||||
workspace_sharing_enabled_at: Some(chrono::Utc::now()),
|
||||
};
|
||||
|
||||
diesel::insert_into(chats::table)
|
||||
.values(&shared_chat)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
// Create a message by the other user
|
||||
let message1 = Message {
|
||||
id: Uuid::new_v4(),
|
||||
request_message: Some("Other user message".to_string()),
|
||||
response_messages: serde_json::json!({}),
|
||||
reasoning: serde_json::json!({}),
|
||||
title: "Test".to_string(),
|
||||
raw_llm_messages: serde_json::json!({}),
|
||||
final_reasoning_message: None,
|
||||
chat_id: shared_chat.id,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
created_by: other_user.id,
|
||||
feedback: None,
|
||||
is_completed: true,
|
||||
post_processing_message: None,
|
||||
};
|
||||
|
||||
diesel::insert_into(messages::table)
|
||||
.values(&message1)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
// Create a message by the test user (contribution)
|
||||
let message2 = Message {
|
||||
id: Uuid::new_v4(),
|
||||
request_message: Some("Test user message".to_string()),
|
||||
response_messages: serde_json::json!({}),
|
||||
reasoning: serde_json::json!({}),
|
||||
title: "Test".to_string(),
|
||||
raw_llm_messages: serde_json::json!({}),
|
||||
final_reasoning_message: None,
|
||||
chat_id: shared_chat.id,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
created_by: setup.user.id, // Test user contributes
|
||||
feedback: None,
|
||||
is_completed: true,
|
||||
post_processing_message: None,
|
||||
};
|
||||
|
||||
diesel::insert_into(messages::table)
|
||||
.values(&message2)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
// List chats for the test user - should see the shared chat
|
||||
let request = ListChatsRequest {
|
||||
page: Some(1),
|
||||
page_size: 10,
|
||||
admin_view: false,
|
||||
};
|
||||
|
||||
let auth_user = middleware::AuthenticatedUser {
|
||||
id: setup.user.id,
|
||||
email: setup.user.email.clone(),
|
||||
name: setup.user.name.clone(),
|
||||
organizations: vec![UserOrganization {
|
||||
id: setup.organization.id,
|
||||
name: setup.organization.name.clone(),
|
||||
}],
|
||||
};
|
||||
|
||||
let result = list_chats_handler(request, &auth_user).await?;
|
||||
|
||||
// Should see the shared chat since user has contributed
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].name, "Shared Chat With Contribution");
|
||||
|
||||
// Clean up
|
||||
setup.db.cleanup().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_workspace_shared_chats_visible_when_favorited() -> Result<()> {
|
||||
let setup = TestSetup::new(None).await?;
|
||||
let mut conn = setup.db.diesel_conn().await?;
|
||||
|
||||
// Create another user in the same organization
|
||||
let other_user = setup.db.create_user("other@example.com", "Other User").await?;
|
||||
|
||||
// Create a workspace-shared chat by the other user
|
||||
let shared_chat = Chat {
|
||||
id: Uuid::new_v4(),
|
||||
title: "Favorited Shared Chat".to_string(),
|
||||
organization_id: setup.organization.id,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
created_by: other_user.id,
|
||||
updated_by: other_user.id,
|
||||
publicly_accessible: false,
|
||||
publicly_enabled_by: None,
|
||||
public_expiry_date: None,
|
||||
most_recent_file_id: None,
|
||||
most_recent_file_type: None,
|
||||
most_recent_version_number: None,
|
||||
workspace_sharing: WorkspaceSharing::CanView,
|
||||
workspace_sharing_enabled_by: Some(other_user.id),
|
||||
workspace_sharing_enabled_at: Some(chrono::Utc::now()),
|
||||
};
|
||||
|
||||
diesel::insert_into(chats::table)
|
||||
.values(&shared_chat)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
// Create a message by the other user
|
||||
let message = Message {
|
||||
id: Uuid::new_v4(),
|
||||
request_message: Some("Other user message".to_string()),
|
||||
response_messages: serde_json::json!({}),
|
||||
reasoning: serde_json::json!({}),
|
||||
title: "Test".to_string(),
|
||||
raw_llm_messages: serde_json::json!({}),
|
||||
final_reasoning_message: None,
|
||||
chat_id: shared_chat.id,
|
||||
created_at: chrono::Utc::now(),
|
||||
updated_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
created_by: other_user.id,
|
||||
feedback: None,
|
||||
is_completed: true,
|
||||
post_processing_message: None,
|
||||
};
|
||||
|
||||
diesel::insert_into(messages::table)
|
||||
.values(&message)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
// Add the chat to user's favorites
|
||||
let favorite = UserFavorite {
|
||||
user_id: setup.user.id,
|
||||
asset_id: shared_chat.id,
|
||||
asset_type: AssetType::Chat,
|
||||
order_index: 0,
|
||||
created_at: chrono::Utc::now(),
|
||||
deleted_at: None,
|
||||
};
|
||||
|
||||
diesel::insert_into(user_favorites::table)
|
||||
.values(&favorite)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
// List chats for the test user - should see the shared chat
|
||||
let request = ListChatsRequest {
|
||||
page: Some(1),
|
||||
page_size: 10,
|
||||
admin_view: false,
|
||||
};
|
||||
|
||||
let auth_user = middleware::AuthenticatedUser {
|
||||
id: setup.user.id,
|
||||
email: setup.user.email.clone(),
|
||||
name: setup.user.name.clone(),
|
||||
organizations: vec![UserOrganization {
|
||||
id: setup.organization.id,
|
||||
name: setup.organization.name.clone(),
|
||||
}],
|
||||
};
|
||||
|
||||
let result = list_chats_handler(request, &auth_user).await?;
|
||||
|
||||
// Should see the shared chat since user has favorited it
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].name, "Favorited Shared Chat");
|
||||
|
||||
// Clean up
|
||||
setup.db.cleanup().await?;
|
||||
Ok(())
|
||||
}
|
|
@ -33,3 +33,4 @@ pub mod sharing;
|
|||
pub mod metrics;
|
||||
pub mod dashboards;
|
||||
pub mod collections;
|
||||
pub mod chats_list_filter_test;
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { db } from '@buster/database';
|
||||
import { findOrCreateSlackChat } from './events';
|
||||
|
||||
vi.mock('@buster/database', () => ({
|
||||
db: {
|
||||
select: vi.fn(),
|
||||
insert: vi.fn(),
|
||||
},
|
||||
chats: {},
|
||||
slackIntegrations: {},
|
||||
}));
|
||||
|
||||
describe('findOrCreateSlackChat', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should create a chat with workspace sharing when Slack integration has shareWithWorkspace', async () => {
|
||||
// Mock implementations need to handle being called twice
|
||||
let selectCallCount = 0;
|
||||
|
||||
vi.mocked(db.select).mockImplementation(() => {
|
||||
selectCallCount++;
|
||||
|
||||
// First call is for existing chat, second is for slack integration
|
||||
if (selectCallCount === 1) {
|
||||
// Mock for existing chat query
|
||||
return {
|
||||
from: vi.fn().mockReturnThis(),
|
||||
where: vi.fn().mockReturnThis(),
|
||||
limit: vi.fn().mockResolvedValue([]),
|
||||
} as any;
|
||||
} else {
|
||||
// Mock for slack integration query
|
||||
return {
|
||||
from: vi.fn().mockReturnThis(),
|
||||
where: vi.fn().mockReturnThis(),
|
||||
limit: vi.fn().mockResolvedValue([{ defaultSharingPermissions: 'shareWithWorkspace' }]),
|
||||
} as any;
|
||||
}
|
||||
});
|
||||
|
||||
// Mock insert
|
||||
const mockInsert = {
|
||||
values: vi.fn().mockReturnThis(),
|
||||
returning: vi.fn().mockResolvedValue([{ id: 'new-chat-id' }]),
|
||||
};
|
||||
vi.mocked(db.insert).mockReturnValue(mockInsert as any);
|
||||
|
||||
const result = await findOrCreateSlackChat({
|
||||
threadTs: 'thread-123',
|
||||
channelId: 'channel-123',
|
||||
organizationId: 'org-123',
|
||||
userId: 'user-123',
|
||||
slackChatAuthorization: 'authorized',
|
||||
teamId: 'team-123',
|
||||
});
|
||||
|
||||
expect(result).toBe('new-chat-id');
|
||||
expect(mockInsert.values).toHaveBeenCalledWith({
|
||||
title: '',
|
||||
organizationId: 'org-123',
|
||||
createdBy: 'user-123',
|
||||
updatedBy: 'user-123',
|
||||
slackChatAuthorization: 'authorized',
|
||||
slackThreadTs: 'thread-123',
|
||||
slackChannelId: 'channel-123',
|
||||
workspaceSharing: 'can_view',
|
||||
workspaceSharingEnabledBy: 'user-123',
|
||||
workspaceSharingEnabledAt: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a chat without workspace sharing when Slack integration has different setting', async () => {
|
||||
// Mock implementations need to handle being called twice
|
||||
let selectCallCount = 0;
|
||||
|
||||
vi.mocked(db.select).mockImplementation(() => {
|
||||
selectCallCount++;
|
||||
|
||||
// First call is for existing chat, second is for slack integration
|
||||
if (selectCallCount === 1) {
|
||||
// Mock for existing chat query
|
||||
return {
|
||||
from: vi.fn().mockReturnThis(),
|
||||
where: vi.fn().mockReturnThis(),
|
||||
limit: vi.fn().mockResolvedValue([]),
|
||||
} as any;
|
||||
} else {
|
||||
// Mock for slack integration query
|
||||
return {
|
||||
from: vi.fn().mockReturnThis(),
|
||||
where: vi.fn().mockReturnThis(),
|
||||
limit: vi.fn().mockResolvedValue([{ defaultSharingPermissions: 'shareWithChannel' }]),
|
||||
} as any;
|
||||
}
|
||||
});
|
||||
|
||||
// Mock insert
|
||||
const mockInsert = {
|
||||
values: vi.fn().mockReturnThis(),
|
||||
returning: vi.fn().mockResolvedValue([{ id: 'new-chat-id' }]),
|
||||
};
|
||||
vi.mocked(db.insert).mockReturnValue(mockInsert as any);
|
||||
|
||||
const result = await findOrCreateSlackChat({
|
||||
threadTs: 'thread-123',
|
||||
channelId: 'channel-123',
|
||||
organizationId: 'org-123',
|
||||
userId: 'user-123',
|
||||
slackChatAuthorization: 'authorized',
|
||||
teamId: 'team-123',
|
||||
});
|
||||
|
||||
expect(result).toBe('new-chat-id');
|
||||
expect(mockInsert.values).toHaveBeenCalledWith({
|
||||
title: '',
|
||||
organizationId: 'org-123',
|
||||
createdBy: 'user-123',
|
||||
updatedBy: 'user-123',
|
||||
slackChatAuthorization: 'authorized',
|
||||
slackThreadTs: 'thread-123',
|
||||
slackChannelId: 'channel-123',
|
||||
workspaceSharing: 'none',
|
||||
workspaceSharingEnabledBy: null,
|
||||
workspaceSharingEnabledAt: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return existing chat if found', async () => {
|
||||
// Mock implementations need to handle being called twice
|
||||
let selectCallCount = 0;
|
||||
|
||||
vi.mocked(db.select).mockImplementation(() => {
|
||||
selectCallCount++;
|
||||
|
||||
// First call is for existing chat, second is for slack integration
|
||||
if (selectCallCount === 1) {
|
||||
// Mock for existing chat query - chat exists
|
||||
return {
|
||||
from: vi.fn().mockReturnThis(),
|
||||
where: vi.fn().mockReturnThis(),
|
||||
limit: vi.fn().mockResolvedValue([{ id: 'existing-chat-id' }]),
|
||||
} as any;
|
||||
} else {
|
||||
// Mock for slack integration query (won't be used due to early return)
|
||||
return {
|
||||
from: vi.fn().mockReturnThis(),
|
||||
where: vi.fn().mockReturnThis(),
|
||||
limit: vi.fn().mockResolvedValue([]),
|
||||
} as any;
|
||||
}
|
||||
});
|
||||
|
||||
const result = await findOrCreateSlackChat({
|
||||
threadTs: 'thread-123',
|
||||
channelId: 'channel-123',
|
||||
organizationId: 'org-123',
|
||||
userId: 'user-123',
|
||||
slackChatAuthorization: 'authorized',
|
||||
teamId: 'team-123',
|
||||
});
|
||||
|
||||
expect(result).toBe('existing-chat-id');
|
||||
expect(db.insert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should create chat without workspace sharing when no Slack integration found', async () => {
|
||||
// Mock implementations need to handle being called twice
|
||||
let selectCallCount = 0;
|
||||
|
||||
vi.mocked(db.select).mockImplementation(() => {
|
||||
selectCallCount++;
|
||||
|
||||
// First call is for existing chat, second is for slack integration
|
||||
if (selectCallCount === 1) {
|
||||
// Mock for existing chat query
|
||||
return {
|
||||
from: vi.fn().mockReturnThis(),
|
||||
where: vi.fn().mockReturnThis(),
|
||||
limit: vi.fn().mockResolvedValue([]),
|
||||
} as any;
|
||||
} else {
|
||||
// Mock for slack integration query - no integration found
|
||||
return {
|
||||
from: vi.fn().mockReturnThis(),
|
||||
where: vi.fn().mockReturnThis(),
|
||||
limit: vi.fn().mockResolvedValue([]),
|
||||
} as any;
|
||||
}
|
||||
});
|
||||
|
||||
// Mock insert
|
||||
const mockInsert = {
|
||||
values: vi.fn().mockReturnThis(),
|
||||
returning: vi.fn().mockResolvedValue([{ id: 'new-chat-id' }]),
|
||||
};
|
||||
vi.mocked(db.insert).mockReturnValue(mockInsert as any);
|
||||
|
||||
const result = await findOrCreateSlackChat({
|
||||
threadTs: 'thread-123',
|
||||
channelId: 'channel-123',
|
||||
organizationId: 'org-123',
|
||||
userId: 'user-123',
|
||||
slackChatAuthorization: 'authorized',
|
||||
teamId: 'team-123',
|
||||
});
|
||||
|
||||
expect(result).toBe('new-chat-id');
|
||||
expect(mockInsert.values).toHaveBeenCalledWith({
|
||||
title: '',
|
||||
organizationId: 'org-123',
|
||||
createdBy: 'user-123',
|
||||
updatedBy: 'user-123',
|
||||
slackChatAuthorization: 'authorized',
|
||||
slackThreadTs: 'thread-123',
|
||||
slackChannelId: 'channel-123',
|
||||
workspaceSharing: 'none',
|
||||
workspaceSharingEnabledBy: null,
|
||||
workspaceSharingEnabledAt: null,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import { chats, db } from '@buster/database';
|
||||
import { chats, db, slackIntegrations } from '@buster/database';
|
||||
import type { SlackEventsResponse } from '@buster/server-shared/slack';
|
||||
import { type SlackWebhookPayload, isEventCallback } from '@buster/slack';
|
||||
import { tasks } from '@trigger.dev/sdk';
|
||||
|
@ -37,26 +37,44 @@ export async function findOrCreateSlackChat({
|
|||
organizationId,
|
||||
userId,
|
||||
slackChatAuthorization,
|
||||
teamId,
|
||||
}: {
|
||||
threadTs: string;
|
||||
channelId: string;
|
||||
organizationId: string;
|
||||
userId: string;
|
||||
slackChatAuthorization: 'unauthorized' | 'authorized' | 'auto_added';
|
||||
teamId: string;
|
||||
}): Promise<string> {
|
||||
// Find existing chat
|
||||
const existingChat = await db
|
||||
.select()
|
||||
.from(chats)
|
||||
.where(
|
||||
and(
|
||||
eq(chats.slackThreadTs, threadTs),
|
||||
eq(chats.slackChannelId, channelId),
|
||||
eq(chats.organizationId, organizationId),
|
||||
eq(chats.createdBy, userId)
|
||||
// Run both queries concurrently for better performance
|
||||
const [existingChat, slackIntegration] = await Promise.all([
|
||||
// Find existing chat
|
||||
db
|
||||
.select()
|
||||
.from(chats)
|
||||
.where(
|
||||
and(
|
||||
eq(chats.slackThreadTs, threadTs),
|
||||
eq(chats.slackChannelId, channelId),
|
||||
eq(chats.organizationId, organizationId)
|
||||
)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
.limit(1),
|
||||
// Fetch Slack integration settings if we have an organization
|
||||
organizationId
|
||||
? db
|
||||
.select({ defaultSharingPermissions: slackIntegrations.defaultSharingPermissions })
|
||||
.from(slackIntegrations)
|
||||
.where(
|
||||
and(
|
||||
eq(slackIntegrations.organizationId, organizationId),
|
||||
eq(slackIntegrations.teamId, teamId),
|
||||
eq(slackIntegrations.status, 'active')
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
: Promise.resolve([]),
|
||||
]);
|
||||
|
||||
if (existingChat.length > 0) {
|
||||
const chat = existingChat[0];
|
||||
|
@ -66,6 +84,12 @@ export async function findOrCreateSlackChat({
|
|||
return chat.id;
|
||||
}
|
||||
|
||||
// Extract default sharing permissions
|
||||
const defaultSharingPermissions =
|
||||
slackIntegration.length > 0 && slackIntegration[0]
|
||||
? slackIntegration[0].defaultSharingPermissions
|
||||
: undefined;
|
||||
|
||||
// Create new chat
|
||||
const newChat = await db
|
||||
.insert(chats)
|
||||
|
@ -77,6 +101,11 @@ export async function findOrCreateSlackChat({
|
|||
slackChatAuthorization,
|
||||
slackThreadTs: threadTs,
|
||||
slackChannelId: channelId,
|
||||
// Set workspace sharing based on Slack integration settings
|
||||
workspaceSharing: defaultSharingPermissions === 'shareWithWorkspace' ? 'can_view' : 'none',
|
||||
workspaceSharingEnabledBy: defaultSharingPermissions === 'shareWithWorkspace' ? userId : null,
|
||||
workspaceSharingEnabledAt:
|
||||
defaultSharingPermissions === 'shareWithWorkspace' ? new Date().toISOString() : null,
|
||||
})
|
||||
.returning();
|
||||
|
||||
|
@ -166,6 +195,7 @@ export async function eventsHandler(payload: SlackWebhookPayload): Promise<Slack
|
|||
organizationId,
|
||||
userId,
|
||||
slackChatAuthorization: mapAuthResultToDbEnum(authResult.type),
|
||||
teamId: payload.team_id,
|
||||
});
|
||||
|
||||
// Queue the task
|
||||
|
|
|
@ -231,11 +231,11 @@ const SlackSharingPermissions = React.memo(() => {
|
|||
value: 'shareWithWorkspace',
|
||||
secondaryLabel: 'All workspace members will have access to any chat created from any channel.'
|
||||
},
|
||||
{
|
||||
label: 'Channel',
|
||||
value: 'shareWithChannel',
|
||||
secondaryLabel: 'All channel members will have access to any chat created from that channel.'
|
||||
},
|
||||
// {
|
||||
// label: 'Channel',
|
||||
// value: 'shareWithChannel',
|
||||
// secondaryLabel: 'All channel members will have access to any chat created from that channel.'
|
||||
// },
|
||||
{
|
||||
label: 'None',
|
||||
value: 'noSharing',
|
||||
|
|
|
@ -10,13 +10,13 @@ const mockCreate = vi.fn();
|
|||
describe('Reranker - Unit Tests', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
|
||||
process.env.RERANK_API_KEY = 'test-api-key';
|
||||
process.env.RERANK_BASE_URL = 'https://test-api.com/rerank';
|
||||
process.env.RERANK_MODEL = 'test-model';
|
||||
|
||||
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
|
||||
const mockAxiosInstance = {
|
||||
post: vi.fn(),
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue