From aa73186b2a03714bf105be5e3b290e00893039c5 Mon Sep 17 00:00:00 2001 From: Wells Bunker Date: Wed, 1 Oct 2025 10:43:36 -0600 Subject: [PATCH] Adding api for uploading and retrieving screenshots, also adding improvements to get ancestors --- apps/server/src/api/v2/chats/[id]/index.ts | 2 + .../src/api/v2/chats/[id]/screenshot/GET.ts | 57 + .../src/api/v2/chats/[id]/screenshot/PUT.ts | 53 + .../src/api/v2/chats/[id]/screenshot/index.ts | 7 + .../src/api/v2/dashboards/[id]/index.ts | 4 +- .../api/v2/dashboards/[id]/screenshot/GET.ts | 57 + .../api/v2/dashboards/[id]/screenshot/PUT.ts | 53 + .../v2/dashboards/[id]/screenshot/index.ts | 7 + .../src/api/v2/metric_files/[id]/index.ts | 2 + .../v2/metric_files/[id]/screenshot/GET.ts | 57 + .../v2/metric_files/[id]/screenshot/PUT.ts | 57 + .../v2/metric_files/[id]/screenshot/index.ts | 7 + apps/server/src/api/v2/reports/[id]/index.ts | 7 +- .../src/api/v2/reports/[id]/screenshot/GET.ts | 58 + .../src/api/v2/reports/[id]/screenshot/PUT.ts | 57 + .../api/v2/reports/[id]/screenshot/index.ts | 7 + .../upload-screenshot-handler.ts | 89 + .../src/storage/storage-factory.test.ts | 14 +- .../src/storage/storage-factory.ts | 2 +- .../drizzle/0115_glamorous_penance.sql | 2 + .../database/drizzle/meta/0115_snapshot.json | 6118 +++++++++++++++++ packages/database/drizzle/meta/_journal.json | 7 + .../src/queries/assets/asset-ancestors.ts | 177 +- packages/database/src/queries/assets/index.ts | 3 + packages/database/src/schema.ts | 4 + .../src/asset-search/get-asset-screenshot.ts | 57 + packages/search/src/asset-search/index.ts | 1 + .../search/src/asset-search/search.test.ts | 53 +- packages/search/src/asset-search/search.ts | 36 +- packages/server-shared/package.json | 4 + packages/server-shared/src/index.ts | 1 + .../server-shared/src/screenshots/index.ts | 1 + .../src/screenshots/screenshots.ts | 25 + 33 files changed, 7036 insertions(+), 50 deletions(-) create mode 100644 apps/server/src/api/v2/chats/[id]/screenshot/GET.ts create mode 100644 apps/server/src/api/v2/chats/[id]/screenshot/PUT.ts create mode 100644 apps/server/src/api/v2/chats/[id]/screenshot/index.ts create mode 100644 apps/server/src/api/v2/dashboards/[id]/screenshot/GET.ts create mode 100644 apps/server/src/api/v2/dashboards/[id]/screenshot/PUT.ts create mode 100644 apps/server/src/api/v2/dashboards/[id]/screenshot/index.ts create mode 100644 apps/server/src/api/v2/metric_files/[id]/screenshot/GET.ts create mode 100644 apps/server/src/api/v2/metric_files/[id]/screenshot/PUT.ts create mode 100644 apps/server/src/api/v2/metric_files/[id]/screenshot/index.ts create mode 100644 apps/server/src/api/v2/reports/[id]/screenshot/GET.ts create mode 100644 apps/server/src/api/v2/reports/[id]/screenshot/PUT.ts create mode 100644 apps/server/src/api/v2/reports/[id]/screenshot/index.ts create mode 100644 apps/server/src/shared-helpers/upload-screenshot-handler.ts create mode 100644 packages/database/drizzle/0115_glamorous_penance.sql create mode 100644 packages/database/drizzle/meta/0115_snapshot.json create mode 100644 packages/search/src/asset-search/get-asset-screenshot.ts create mode 100644 packages/server-shared/src/screenshots/index.ts create mode 100644 packages/server-shared/src/screenshots/screenshots.ts diff --git a/apps/server/src/api/v2/chats/[id]/index.ts b/apps/server/src/api/v2/chats/[id]/index.ts index 3cebe2107..f42e6c13f 100644 --- a/apps/server/src/api/v2/chats/[id]/index.ts +++ b/apps/server/src/api/v2/chats/[id]/index.ts @@ -1,10 +1,12 @@ import { Hono } from 'hono'; import GET from './GET'; +import SCREENSHOT from './screenshot'; import SHARING from './sharing'; const app = new Hono(); app.route('/', GET); app.route('/sharing', SHARING); +app.route('/screenshot', SCREENSHOT); export default app; diff --git a/apps/server/src/api/v2/chats/[id]/screenshot/GET.ts b/apps/server/src/api/v2/chats/[id]/screenshot/GET.ts new file mode 100644 index 000000000..6da1f6157 --- /dev/null +++ b/apps/server/src/api/v2/chats/[id]/screenshot/GET.ts @@ -0,0 +1,57 @@ +import { checkPermission } from '@buster/access-controls'; +import { getChatById } from '@buster/database/queries'; +import { getAssetScreenshotSignedUrl } from '@buster/search'; +import { AssetIdParamsSchema, type GetScreenshotResponse } from '@buster/server-shared/screenshots'; +import { zValidator } from '@hono/zod-validator'; +import { Hono } from 'hono'; +import { HTTPException } from 'hono/http-exception'; + +const app = new Hono().get('/', zValidator('param', AssetIdParamsSchema), async (c) => { + const { id } = c.req.valid('param'); + const user = c.get('busterUser'); + + const chat = await getChatById(id); + if (!chat) { + throw new HTTPException(404, { message: 'Chat not found' }); + } + + const permission = await checkPermission({ + userId: user.id, + assetId: id, + assetType: 'chat', + requiredRole: 'can_view', + workspaceSharing: chat.workspaceSharing, + organizationId: chat.organizationId, + }); + + if (!permission.hasAccess) { + throw new HTTPException(403, { + message: 'You do not have permission to view this chat', + }); + } + + let signedUrl = ''; + let success = true; + + try { + signedUrl = await getAssetScreenshotSignedUrl({ + assetType: 'chat', + assetId: id, + organizationId: chat.organizationId, + }); + } catch (error) { + console.error('Failed to generate chat screenshot URL', { + chatId: id, + error, + }); + success = false; + } + + const response: GetScreenshotResponse = { + success, + url: signedUrl, + }; + return c.json(response); +}); + +export default app; diff --git a/apps/server/src/api/v2/chats/[id]/screenshot/PUT.ts b/apps/server/src/api/v2/chats/[id]/screenshot/PUT.ts new file mode 100644 index 000000000..7769d2433 --- /dev/null +++ b/apps/server/src/api/v2/chats/[id]/screenshot/PUT.ts @@ -0,0 +1,53 @@ +import { checkPermission } from '@buster/access-controls'; +import { getChatById } from '@buster/database/queries'; +import { + AssetIdParamsSchema, + PutScreenshotRequestSchema, + type PutScreenshotResponse, +} from '@buster/server-shared/screenshots'; +import { zValidator } from '@hono/zod-validator'; +import { Hono } from 'hono'; +import { HTTPException } from 'hono/http-exception'; +import { uploadScreenshotHandler } from '../../../../../shared-helpers/upload-screenshot-handler'; + +const app = new Hono().put( + '/', + zValidator('json', PutScreenshotRequestSchema), + zValidator('param', AssetIdParamsSchema), + async (c) => { + const assetId = c.req.valid('param').id; + const { base64Image } = c.req.valid('json'); + const user = c.get('busterUser'); + + const chat = await getChatById(assetId); + if (!chat) { + throw new HTTPException(404, { message: 'Chat not found' }); + } + + const permission = await checkPermission({ + userId: user.id, + assetId, + assetType: 'chat', + requiredRole: 'can_edit', + workspaceSharing: chat.workspaceSharing, + organizationId: chat.organizationId, + }); + + if (!permission.hasAccess) { + throw new HTTPException(403, { + message: 'You do not have permission to upload a screenshot for this chat', + }); + } + + const result: PutScreenshotResponse = await uploadScreenshotHandler({ + assetType: 'chat', + assetId, + base64Image, + organizationId: chat.organizationId, + }); + + return c.json(result); + } +); + +export default app; diff --git a/apps/server/src/api/v2/chats/[id]/screenshot/index.ts b/apps/server/src/api/v2/chats/[id]/screenshot/index.ts new file mode 100644 index 000000000..6a7840d91 --- /dev/null +++ b/apps/server/src/api/v2/chats/[id]/screenshot/index.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono'; +import GET from './GET'; +import PUT from './PUT'; + +const app = new Hono().route('/', GET).route('/', PUT); + +export default app; diff --git a/apps/server/src/api/v2/dashboards/[id]/index.ts b/apps/server/src/api/v2/dashboards/[id]/index.ts index 11abb7e7c..428178c29 100644 --- a/apps/server/src/api/v2/dashboards/[id]/index.ts +++ b/apps/server/src/api/v2/dashboards/[id]/index.ts @@ -1,11 +1,13 @@ import { Hono } from 'hono'; import '../../../../types/hono.types'; import dashboardByIdRoutes from './GET'; +import SCREENSHOT from './screenshot'; import SHARING from './sharing'; const app = new Hono() // /dashboards/:id GET .route('/', dashboardByIdRoutes) - .route('/sharing', SHARING); + .route('/sharing', SHARING) + .route('/screenshot', SCREENSHOT); export default app; diff --git a/apps/server/src/api/v2/dashboards/[id]/screenshot/GET.ts b/apps/server/src/api/v2/dashboards/[id]/screenshot/GET.ts new file mode 100644 index 000000000..50fa33bb7 --- /dev/null +++ b/apps/server/src/api/v2/dashboards/[id]/screenshot/GET.ts @@ -0,0 +1,57 @@ +import { checkPermission } from '@buster/access-controls'; +import { getDashboardById } from '@buster/database/queries'; +import { getAssetScreenshotSignedUrl } from '@buster/search'; +import { AssetIdParamsSchema, type GetScreenshotResponse } from '@buster/server-shared/screenshots'; +import { zValidator } from '@hono/zod-validator'; +import { Hono } from 'hono'; +import { HTTPException } from 'hono/http-exception'; + +const app = new Hono().get('/', zValidator('param', AssetIdParamsSchema), async (c) => { + const dashboardId = c.req.valid('param').id; + const user = c.get('busterUser'); + + const dashboard = await getDashboardById({ dashboardId }); + if (!dashboard) { + throw new HTTPException(404, { message: 'Dashboard not found' }); + } + + const permission = await checkPermission({ + userId: user.id, + assetId: dashboardId, + assetType: 'dashboard_file', + requiredRole: 'can_view', + workspaceSharing: dashboard.workspaceSharing, + organizationId: dashboard.organizationId, + }); + + if (!permission.hasAccess) { + throw new HTTPException(403, { + message: 'You do not have permission to view this dashboard', + }); + } + + let signedUrl = ''; + let success = true; + + try { + signedUrl = await getAssetScreenshotSignedUrl({ + assetType: 'dashboard_file', + assetId: dashboardId, + organizationId: dashboard.organizationId, + }); + } catch (error) { + console.error('Failed to generate dashboard screenshot URL', { + dashboardId, + error, + }); + success = false; + } + + const response: GetScreenshotResponse = { + success, + url: signedUrl, + }; + return c.json(response); +}); + +export default app; diff --git a/apps/server/src/api/v2/dashboards/[id]/screenshot/PUT.ts b/apps/server/src/api/v2/dashboards/[id]/screenshot/PUT.ts new file mode 100644 index 000000000..203c5409b --- /dev/null +++ b/apps/server/src/api/v2/dashboards/[id]/screenshot/PUT.ts @@ -0,0 +1,53 @@ +import { checkPermission } from '@buster/access-controls'; +import { getDashboardById } from '@buster/database/queries'; +import { + AssetIdParamsSchema, + PutScreenshotRequestSchema, + type PutScreenshotResponse, +} from '@buster/server-shared/screenshots'; +import { zValidator } from '@hono/zod-validator'; +import { Hono } from 'hono'; +import { HTTPException } from 'hono/http-exception'; +import { uploadScreenshotHandler } from '../../../../../shared-helpers/upload-screenshot-handler'; + +const app = new Hono().put( + '/', + zValidator('json', PutScreenshotRequestSchema), + zValidator('param', AssetIdParamsSchema), + async (c) => { + const assetId = c.req.valid('param').id; + const { base64Image } = c.req.valid('json'); + const user = c.get('busterUser'); + + const dashboard = await getDashboardById({ dashboardId: assetId }); + if (!dashboard) { + throw new HTTPException(404, { message: 'Dashboard not found' }); + } + + const permission = await checkPermission({ + userId: user.id, + assetId, + assetType: 'dashboard_file', + requiredRole: 'can_edit', + workspaceSharing: dashboard.workspaceSharing, + organizationId: dashboard.organizationId, + }); + + if (!permission.hasAccess) { + throw new HTTPException(403, { + message: 'You do not have permission to upload a screenshot for this dashboard', + }); + } + + const result: PutScreenshotResponse = await uploadScreenshotHandler({ + assetType: 'dashboard_file', + assetId, + base64Image, + organizationId: dashboard.organizationId, + }); + + return c.json(result); + } +); + +export default app; diff --git a/apps/server/src/api/v2/dashboards/[id]/screenshot/index.ts b/apps/server/src/api/v2/dashboards/[id]/screenshot/index.ts new file mode 100644 index 000000000..6a7840d91 --- /dev/null +++ b/apps/server/src/api/v2/dashboards/[id]/screenshot/index.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono'; +import GET from './GET'; +import PUT from './PUT'; + +const app = new Hono().route('/', GET).route('/', PUT); + +export default app; diff --git a/apps/server/src/api/v2/metric_files/[id]/index.ts b/apps/server/src/api/v2/metric_files/[id]/index.ts index af121e56c..4bae08354 100644 --- a/apps/server/src/api/v2/metric_files/[id]/index.ts +++ b/apps/server/src/api/v2/metric_files/[id]/index.ts @@ -3,6 +3,7 @@ import { standardErrorHandler } from '../../../../utils/response'; import GET from './GET'; import DATA from './data/GET'; import DOWNLOAD from './download/GET'; +import SCREENSHOT from './screenshot'; import SHARING from './sharing'; const app = new Hono() @@ -10,6 +11,7 @@ const app = new Hono() .route('/data', DATA) .route('/download', DOWNLOAD) .route('/sharing', SHARING) + .route('/screenshot', SCREENSHOT) .onError(standardErrorHandler); export default app; diff --git a/apps/server/src/api/v2/metric_files/[id]/screenshot/GET.ts b/apps/server/src/api/v2/metric_files/[id]/screenshot/GET.ts new file mode 100644 index 000000000..92db1db80 --- /dev/null +++ b/apps/server/src/api/v2/metric_files/[id]/screenshot/GET.ts @@ -0,0 +1,57 @@ +import { checkPermission } from '@buster/access-controls'; +import { getMetricFileById } from '@buster/database/queries'; +import { getAssetScreenshotSignedUrl } from '@buster/search'; +import { AssetIdParamsSchema, type GetScreenshotResponse } from '@buster/server-shared/screenshots'; +import { zValidator } from '@hono/zod-validator'; +import { Hono } from 'hono'; +import { HTTPException } from 'hono/http-exception'; + +const app = new Hono().get('/', zValidator('param', AssetIdParamsSchema), async (c) => { + const metricId = c.req.valid('param').id; + const user = c.get('busterUser'); + + const metric = await getMetricFileById(metricId); + if (!metric) { + throw new HTTPException(404, { message: 'Metric not found' }); + } + + const permission = await checkPermission({ + userId: user.id, + assetId: metricId, + assetType: 'metric_file', + requiredRole: 'can_view', + workspaceSharing: metric.workspaceSharing, + organizationId: metric.organizationId, + }); + + if (!permission.hasAccess) { + throw new HTTPException(403, { + message: 'You do not have permission to view this metric', + }); + } + + let signedUrl = ''; + let success = true; + + try { + signedUrl = await getAssetScreenshotSignedUrl({ + assetType: 'metric_file', + assetId: metricId, + organizationId: metric.organizationId, + }); + } catch (error) { + console.error('Failed to generate metric screenshot URL', { + metricId, + error, + }); + success = false; + } + + const response: GetScreenshotResponse = { + success, + url: signedUrl, + }; + return c.json(response); +}); + +export default app; diff --git a/apps/server/src/api/v2/metric_files/[id]/screenshot/PUT.ts b/apps/server/src/api/v2/metric_files/[id]/screenshot/PUT.ts new file mode 100644 index 000000000..04e163dca --- /dev/null +++ b/apps/server/src/api/v2/metric_files/[id]/screenshot/PUT.ts @@ -0,0 +1,57 @@ +import { checkPermission } from '@buster/access-controls'; +import { getMetricFileById } from '@buster/database/queries'; +import { + AssetIdParamsSchema, + PutScreenshotRequestSchema, + type PutScreenshotResponse, +} from '@buster/server-shared/screenshots'; +import { zValidator } from '@hono/zod-validator'; +import { Hono } from 'hono'; +import { HTTPException } from 'hono/http-exception'; +import { uploadScreenshotHandler } from '../../../../../shared-helpers/upload-screenshot-handler'; + +const app = new Hono().put( + '/', + zValidator('json', PutScreenshotRequestSchema), + zValidator('param', AssetIdParamsSchema), + async (c) => { + const assetId = c.req.valid('param').id; + const { base64Image } = c.req.valid('json'); + const user = c.get('busterUser'); + + const metric = await getMetricFileById(assetId); + if (!metric) { + throw new HTTPException(404, { message: 'Metric not found' }); + } + + const permission = await checkPermission({ + userId: user.id, + assetId, + assetType: 'metric_file', + requiredRole: 'can_edit', + workspaceSharing: metric.workspaceSharing, + organizationId: metric.organizationId, + }); + + if (!permission.hasAccess) { + throw new HTTPException(403, { + message: 'You do not have permission to upload a screenshot for this metric', + }); + } + + if (!assetId) { + throw new HTTPException(404, { message: 'Metric not found' }); + } + + const result: PutScreenshotResponse = await uploadScreenshotHandler({ + assetType: 'metric_file', + assetId, + base64Image, + organizationId: metric.organizationId, + }); + + return c.json(result); + } +); + +export default app; diff --git a/apps/server/src/api/v2/metric_files/[id]/screenshot/index.ts b/apps/server/src/api/v2/metric_files/[id]/screenshot/index.ts new file mode 100644 index 000000000..6a7840d91 --- /dev/null +++ b/apps/server/src/api/v2/metric_files/[id]/screenshot/index.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono'; +import GET from './GET'; +import PUT from './PUT'; + +const app = new Hono().route('/', GET).route('/', PUT); + +export default app; diff --git a/apps/server/src/api/v2/reports/[id]/index.ts b/apps/server/src/api/v2/reports/[id]/index.ts index d639fa164..0b355eed7 100644 --- a/apps/server/src/api/v2/reports/[id]/index.ts +++ b/apps/server/src/api/v2/reports/[id]/index.ts @@ -1,8 +1,13 @@ import { Hono } from 'hono'; import GET from './GET'; import PUT from './PUT'; +import SCREENSHOT from './screenshot'; import SHARING from './sharing'; -const app = new Hono().route('/', GET).route('/', PUT).route('/sharing', SHARING); +const app = new Hono() + .route('/', GET) + .route('/', PUT) + .route('/sharing', SHARING) + .route('/screenshot', SCREENSHOT); export default app; diff --git a/apps/server/src/api/v2/reports/[id]/screenshot/GET.ts b/apps/server/src/api/v2/reports/[id]/screenshot/GET.ts new file mode 100644 index 000000000..59be91e36 --- /dev/null +++ b/apps/server/src/api/v2/reports/[id]/screenshot/GET.ts @@ -0,0 +1,58 @@ +import { checkPermission } from '@buster/access-controls'; +import { getReportFileById } from '@buster/database/queries'; +import { getAssetScreenshotSignedUrl } from '@buster/search'; +import { AssetIdParamsSchema, type GetScreenshotResponse } from '@buster/server-shared/screenshots'; +import { zValidator } from '@hono/zod-validator'; +import { Hono } from 'hono'; +import { HTTPException } from 'hono/http-exception'; + +const app = new Hono().get('/', zValidator('param', AssetIdParamsSchema), async (c) => { + const reportId = c.req.valid('param').id; + const user = c.get('busterUser'); + + const report = await getReportFileById({ reportId, userId: user.id }); + + if (!report) { + throw new HTTPException(404, { message: 'Report not found' }); + } + + const permission = await checkPermission({ + userId: user.id, + assetId: reportId, + assetType: 'report_file', + requiredRole: 'can_view', + workspaceSharing: report.workspace_sharing, + organizationId: report.organization_id, + }); + + if (!permission.hasAccess) { + throw new HTTPException(403, { + message: 'You do not have permission to view this report', + }); + } + + let signedUrl = ''; + let success = true; + + try { + signedUrl = await getAssetScreenshotSignedUrl({ + assetType: 'report_file', + assetId: reportId, + organizationId: report.organization_id, + }); + } catch (error) { + console.error('Failed to generate report screenshot URL', { + reportId, + error, + }); + success = false; + } + + const response: GetScreenshotResponse = { + success, + url: signedUrl, + }; + return c.json(response); +}); + +export default app; diff --git a/apps/server/src/api/v2/reports/[id]/screenshot/PUT.ts b/apps/server/src/api/v2/reports/[id]/screenshot/PUT.ts new file mode 100644 index 000000000..aa08890b2 --- /dev/null +++ b/apps/server/src/api/v2/reports/[id]/screenshot/PUT.ts @@ -0,0 +1,57 @@ +import { checkPermission } from '@buster/access-controls'; +import { getReportFileById } from '@buster/database/queries'; +import { + AssetIdParamsSchema, + PutScreenshotRequestSchema, + type PutScreenshotResponse, +} from '@buster/server-shared/screenshots'; +import { zValidator } from '@hono/zod-validator'; +import { Hono } from 'hono'; +import { HTTPException } from 'hono/http-exception'; +import { uploadScreenshotHandler } from '../../../../../shared-helpers/upload-screenshot-handler'; + +const app = new Hono().put( + '/', + zValidator('json', PutScreenshotRequestSchema), + zValidator('param', AssetIdParamsSchema), + async (c) => { + const assetId = c.req.valid('param').id; + const { base64Image } = c.req.valid('json'); + const user = c.get('busterUser'); + + const report = await getReportFileById({ reportId: assetId, userId: user.id }); + if (!report) { + throw new HTTPException(404, { message: 'Report not found' }); + } + + if (!report) { + throw new HTTPException(404, { message: 'Report not found' }); + } + + const permission = await checkPermission({ + userId: user.id, + assetId, + assetType: 'report_file', + requiredRole: 'can_edit', + workspaceSharing: report.workspace_sharing, + organizationId: report.organization_id, + }); + + if (!permission.hasAccess) { + throw new HTTPException(403, { + message: 'You do not have permission to upload a screenshot for this report', + }); + } + + const result: PutScreenshotResponse = await uploadScreenshotHandler({ + assetType: 'report_file', + assetId, + base64Image, + organizationId: report.organization_id, + }); + + return c.json(result); + } +); + +export default app; diff --git a/apps/server/src/api/v2/reports/[id]/screenshot/index.ts b/apps/server/src/api/v2/reports/[id]/screenshot/index.ts new file mode 100644 index 000000000..6a7840d91 --- /dev/null +++ b/apps/server/src/api/v2/reports/[id]/screenshot/index.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono'; +import GET from './GET'; +import PUT from './PUT'; + +const app = new Hono().route('/', GET).route('/', PUT); + +export default app; diff --git a/apps/server/src/shared-helpers/upload-screenshot-handler.ts b/apps/server/src/shared-helpers/upload-screenshot-handler.ts new file mode 100644 index 000000000..e58ae1ecb --- /dev/null +++ b/apps/server/src/shared-helpers/upload-screenshot-handler.ts @@ -0,0 +1,89 @@ +import { getProviderForOrganization } from '@buster/data-source'; +import type { AssetType } from '@buster/server-shared/assets'; +import { AssetTypeSchema } from '@buster/server-shared/assets'; +import { + PutScreenshotRequestSchema, + type PutScreenshotResponse, + PutScreenshotResponseSchema, +} from '@buster/server-shared/screenshots'; +import z from 'zod'; + +export const UploadScreenshotParamsSchema = PutScreenshotRequestSchema.extend({ + assetType: AssetTypeSchema, + assetId: z.string().uuid('Asset ID must be a valid UUID'), + organizationId: z.string().uuid('Organization ID must be a valid UUID'), +}); + +export type UploadScreenshotParams = z.infer; + +function getExtensionFromContentType(contentType: string): string { + switch (contentType) { + case 'image/jpeg': + case 'image/jpg': + return '.jpg'; + case 'image/png': + return '.png'; + case 'image/webp': + return '.webp'; + default: + return '.png'; + } +} + +function parseBase64Image(base64Image: string): { + buffer: Buffer; + contentType: string; + extension: string; +} { + const dataUriPattern = /^data:(?[^;]+);base64,(?.+)$/; + const match = base64Image.match(dataUriPattern); + + const contentType = match?.groups?.mime ?? 'image/png'; + const base64Data = match?.groups?.data ?? base64Image; + + const buffer = Buffer.from(base64Data, 'base64'); + + if (buffer.length === 0) { + throw new Error('Provided image data is empty'); + } + + return { + buffer, + contentType, + extension: getExtensionFromContentType(contentType), + }; +} + +function buildScreenshotKey( + assetType: AssetType, + assetId: string, + extension: string, + organizationId: string +): string { + return `screenshots/${organizationId}/${assetType}-${assetId}${extension}`; +} + +export async function uploadScreenshotHandler( + params: UploadScreenshotParams +): Promise { + const { assetType, assetId, base64Image, organizationId } = + UploadScreenshotParamsSchema.parse(params); + + const { buffer, contentType, extension } = parseBase64Image(base64Image); + + const targetKey = buildScreenshotKey(assetType, assetId, extension, organizationId); + + const provider = await getProviderForOrganization(organizationId); + const result = await provider.upload(targetKey, buffer, { + contentType, + }); + + if (!result.success) { + throw new Error(result.error ?? 'Failed to upload screenshot'); + } + + return PutScreenshotResponseSchema.parse({ + success: true, + bucketKey: result.key, + }); +} diff --git a/packages/data-source/src/storage/storage-factory.test.ts b/packages/data-source/src/storage/storage-factory.test.ts index 641180b02..356ae4789 100644 --- a/packages/data-source/src/storage/storage-factory.test.ts +++ b/packages/data-source/src/storage/storage-factory.test.ts @@ -156,7 +156,7 @@ describe('Storage Factory', () => { expect(createR2Provider).toHaveBeenCalledWith({ provider: 'r2', accountId: 'test-account', - bucket: 'metric-exports', + bucket: 'development', accessKeyId: 'test-key', secretAccessKey: 'test-secret', }); @@ -198,7 +198,7 @@ describe('Storage Factory', () => { process.env.R2_ACCOUNT_ID = 'default-account'; process.env.R2_ACCESS_KEY_ID = 'default-key'; process.env.R2_SECRET_ACCESS_KEY = 'default-secret'; - // Intentionally not setting R2_BUCKET so it uses the default 'metric-exports' + // Intentionally not setting R2_BUCKET so it uses the default 'development' }); afterEach(() => { @@ -296,7 +296,7 @@ describe('Storage Factory', () => { expect(createR2Provider).toHaveBeenCalledWith({ provider: 'r2', accountId: 'default-account', - bucket: 'metric-exports', + bucket: 'development', accessKeyId: 'default-key', secretAccessKey: 'default-secret', }); @@ -314,7 +314,7 @@ describe('Storage Factory', () => { expect(createR2Provider).toHaveBeenCalledWith({ provider: 'r2', accountId: 'default-account', - bucket: 'metric-exports', + bucket: 'development', accessKeyId: 'default-key', secretAccessKey: 'default-secret', }); @@ -329,7 +329,7 @@ describe('Storage Factory', () => { expect(createR2Provider).toHaveBeenCalledWith({ provider: 'r2', accountId: 'default-account', - bucket: 'metric-exports', + bucket: 'development', accessKeyId: 'default-key', secretAccessKey: 'default-secret', }); @@ -349,7 +349,7 @@ describe('Storage Factory', () => { expect(createR2Provider).toHaveBeenCalledWith({ provider: 'r2', accountId: 'default-account', - bucket: 'metric-exports', + bucket: 'development', accessKeyId: 'default-key', secretAccessKey: 'default-secret', }); @@ -374,7 +374,7 @@ describe('Storage Factory', () => { expect(createR2Provider).toHaveBeenCalledWith({ provider: 'r2', accountId: 'default-account', - bucket: 'metric-exports', + bucket: 'development', accessKeyId: 'default-key', secretAccessKey: 'default-secret', }); diff --git a/packages/data-source/src/storage/storage-factory.ts b/packages/data-source/src/storage/storage-factory.ts index 5715a2bbf..3a9bc89f5 100644 --- a/packages/data-source/src/storage/storage-factory.ts +++ b/packages/data-source/src/storage/storage-factory.ts @@ -29,7 +29,7 @@ export function getDefaultProvider(): StorageProvider { const accountId = process.env.R2_ACCOUNT_ID; const accessKeyId = process.env.R2_ACCESS_KEY_ID; const secretAccessKey = process.env.R2_SECRET_ACCESS_KEY; - const bucket = process.env.R2_BUCKET || 'metric-exports'; + const bucket = process.env.R2_BUCKET ?? 'development'; if (!accountId || !accessKeyId || !secretAccessKey) { throw new Error('Default R2 storage credentials not configured'); diff --git a/packages/database/drizzle/0115_glamorous_penance.sql b/packages/database/drizzle/0115_glamorous_penance.sql new file mode 100644 index 000000000..d2231e28d --- /dev/null +++ b/packages/database/drizzle/0115_glamorous_penance.sql @@ -0,0 +1,2 @@ +CREATE INDEX "idx_perm_active_identity_asset" ON "asset_permissions" USING btree ("identity_type","identity_id","asset_type","asset_id") WHERE "asset_permissions"."deleted_at" is null;--> statement-breakpoint +CREATE INDEX "idx_as2_active_by_org" ON "asset_search_v2" USING btree ("organization_id") WHERE "asset_search_v2"."deleted_at" is null; \ No newline at end of file diff --git a/packages/database/drizzle/meta/0115_snapshot.json b/packages/database/drizzle/meta/0115_snapshot.json new file mode 100644 index 000000000..c1616c695 --- /dev/null +++ b/packages/database/drizzle/meta/0115_snapshot.json @@ -0,0 +1,6118 @@ +{ + "id": "59434541-c738-4121-9181-68b46d3e7706", + "prevId": "a4374329-cc1c-4957-aefb-c876e94ea876", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owner_id": { + "name": "owner_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "api_keys_organization_id_fkey": { + "name": "api_keys_organization_id_fkey", + "tableFrom": "api_keys", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "api_keys_owner_id_fkey": { + "name": "api_keys_owner_id_fkey", + "tableFrom": "api_keys", + "tableTo": "users", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_key": { + "name": "api_keys_key_key", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.asset_permissions": { + "name": "asset_permissions", + "schema": "", + "columns": { + "identity_id": { + "name": "identity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "identity_type": { + "name": "identity_type", + "type": "identity_type_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "asset_id": { + "name": "asset_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "asset_type": { + "name": "asset_type", + "type": "asset_type_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "asset_permission_role_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "idx_perm_active_asset_identity": { + "name": "idx_perm_active_asset_identity", + "columns": [ + { + "expression": "asset_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "asset_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "identity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "identity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"asset_permissions\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_perm_active_identity_asset": { + "name": "idx_perm_active_identity_asset", + "columns": [ + { + "expression": "identity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "identity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "asset_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "asset_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"asset_permissions\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "asset_permissions_created_by_fkey": { + "name": "asset_permissions_created_by_fkey", + "tableFrom": "asset_permissions", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "asset_permissions_updated_by_fkey": { + "name": "asset_permissions_updated_by_fkey", + "tableFrom": "asset_permissions", + "tableTo": "users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "asset_permissions_pkey": { + "name": "asset_permissions_pkey", + "columns": [ + "identity_id", + "identity_type", + "asset_id", + "asset_type" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.asset_search": { + "name": "asset_search", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "asset_id": { + "name": "asset_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "asset_type": { + "name": "asset_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "asset_search_asset_id_asset_type_idx": { + "name": "asset_search_asset_id_asset_type_idx", + "columns": [ + { + "expression": "asset_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "asset_type", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pgroonga_content_index": { + "name": "pgroonga_content_index", + "columns": [ + { + "expression": "content", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "pgroonga_text_full_text_search_ops_v2" + } + ], + "isUnique": false, + "concurrently": false, + "method": "pgroonga", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.asset_search_v2": { + "name": "asset_search_v2", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "asset_type": { + "name": "asset_type", + "type": "asset_type_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "asset_id": { + "name": "asset_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "additional_text": { + "name": "additional_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pgroonga_search_title_description_index": { + "name": "pgroonga_search_title_description_index", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "pgroonga_text_full_text_search_ops_v2" + }, + { + "expression": "additional_text", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "pgroonga_text_full_text_search_ops_v2" + } + ], + "isUnique": false, + "concurrently": false, + "method": "pgroonga", + "with": {} + }, + "idx_as2_active_by_asset": { + "name": "idx_as2_active_by_asset", + "columns": [ + { + "expression": "asset_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "asset_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"asset_search_v2\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_as2_active_by_org": { + "name": "idx_as2_active_by_org", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"asset_search_v2\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "asset_search_v2_organization_id_fkey": { + "name": "asset_search_v2_organization_id_fkey", + "tableFrom": "asset_search_v2", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "asset_search_v2_asset_type_asset_id_unique": { + "name": "asset_search_v2_asset_type_asset_id_unique", + "nullsNotDistinct": false, + "columns": [ + "asset_id", + "asset_type" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chats": { + "name": "chats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "publicly_accessible": { + "name": "publicly_accessible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "publicly_enabled_by": { + "name": "publicly_enabled_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "public_expiry_date": { + "name": "public_expiry_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "public_password": { + "name": "public_password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "most_recent_file_id": { + "name": "most_recent_file_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "most_recent_file_type": { + "name": "most_recent_file_type", + "type": "asset_type_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "most_recent_version_number": { + "name": "most_recent_version_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "slack_chat_authorization": { + "name": "slack_chat_authorization", + "type": "slack_chat_authorization_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "slack_thread_ts": { + "name": "slack_thread_ts", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slack_channel_id": { + "name": "slack_channel_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_sharing": { + "name": "workspace_sharing", + "type": "workspace_sharing_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "workspace_sharing_enabled_by": { + "name": "workspace_sharing_enabled_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workspace_sharing_enabled_at": { + "name": "workspace_sharing_enabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "chats_created_at_idx": { + "name": "chats_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chats_created_by_idx": { + "name": "chats_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chats_organization_id_idx": { + "name": "chats_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_chats_most_recent_file_id": { + "name": "idx_chats_most_recent_file_id", + "columns": [ + { + "expression": "most_recent_file_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_chats_most_recent_file_type": { + "name": "idx_chats_most_recent_file_type", + "columns": [ + { + "expression": "most_recent_file_type", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chats_organization_id_fkey": { + "name": "chats_organization_id_fkey", + "tableFrom": "chats", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chats_created_by_fkey": { + "name": "chats_created_by_fkey", + "tableFrom": "chats", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "chats_updated_by_fkey": { + "name": "chats_updated_by_fkey", + "tableFrom": "chats", + "tableTo": "users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "chats_publicly_enabled_by_fkey": { + "name": "chats_publicly_enabled_by_fkey", + "tableFrom": "chats", + "tableTo": "users", + "columnsFrom": [ + "publicly_enabled_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "chats_workspace_sharing_enabled_by_fkey": { + "name": "chats_workspace_sharing_enabled_by_fkey", + "tableFrom": "chats", + "tableTo": "users", + "columnsFrom": [ + "workspace_sharing_enabled_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.collections": { + "name": "collections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workspace_sharing": { + "name": "workspace_sharing", + "type": "workspace_sharing_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "workspace_sharing_enabled_by": { + "name": "workspace_sharing_enabled_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workspace_sharing_enabled_at": { + "name": "workspace_sharing_enabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "collections_organization_id_fkey": { + "name": "collections_organization_id_fkey", + "tableFrom": "collections", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "collections_created_by_fkey": { + "name": "collections_created_by_fkey", + "tableFrom": "collections", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "collections_updated_by_fkey": { + "name": "collections_updated_by_fkey", + "tableFrom": "collections", + "tableTo": "users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "collections_workspace_sharing_enabled_by_fkey": { + "name": "collections_workspace_sharing_enabled_by_fkey", + "tableFrom": "collections", + "tableTo": "users", + "columnsFrom": [ + "workspace_sharing_enabled_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.collections_to_assets": { + "name": "collections_to_assets", + "schema": "", + "columns": { + "collection_id": { + "name": "collection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "asset_id": { + "name": "asset_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "asset_type": { + "name": "asset_type", + "type": "asset_type_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "idx_cta_active_by_asset": { + "name": "idx_cta_active_by_asset", + "columns": [ + { + "expression": "asset_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "asset_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "collection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"collections_to_assets\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "collections_to_assets_created_by_fkey": { + "name": "collections_to_assets_created_by_fkey", + "tableFrom": "collections_to_assets", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "collections_to_assets_updated_by_fkey": { + "name": "collections_to_assets_updated_by_fkey", + "tableFrom": "collections_to_assets", + "tableTo": "users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "collections_to_assets_pkey": { + "name": "collections_to_assets_pkey", + "columns": [ + "collection_id", + "asset_id", + "asset_type" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dashboard_files": { + "name": "dashboard_files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "filter": { + "name": "filter", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "publicly_accessible": { + "name": "publicly_accessible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "publicly_enabled_by": { + "name": "publicly_enabled_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "public_expiry_date": { + "name": "public_expiry_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "version_history": { + "name": "version_history", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "public_password": { + "name": "public_password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_sharing": { + "name": "workspace_sharing", + "type": "workspace_sharing_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "workspace_sharing_enabled_by": { + "name": "workspace_sharing_enabled_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workspace_sharing_enabled_at": { + "name": "workspace_sharing_enabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "dashboard_files_created_by_idx": { + "name": "dashboard_files_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dashboard_files_deleted_at_idx": { + "name": "dashboard_files_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dashboard_files_organization_id_idx": { + "name": "dashboard_files_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "dashboard_files_created_by_fkey": { + "name": "dashboard_files_created_by_fkey", + "tableFrom": "dashboard_files", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "dashboard_files_publicly_enabled_by_fkey": { + "name": "dashboard_files_publicly_enabled_by_fkey", + "tableFrom": "dashboard_files", + "tableTo": "users", + "columnsFrom": [ + "publicly_enabled_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "dashboard_files_workspace_sharing_enabled_by_fkey": { + "name": "dashboard_files_workspace_sharing_enabled_by_fkey", + "tableFrom": "dashboard_files", + "tableTo": "users", + "columnsFrom": [ + "workspace_sharing_enabled_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.data_sources": { + "name": "data_sources", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret_id": { + "name": "secret_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "onboarding_status": { + "name": "onboarding_status", + "type": "data_source_onboarding_status_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'notStarted'" + }, + "onboarding_error": { + "name": "onboarding_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "varchar", + "primaryKey": false, + "notNull": true, + "default": "'dev'" + } + }, + "indexes": {}, + "foreignKeys": { + "data_sources_organization_id_fkey": { + "name": "data_sources_organization_id_fkey", + "tableFrom": "data_sources", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "data_sources_created_by_fkey": { + "name": "data_sources_created_by_fkey", + "tableFrom": "data_sources", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "data_sources_updated_by_fkey": { + "name": "data_sources_updated_by_fkey", + "tableFrom": "data_sources", + "tableTo": "users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "data_sources_name_organization_id_env_key": { + "name": "data_sources_name_organization_id_env_key", + "nullsNotDistinct": false, + "columns": [ + "name", + "organization_id", + "env" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dataset_groups": { + "name": "dataset_groups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "dataset_groups_deleted_at_idx": { + "name": "dataset_groups_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dataset_groups_organization_id_idx": { + "name": "dataset_groups_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "dataset_groups_organization_id_fkey": { + "name": "dataset_groups_organization_id_fkey", + "tableFrom": "dataset_groups", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "dataset_groups_policy": { + "name": "dataset_groups_policy", + "as": "PERMISSIVE", + "for": "ALL", + "to": [ + "authenticated" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dataset_groups_permissions": { + "name": "dataset_groups_permissions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "dataset_group_id": { + "name": "dataset_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "permission_id": { + "name": "permission_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "permission_type": { + "name": "permission_type", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "dataset_groups_permissions_dataset_group_id_idx": { + "name": "dataset_groups_permissions_dataset_group_id_idx", + "columns": [ + { + "expression": "dataset_group_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dataset_groups_permissions_organization_id_idx": { + "name": "dataset_groups_permissions_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dataset_groups_permissions_permission_id_idx": { + "name": "dataset_groups_permissions_permission_id_idx", + "columns": [ + { + "expression": "permission_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "dataset_groups_permissions_dataset_group_id_fkey": { + "name": "dataset_groups_permissions_dataset_group_id_fkey", + "tableFrom": "dataset_groups_permissions", + "tableTo": "dataset_groups", + "columnsFrom": [ + "dataset_group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "dataset_groups_permissions_organization_id_fkey": { + "name": "dataset_groups_permissions_organization_id_fkey", + "tableFrom": "dataset_groups_permissions", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_dataset_group_permission": { + "name": "unique_dataset_group_permission", + "nullsNotDistinct": false, + "columns": [ + "dataset_group_id", + "permission_id", + "permission_type" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dataset_permissions": { + "name": "dataset_permissions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "dataset_id": { + "name": "dataset_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "permission_id": { + "name": "permission_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "permission_type": { + "name": "permission_type", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "dataset_permissions_dataset_id_idx": { + "name": "dataset_permissions_dataset_id_idx", + "columns": [ + { + "expression": "dataset_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dataset_permissions_deleted_at_idx": { + "name": "dataset_permissions_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dataset_permissions_organization_id_idx": { + "name": "dataset_permissions_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dataset_permissions_permission_lookup_idx": { + "name": "dataset_permissions_permission_lookup_idx", + "columns": [ + { + "expression": "permission_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "permission_type", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "dataset_permissions_organization_id_fkey": { + "name": "dataset_permissions_organization_id_fkey", + "tableFrom": "dataset_permissions", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "dataset_permissions_dataset_id_fkey": { + "name": "dataset_permissions_dataset_id_fkey", + "tableFrom": "dataset_permissions", + "tableTo": "datasets", + "columnsFrom": [ + "dataset_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "dataset_permissions_dataset_id_permission_id_permission_typ_key": { + "name": "dataset_permissions_dataset_id_permission_id_permission_typ_key", + "nullsNotDistinct": false, + "columns": [ + "dataset_id", + "permission_id", + "permission_type" + ] + } + }, + "policies": { + "dataset_permissions_policy": { + "name": "dataset_permissions_policy", + "as": "PERMISSIVE", + "for": "ALL", + "to": [ + "authenticated" + ], + "using": "true" + } + }, + "checkConstraints": { + "dataset_permissions_permission_type_check": { + "name": "dataset_permissions_permission_type_check", + "value": "(permission_type)::text = ANY ((ARRAY['user'::character varying, 'dataset_group'::character varying, 'permission_group'::character varying])::text[])" + } + }, + "isRLSEnabled": false + }, + "public.datasets": { + "name": "datasets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "database_name": { + "name": "database_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "when_to_use": { + "name": "when_to_use", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "when_not_to_use": { + "name": "when_not_to_use", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "dataset_type_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "definition": { + "name": "definition", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema": { + "name": "schema", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "imported": { + "name": "imported", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "data_source_id": { + "name": "data_source_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "yml_file": { + "name": "yml_file", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "database_identifier": { + "name": "database_identifier", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "datasets_data_source_id_fkey": { + "name": "datasets_data_source_id_fkey", + "tableFrom": "datasets", + "tableTo": "data_sources", + "columnsFrom": [ + "data_source_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "datasets_organization_id_fkey": { + "name": "datasets_organization_id_fkey", + "tableFrom": "datasets", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "datasets_created_by_fkey": { + "name": "datasets_created_by_fkey", + "tableFrom": "datasets", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "datasets_updated_by_fkey": { + "name": "datasets_updated_by_fkey", + "tableFrom": "datasets", + "tableTo": "users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "datasets_name_schema_database_identifier_data_source_id_key": { + "name": "datasets_name_schema_database_identifier_data_source_id_key", + "nullsNotDistinct": false, + "columns": [ + "name", + "schema", + "database_identifier", + "data_source_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.datasets_to_dataset_groups": { + "name": "datasets_to_dataset_groups", + "schema": "", + "columns": { + "dataset_id": { + "name": "dataset_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "dataset_group_id": { + "name": "dataset_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "datasets_to_dataset_groups_dataset_group_id_idx": { + "name": "datasets_to_dataset_groups_dataset_group_id_idx", + "columns": [ + { + "expression": "dataset_group_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "datasets_to_dataset_groups_dataset_id_fkey": { + "name": "datasets_to_dataset_groups_dataset_id_fkey", + "tableFrom": "datasets_to_dataset_groups", + "tableTo": "datasets", + "columnsFrom": [ + "dataset_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "datasets_to_dataset_groups_dataset_group_id_fkey": { + "name": "datasets_to_dataset_groups_dataset_group_id_fkey", + "tableFrom": "datasets_to_dataset_groups", + "tableTo": "dataset_groups", + "columnsFrom": [ + "dataset_group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "datasets_to_dataset_groups_pkey": { + "name": "datasets_to_dataset_groups_pkey", + "columns": [ + "dataset_id", + "dataset_group_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": { + "datasets_to_dataset_groups_policy": { + "name": "datasets_to_dataset_groups_policy", + "as": "PERMISSIVE", + "for": "ALL", + "to": [ + "authenticated" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.datasets_to_permission_groups": { + "name": "datasets_to_permission_groups", + "schema": "", + "columns": { + "dataset_id": { + "name": "dataset_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "permission_group_id": { + "name": "permission_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "datasets_to_permission_groups_dataset_id_fkey": { + "name": "datasets_to_permission_groups_dataset_id_fkey", + "tableFrom": "datasets_to_permission_groups", + "tableTo": "datasets", + "columnsFrom": [ + "dataset_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "datasets_to_permission_groups_permission_group_id_fkey": { + "name": "datasets_to_permission_groups_permission_group_id_fkey", + "tableFrom": "datasets_to_permission_groups", + "tableTo": "permission_groups", + "columnsFrom": [ + "permission_group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "datasets_to_permission_groups_pkey": { + "name": "datasets_to_permission_groups_pkey", + "columns": [ + "dataset_id", + "permission_group_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": { + "datasets_to_permission_groups_policy": { + "name": "datasets_to_permission_groups_policy", + "as": "PERMISSIVE", + "for": "ALL", + "to": [ + "authenticated" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.docs": { + "name": "docs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "docs_type_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'normal'" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "docs_organization_id_fkey": { + "name": "docs_organization_id_fkey", + "tableFrom": "docs", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "docs_name_organization_id_key": { + "name": "docs_name_organization_id_key", + "nullsNotDistinct": false, + "columns": [ + "name", + "organization_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github_integrations": { + "name": "github_integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "installation_id": { + "name": "installation_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "app_id": { + "name": "app_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "github_org_id": { + "name": "github_org_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "github_org_name": { + "name": "github_org_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "token_vault_key": { + "name": "token_vault_key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "webhook_secret_vault_key": { + "name": "webhook_secret_vault_key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "repository_permissions": { + "name": "repository_permissions", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "status": { + "name": "status", + "type": "github_integration_status_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "installed_at": { + "name": "installed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_github_integrations_org_id": { + "name": "idx_github_integrations_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_github_integrations_installation_id": { + "name": "idx_github_integrations_installation_id", + "columns": [ + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_github_integrations_github_org_id": { + "name": "idx_github_integrations_github_org_id", + "columns": [ + { + "expression": "github_org_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "github_integrations_organization_id_fkey": { + "name": "github_integrations_organization_id_fkey", + "tableFrom": "github_integrations", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "github_integrations_user_id_fkey": { + "name": "github_integrations_user_id_fkey", + "tableFrom": "github_integrations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "github_integrations_token_vault_key_unique": { + "name": "github_integrations_token_vault_key_unique", + "nullsNotDistinct": false, + "columns": [ + "token_vault_key" + ] + }, + "github_integrations_org_installation_key": { + "name": "github_integrations_org_installation_key", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "installation_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.logs_write_back_configs": { + "name": "logs_write_back_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "data_source_id": { + "name": "data_source_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "database": { + "name": "database", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "schema": { + "name": "schema", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "table_name": { + "name": "table_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "default": "'buster_query_logs'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "logs_write_back_configs_org_unique": { + "name": "logs_write_back_configs_org_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"logs_write_back_configs\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_logs_write_back_configs_org_id": { + "name": "idx_logs_write_back_configs_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_logs_write_back_configs_data_source_id": { + "name": "idx_logs_write_back_configs_data_source_id", + "columns": [ + { + "expression": "data_source_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_logs_write_back_configs_deleted_at": { + "name": "idx_logs_write_back_configs_deleted_at", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "logs_write_back_configs_organization_id_fkey": { + "name": "logs_write_back_configs_organization_id_fkey", + "tableFrom": "logs_write_back_configs", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "logs_write_back_configs_data_source_id_fkey": { + "name": "logs_write_back_configs_data_source_id_fkey", + "tableFrom": "logs_write_back_configs", + "tableTo": "data_sources", + "columnsFrom": [ + "data_source_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "request_message": { + "name": "request_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "response_messages": { + "name": "response_messages", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "message_analysis_mode": { + "name": "message_analysis_mode", + "type": "message_analysis_mode_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'auto'" + }, + "reasoning": { + "name": "reasoning", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "raw_llm_messages": { + "name": "raw_llm_messages", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "final_reasoning_message": { + "name": "final_reasoning_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_completed": { + "name": "is_completed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "post_processing_message": { + "name": "post_processing_message", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "trigger_run_id": { + "name": "trigger_run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "messages_chat_id_idx": { + "name": "messages_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_created_at_idx": { + "name": "messages_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_created_by_idx": { + "name": "messages_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_deleted_at_idx": { + "name": "messages_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_raw_llm_messages_gin_idx": { + "name": "messages_raw_llm_messages_gin_idx", + "columns": [ + { + "expression": "raw_llm_messages", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "jsonb_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "messages_response_messages_gin_idx": { + "name": "messages_response_messages_gin_idx", + "columns": [ + { + "expression": "response_messages", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "jsonb_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "messages_reasoning_gin_idx": { + "name": "messages_reasoning_gin_idx", + "columns": [ + { + "expression": "reasoning", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "jsonb_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "messages_id_deleted_at_idx": { + "name": "messages_id_deleted_at_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "messages_chat_id_fkey": { + "name": "messages_chat_id_fkey", + "tableFrom": "messages", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_created_by_fkey": { + "name": "messages_created_by_fkey", + "tableFrom": "messages", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages_to_files": { + "name": "messages_to_files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "is_duplicate": { + "name": "is_duplicate", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "version_number": { + "name": "version_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + } + }, + "indexes": { + "messages_files_file_id_idx": { + "name": "messages_files_file_id_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_files_message_id_idx": { + "name": "messages_files_message_id_idx", + "columns": [ + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_mtf_active_by_file": { + "name": "idx_mtf_active_by_file", + "columns": [ + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"messages_to_files\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "messages_to_files_message_id_fkey": { + "name": "messages_to_files_message_id_fkey", + "tableFrom": "messages_to_files", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "messages_to_files_message_id_file_id_key": { + "name": "messages_to_files_message_id_file_id_key", + "nullsNotDistinct": false, + "columns": [ + "message_id", + "file_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages_to_slack_messages": { + "name": "messages_to_slack_messages", + "schema": "", + "columns": { + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "slack_message_id": { + "name": "slack_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "messages_to_slack_messages_message_id_idx": { + "name": "messages_to_slack_messages_message_id_idx", + "columns": [ + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_to_slack_messages_slack_message_id_idx": { + "name": "messages_to_slack_messages_slack_message_id_idx", + "columns": [ + { + "expression": "slack_message_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "messages_to_slack_messages_message_id_fkey": { + "name": "messages_to_slack_messages_message_id_fkey", + "tableFrom": "messages_to_slack_messages", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "messages_to_slack_messages_slack_message_id_fkey": { + "name": "messages_to_slack_messages_slack_message_id_fkey", + "tableFrom": "messages_to_slack_messages", + "tableTo": "slack_message_tracking", + "columnsFrom": [ + "slack_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "messages_to_slack_messages_pkey": { + "name": "messages_to_slack_messages_pkey", + "columns": [ + "message_id", + "slack_message_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.metric_files": { + "name": "metric_files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "verification": { + "name": "verification", + "type": "verification_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'notRequested'" + }, + "evaluation_obj": { + "name": "evaluation_obj", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "evaluation_summary": { + "name": "evaluation_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "evaluation_score": { + "name": "evaluation_score", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "publicly_accessible": { + "name": "publicly_accessible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "publicly_enabled_by": { + "name": "publicly_enabled_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "public_expiry_date": { + "name": "public_expiry_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "version_history": { + "name": "version_history", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "data_metadata": { + "name": "data_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "public_password": { + "name": "public_password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "data_source_id": { + "name": "data_source_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workspace_sharing": { + "name": "workspace_sharing", + "type": "workspace_sharing_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "workspace_sharing_enabled_by": { + "name": "workspace_sharing_enabled_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workspace_sharing_enabled_at": { + "name": "workspace_sharing_enabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "metric_files_created_by_idx": { + "name": "metric_files_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "metric_files_data_metadata_idx": { + "name": "metric_files_data_metadata_idx", + "columns": [ + { + "expression": "data_metadata", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "jsonb_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "metric_files_deleted_at_idx": { + "name": "metric_files_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "metric_files_organization_id_idx": { + "name": "metric_files_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "metric_files_created_by_fkey": { + "name": "metric_files_created_by_fkey", + "tableFrom": "metric_files", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "metric_files_publicly_enabled_by_fkey": { + "name": "metric_files_publicly_enabled_by_fkey", + "tableFrom": "metric_files", + "tableTo": "users", + "columnsFrom": [ + "publicly_enabled_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "fk_data_source": { + "name": "fk_data_source", + "tableFrom": "metric_files", + "tableTo": "data_sources", + "columnsFrom": [ + "data_source_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "metric_files_workspace_sharing_enabled_by_fkey": { + "name": "metric_files_workspace_sharing_enabled_by_fkey", + "tableFrom": "metric_files", + "tableTo": "users", + "columnsFrom": [ + "workspace_sharing_enabled_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.metric_files_to_dashboard_files": { + "name": "metric_files_to_dashboard_files", + "schema": "", + "columns": { + "metric_file_id": { + "name": "metric_file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "dashboard_file_id": { + "name": "dashboard_file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "metric_files_to_dashboard_files_dashboard_id_idx": { + "name": "metric_files_to_dashboard_files_dashboard_id_idx", + "columns": [ + { + "expression": "dashboard_file_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "metric_files_to_dashboard_files_deleted_at_idx": { + "name": "metric_files_to_dashboard_files_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "metric_files_to_dashboard_files_metric_id_idx": { + "name": "metric_files_to_dashboard_files_metric_id_idx", + "columns": [ + { + "expression": "metric_file_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "metric_files_to_dashboard_files_metric_file_id_fkey": { + "name": "metric_files_to_dashboard_files_metric_file_id_fkey", + "tableFrom": "metric_files_to_dashboard_files", + "tableTo": "metric_files", + "columnsFrom": [ + "metric_file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "metric_files_to_dashboard_files_dashboard_file_id_fkey": { + "name": "metric_files_to_dashboard_files_dashboard_file_id_fkey", + "tableFrom": "metric_files_to_dashboard_files", + "tableTo": "dashboard_files", + "columnsFrom": [ + "dashboard_file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "metric_files_to_dashboard_files_created_by_fkey": { + "name": "metric_files_to_dashboard_files_created_by_fkey", + "tableFrom": "metric_files_to_dashboard_files", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "metric_files_to_dashboard_files_pkey": { + "name": "metric_files_to_dashboard_files_pkey", + "columns": [ + "metric_file_id", + "dashboard_file_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.metric_files_to_datasets": { + "name": "metric_files_to_datasets", + "schema": "", + "columns": { + "metric_file_id": { + "name": "metric_file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "dataset_id": { + "name": "dataset_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "metric_version_number": { + "name": "metric_version_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "fk_metric_file": { + "name": "fk_metric_file", + "tableFrom": "metric_files_to_datasets", + "tableTo": "metric_files", + "columnsFrom": [ + "metric_file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "fk_dataset": { + "name": "fk_dataset", + "tableFrom": "metric_files_to_datasets", + "tableTo": "datasets", + "columnsFrom": [ + "dataset_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "metric_files_to_datasets_pkey": { + "name": "metric_files_to_datasets_pkey", + "columns": [ + "metric_file_id", + "dataset_id", + "metric_version_number" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.metric_files_to_report_files": { + "name": "metric_files_to_report_files", + "schema": "", + "columns": { + "metric_file_id": { + "name": "metric_file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "report_file_id": { + "name": "report_file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "metric_files_to_report_files_report_id_idx": { + "name": "metric_files_to_report_files_report_id_idx", + "columns": [ + { + "expression": "report_file_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "metric_files_to_report_files_deleted_at_idx": { + "name": "metric_files_to_report_files_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "metric_files_to_report_files_metric_id_idx": { + "name": "metric_files_to_report_files_metric_id_idx", + "columns": [ + { + "expression": "metric_file_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "metric_files_to_report_files_metric_file_id_fkey": { + "name": "metric_files_to_report_files_metric_file_id_fkey", + "tableFrom": "metric_files_to_report_files", + "tableTo": "metric_files", + "columnsFrom": [ + "metric_file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "metric_files_to_report_files_report_file_id_fkey": { + "name": "metric_files_to_report_files_report_file_id_fkey", + "tableFrom": "metric_files_to_report_files", + "tableTo": "report_files", + "columnsFrom": [ + "report_file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "metric_files_to_report_files_created_by_fkey": { + "name": "metric_files_to_report_files_created_by_fkey", + "tableFrom": "metric_files_to_report_files", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "metric_files_to_report_files_pkey": { + "name": "metric_files_to_report_files_pkey", + "columns": [ + "metric_file_id", + "report_file_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organizations": { + "name": "organizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "payment_required": { + "name": "payment_required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "domains": { + "name": "domains", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "restrict_new_user_invitations": { + "name": "restrict_new_user_invitations", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "default_role": { + "name": "default_role", + "type": "user_organization_role_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'restricted_querier'" + }, + "organization_color_palettes": { + "name": "organization_color_palettes", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"selectedId\": null, \"palettes\": [], \"selectedDictionaryPalette\": null}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organizations_name_key": { + "name": "organizations_name_key", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permission_groups": { + "name": "permission_groups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "permission_groups_organization_id_fkey": { + "name": "permission_groups_organization_id_fkey", + "tableFrom": "permission_groups", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_groups_created_by_fkey": { + "name": "permission_groups_created_by_fkey", + "tableFrom": "permission_groups", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "permission_groups_updated_by_fkey": { + "name": "permission_groups_updated_by_fkey", + "tableFrom": "permission_groups", + "tableTo": "users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permission_groups_to_identities": { + "name": "permission_groups_to_identities", + "schema": "", + "columns": { + "permission_group_id": { + "name": "permission_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "identity_id": { + "name": "identity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "identity_type": { + "name": "identity_type", + "type": "identity_type_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "permission_groups_to_identities_created_by_fkey": { + "name": "permission_groups_to_identities_created_by_fkey", + "tableFrom": "permission_groups_to_identities", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "permission_groups_to_identities_updated_by_fkey": { + "name": "permission_groups_to_identities_updated_by_fkey", + "tableFrom": "permission_groups_to_identities", + "tableTo": "users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "permission_groups_to_identities_pkey": { + "name": "permission_groups_to_identities_pkey", + "columns": [ + "permission_group_id", + "identity_id", + "identity_type" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permission_groups_to_users": { + "name": "permission_groups_to_users", + "schema": "", + "columns": { + "permission_group_id": { + "name": "permission_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "permission_groups_to_users_user_id_idx": { + "name": "permission_groups_to_users_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permission_groups_to_users_permission_group_id_fkey": { + "name": "permission_groups_to_users_permission_group_id_fkey", + "tableFrom": "permission_groups_to_users", + "tableTo": "permission_groups", + "columnsFrom": [ + "permission_group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_groups_to_users_user_id_fkey": { + "name": "permission_groups_to_users_user_id_fkey", + "tableFrom": "permission_groups_to_users", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "permission_groups_to_users_pkey": { + "name": "permission_groups_to_users_pkey", + "columns": [ + "permission_group_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": { + "permission_groups_to_users_policy": { + "name": "permission_groups_to_users_policy", + "as": "PERMISSIVE", + "for": "ALL", + "to": [ + "authenticated" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.report_files": { + "name": "report_files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "publicly_accessible": { + "name": "publicly_accessible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "publicly_enabled_by": { + "name": "publicly_enabled_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "public_expiry_date": { + "name": "public_expiry_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "version_history": { + "name": "version_history", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "public_password": { + "name": "public_password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_sharing": { + "name": "workspace_sharing", + "type": "workspace_sharing_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "workspace_sharing_enabled_by": { + "name": "workspace_sharing_enabled_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workspace_sharing_enabled_at": { + "name": "workspace_sharing_enabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "report_files_created_by_idx": { + "name": "report_files_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "report_files_deleted_at_idx": { + "name": "report_files_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "report_files_organization_id_idx": { + "name": "report_files_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_files_created_by_fkey": { + "name": "report_files_created_by_fkey", + "tableFrom": "report_files", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "report_files_publicly_enabled_by_fkey": { + "name": "report_files_publicly_enabled_by_fkey", + "tableFrom": "report_files", + "tableTo": "users", + "columnsFrom": [ + "publicly_enabled_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "report_files_workspace_sharing_enabled_by_fkey": { + "name": "report_files_workspace_sharing_enabled_by_fkey", + "tableFrom": "report_files", + "tableTo": "users", + "columnsFrom": [ + "workspace_sharing_enabled_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "report_files_organization_id_fkey": { + "name": "report_files_organization_id_fkey", + "tableFrom": "report_files", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.s3_integrations": { + "name": "s3_integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "storage_provider_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_s3_integrations_organization_id": { + "name": "idx_s3_integrations_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_s3_integrations_deleted_at": { + "name": "idx_s3_integrations_deleted_at", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "s3_integrations_organization_id_fkey": { + "name": "s3_integrations_organization_id_fkey", + "tableFrom": "s3_integrations", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.shortcuts": { + "name": "shortcuts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "instructions": { + "name": "instructions", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "share_with_workspace": { + "name": "share_with_workspace", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "shortcuts_org_user_idx": { + "name": "shortcuts_org_user_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "shortcuts_name_idx": { + "name": "shortcuts_name_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "shortcuts_workspace_unique": { + "name": "shortcuts_workspace_unique", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"shortcuts\".\"share_with_workspace\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "shortcuts_created_by_fkey": { + "name": "shortcuts_created_by_fkey", + "tableFrom": "shortcuts", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "shortcuts_updated_by_fkey": { + "name": "shortcuts_updated_by_fkey", + "tableFrom": "shortcuts", + "tableTo": "users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "shortcuts_organization_id_fkey": { + "name": "shortcuts_organization_id_fkey", + "tableFrom": "shortcuts", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "shortcuts_personal_unique": { + "name": "shortcuts_personal_unique", + "nullsNotDistinct": false, + "columns": [ + "name", + "organization_id", + "created_by" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack_integrations": { + "name": "slack_integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "oauth_state": { + "name": "oauth_state", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "oauth_expires_at": { + "name": "oauth_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "oauth_metadata": { + "name": "oauth_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "team_id": { + "name": "team_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "team_name": { + "name": "team_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "team_domain": { + "name": "team_domain", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "enterprise_id": { + "name": "enterprise_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "bot_user_id": { + "name": "bot_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_vault_key": { + "name": "token_vault_key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "installed_by_slack_user_id": { + "name": "installed_by_slack_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "installed_at": { + "name": "installed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "slack_integration_status_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "default_channel": { + "name": "default_channel", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "default_sharing_permissions": { + "name": "default_sharing_permissions", + "type": "slack_sharing_permission_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'shareWithChannel'" + } + }, + "indexes": { + "idx_slack_integrations_org_id": { + "name": "idx_slack_integrations_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_integrations_team_id": { + "name": "idx_slack_integrations_team_id", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_integrations_oauth_state": { + "name": "idx_slack_integrations_oauth_state", + "columns": [ + { + "expression": "oauth_state", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_integrations_oauth_expires": { + "name": "idx_slack_integrations_oauth_expires", + "columns": [ + { + "expression": "oauth_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "slack_integrations_organization_id_fkey": { + "name": "slack_integrations_organization_id_fkey", + "tableFrom": "slack_integrations", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "slack_integrations_user_id_fkey": { + "name": "slack_integrations_user_id_fkey", + "tableFrom": "slack_integrations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "slack_integrations_oauth_state_unique": { + "name": "slack_integrations_oauth_state_unique", + "nullsNotDistinct": false, + "columns": [ + "oauth_state" + ] + }, + "slack_integrations_token_vault_key_unique": { + "name": "slack_integrations_token_vault_key_unique", + "nullsNotDistinct": false, + "columns": [ + "token_vault_key" + ] + }, + "slack_integrations_org_team_key": { + "name": "slack_integrations_org_team_key", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "team_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "slack_integrations_status_check": { + "name": "slack_integrations_status_check", + "value": "(status = 'pending' AND oauth_state IS NOT NULL) OR (status != 'pending' AND team_id IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.slack_message_tracking": { + "name": "slack_message_tracking", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "internal_message_id": { + "name": "internal_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "slack_channel_id": { + "name": "slack_channel_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "slack_message_ts": { + "name": "slack_message_ts", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "slack_thread_ts": { + "name": "slack_thread_ts", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "message_type": { + "name": "message_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sender_info": { + "name": "sender_info", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_message_tracking_integration": { + "name": "idx_message_tracking_integration", + "columns": [ + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_tracking_channel": { + "name": "idx_message_tracking_channel", + "columns": [ + { + "expression": "slack_channel_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_tracking_thread": { + "name": "idx_message_tracking_thread", + "columns": [ + { + "expression": "slack_thread_ts", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "slack_message_tracking_integration_id_fkey": { + "name": "slack_message_tracking_integration_id_fkey", + "tableFrom": "slack_message_tracking", + "tableTo": "slack_integrations", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "slack_message_tracking_internal_message_id_unique": { + "name": "slack_message_tracking_internal_message_id_unique", + "nullsNotDistinct": false, + "columns": [ + "internal_message_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams": { + "name": "teams", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sharing_setting": { + "name": "sharing_setting", + "type": "sharing_setting_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "edit_sql": { + "name": "edit_sql", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "upload_csv": { + "name": "upload_csv", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "export_assets": { + "name": "export_assets", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "email_slack_enabled": { + "name": "email_slack_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "teams_organization_id_fkey": { + "name": "teams_organization_id_fkey", + "tableFrom": "teams", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "teams_created_by_fkey": { + "name": "teams_created_by_fkey", + "tableFrom": "teams", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "teams_name_key": { + "name": "teams_name_key", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams_to_users": { + "name": "teams_to_users", + "schema": "", + "columns": { + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "team_role_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "teams_to_users_team_id_fkey": { + "name": "teams_to_users_team_id_fkey", + "tableFrom": "teams_to_users", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "teams_to_users_user_id_fkey": { + "name": "teams_to_users_user_id_fkey", + "tableFrom": "teams_to_users", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "teams_to_users_pkey": { + "name": "teams_to_users_pkey", + "columns": [ + "team_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_favorites": { + "name": "user_favorites", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "asset_id": { + "name": "asset_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "asset_type": { + "name": "asset_type", + "type": "asset_type_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "order_index": { + "name": "order_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_favorites_user_id_fkey": { + "name": "user_favorites_user_id_fkey", + "tableFrom": "user_favorites", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "user_favorites_pkey": { + "name": "user_favorites_pkey", + "columns": [ + "user_id", + "asset_id", + "asset_type" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "attributes": { + "name": "attributes", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "suggested_prompts": { + "name": "suggested_prompts", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\n \"suggestedPrompts\": {\n \"report\": [\n \"provide a trend analysis of quarterly profits\",\n \"evaluate product performance across regions\"\n ],\n \"dashboard\": [\n \"create a sales performance dashboard\",\n \"design a revenue forecast dashboard\"\n ],\n \"visualization\": [\n \"create a metric for monthly sales\",\n \"show top vendors by purchase volume\"\n ],\n \"help\": [\n \"what types of analyses can you perform?\",\n \"what questions can I ask buster?\",\n \"what data models are available for queries?\",\n \"can you explain your forecasting capabilities?\"\n ]\n },\n \"updatedAt\": \"2024-01-01T00:00:00.000Z\"\n }'::jsonb" + }, + "personalization_enabled": { + "name": "personalization_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "personalization_config": { + "name": "personalization_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_used_shortcuts": { + "name": "last_used_shortcuts", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_key": { + "name": "users_email_key", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users_to_organizations": { + "name": "users_to_organizations", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "user_organization_role_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'querier'" + }, + "sharing_setting": { + "name": "sharing_setting", + "type": "sharing_setting_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "edit_sql": { + "name": "edit_sql", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "upload_csv": { + "name": "upload_csv", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "export_assets": { + "name": "export_assets", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "email_slack_enabled": { + "name": "email_slack_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "deleted_by": { + "name": "deleted_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "user_organization_status_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + } + }, + "indexes": { + "idx_uto_active_by_user": { + "name": "idx_uto_active_by_user", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"users_to_organizations\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_to_organizations_organization_id_fkey": { + "name": "users_to_organizations_organization_id_fkey", + "tableFrom": "users_to_organizations", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "users_to_organizations_user_id_fkey": { + "name": "users_to_organizations_user_id_fkey", + "tableFrom": "users_to_organizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "users_to_organizations_created_by_fkey": { + "name": "users_to_organizations_created_by_fkey", + "tableFrom": "users_to_organizations", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "users_to_organizations_updated_by_fkey": { + "name": "users_to_organizations_updated_by_fkey", + "tableFrom": "users_to_organizations", + "tableTo": "users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + }, + "users_to_organizations_deleted_by_fkey": { + "name": "users_to_organizations_deleted_by_fkey", + "tableFrom": "users_to_organizations", + "tableTo": "users", + "columnsFrom": [ + "deleted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "users_to_organizations_pkey": { + "name": "users_to_organizations_pkey", + "columns": [ + "user_id", + "organization_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.asset_permission_role_enum": { + "name": "asset_permission_role_enum", + "schema": "public", + "values": [ + "owner", + "viewer", + "can_view", + "can_filter", + "can_edit", + "full_access" + ] + }, + "public.asset_type_enum": { + "name": "asset_type_enum", + "schema": "public", + "values": [ + "chat", + "metric_file", + "dashboard_file", + "report_file", + "collection" + ] + }, + "public.data_source_onboarding_status_enum": { + "name": "data_source_onboarding_status_enum", + "schema": "public", + "values": [ + "notStarted", + "inProgress", + "completed", + "failed" + ] + }, + "public.dataset_type_enum": { + "name": "dataset_type_enum", + "schema": "public", + "values": [ + "table", + "view", + "materializedView" + ] + }, + "public.docs_type_enum": { + "name": "docs_type_enum", + "schema": "public", + "values": [ + "analyst", + "normal" + ] + }, + "public.github_integration_status_enum": { + "name": "github_integration_status_enum", + "schema": "public", + "values": [ + "pending", + "active", + "suspended", + "revoked" + ] + }, + "public.identity_type_enum": { + "name": "identity_type_enum", + "schema": "public", + "values": [ + "user", + "team", + "organization" + ] + }, + "public.message_analysis_mode_enum": { + "name": "message_analysis_mode_enum", + "schema": "public", + "values": [ + "auto", + "standard", + "investigation" + ] + }, + "public.message_feedback_enum": { + "name": "message_feedback_enum", + "schema": "public", + "values": [ + "positive", + "negative" + ] + }, + "public.sharing_setting_enum": { + "name": "sharing_setting_enum", + "schema": "public", + "values": [ + "none", + "team", + "organization", + "public" + ] + }, + "public.slack_chat_authorization_enum": { + "name": "slack_chat_authorization_enum", + "schema": "public", + "values": [ + "unauthorized", + "authorized", + "auto_added" + ] + }, + "public.slack_integration_status_enum": { + "name": "slack_integration_status_enum", + "schema": "public", + "values": [ + "pending", + "active", + "failed", + "revoked" + ] + }, + "public.slack_sharing_permission_enum": { + "name": "slack_sharing_permission_enum", + "schema": "public", + "values": [ + "shareWithWorkspace", + "shareWithChannel", + "noSharing" + ] + }, + "public.storage_provider_enum": { + "name": "storage_provider_enum", + "schema": "public", + "values": [ + "s3", + "r2", + "gcs" + ] + }, + "public.stored_values_status_enum": { + "name": "stored_values_status_enum", + "schema": "public", + "values": [ + "syncing", + "success", + "failed" + ] + }, + "public.table_type_enum": { + "name": "table_type_enum", + "schema": "public", + "values": [ + "TABLE", + "VIEW", + "MATERIALIZED_VIEW", + "EXTERNAL_TABLE", + "TEMPORARY_TABLE" + ] + }, + "public.team_role_enum": { + "name": "team_role_enum", + "schema": "public", + "values": [ + "manager", + "member", + "none" + ] + }, + "public.user_organization_role_enum": { + "name": "user_organization_role_enum", + "schema": "public", + "values": [ + "workspace_admin", + "data_admin", + "querier", + "restricted_querier", + "viewer" + ] + }, + "public.user_organization_status_enum": { + "name": "user_organization_status_enum", + "schema": "public", + "values": [ + "active", + "inactive", + "pending", + "guest" + ] + }, + "public.verification_enum": { + "name": "verification_enum", + "schema": "public", + "values": [ + "verified", + "backlogged", + "inReview", + "requested", + "notRequested" + ] + }, + "public.workspace_sharing_enum": { + "name": "workspace_sharing_enum", + "schema": "public", + "values": [ + "none", + "can_view", + "can_edit", + "full_access" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/database/drizzle/meta/_journal.json b/packages/database/drizzle/meta/_journal.json index 0e7485238..cee3fb0ec 100644 --- a/packages/database/drizzle/meta/_journal.json +++ b/packages/database/drizzle/meta/_journal.json @@ -799,6 +799,13 @@ "when": 1759256209020, "tag": "0114_lovely_risque", "breakpoints": true + }, + { + "idx": 115, + "version": "7", + "when": 1759336097951, + "tag": "0115_glamorous_penance", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/database/src/queries/assets/asset-ancestors.ts b/packages/database/src/queries/assets/asset-ancestors.ts index cdf7677b8..a8465eec0 100644 --- a/packages/database/src/queries/assets/asset-ancestors.ts +++ b/packages/database/src/queries/assets/asset-ancestors.ts @@ -1,4 +1,4 @@ -import { and, eq, isNull, sql } from 'drizzle-orm'; +import { and, eq, inArray, isNull, sql } from 'drizzle-orm'; import { db } from '../../connection'; import { chats, @@ -11,11 +11,30 @@ import { metricFilesToReportFiles, reportFiles, } from '../../schema'; -import type { Ancestor, AssetAncestors } from '../../schema-types'; +import type { Ancestor, AssetAncestors, AssetType } from '../../schema-types'; // Type for database transaction type DatabaseTransaction = Parameters[0]>[0]; +type BatchedAsset = { + assetId: string; + assetType: AssetType; +}; + +type BatchedAncestorRow = { + assetId: string; + ancestorId: string; + ancestorTitle: string; + ancestorType: 'chat' | 'collection' | 'dashboard_file' | 'report_file'; +}; + +const ancestorTypeToKey: Record = { + chat: 'chats', + collection: 'collections', + dashboard_file: 'dashboards', + report_file: 'reports', +}; + /** * Get chat ancestors as a subquery */ @@ -165,6 +184,160 @@ export async function getAssetAncestors( return ancestors; } +export type GetAssetAncestorsForAssetsInput = { + assets: BatchedAsset[]; + userId: string; + organizationId: string; +}; + +export async function getAssetAncestorsForAssets( + input: GetAssetAncestorsForAssetsInput +): Promise> { + const { assets } = input; + if (assets.length === 0) { + return {}; + } + + const assetIds: string[] = []; + const metricAssetIds: string[] = []; + + for (const asset of assets) { + assetIds.push(asset.assetId); + if (asset.assetType === 'metric_file') { + metricAssetIds.push(asset.assetId); + } + } + + const chatRowsPromise: Promise = assetIds.length + ? db + .select({ + assetId: messagesToFiles.fileId, + ancestorId: chats.id, + ancestorTitle: chats.title, + ancestorType: sql<'chat'>`'chat'`.as('ancestorType'), + }) + .from(messagesToFiles) + .innerJoin(messages, eq(messages.id, messagesToFiles.messageId)) + .innerJoin(chats, eq(chats.id, messages.chatId)) + .where( + and( + inArray(messagesToFiles.fileId, assetIds), + isNull(messagesToFiles.deletedAt), + isNull(messages.deletedAt), + isNull(chats.deletedAt) + ) + ) + : // .then((rows) => + // rows.map((row) => ({ + // assetId: row.assetId, + // ancestorId: row.ancestorId, + // ancestorTitle: row.ancestorTitle, + // ancestorType: 'chat', + // })) + // ) + Promise.resolve([]); + + const collectionRowsPromise: Promise = assetIds.length + ? db + .select({ + assetId: collectionsToAssets.assetId, + ancestorId: collections.id, + ancestorTitle: collections.name, + ancestorType: sql<'collection'>`'collection'`.as('ancestorType'), + }) + .from(collectionsToAssets) + .innerJoin(collections, eq(collections.id, collectionsToAssets.collectionId)) + .where( + and( + inArray(collectionsToAssets.assetId, assetIds), + isNull(collectionsToAssets.deletedAt), + isNull(collections.deletedAt) + ) + ) + : Promise.resolve([]); + + const dashboardRowsPromise: Promise = metricAssetIds.length + ? db + .select({ + assetId: metricFilesToDashboardFiles.metricFileId, + ancestorId: dashboardFiles.id, + ancestorTitle: dashboardFiles.name, + ancestorType: sql<'dashboard_file'>`'dashboard_file'`.as('ancestorType'), + }) + .from(metricFilesToDashboardFiles) + .innerJoin( + dashboardFiles, + eq(dashboardFiles.id, metricFilesToDashboardFiles.dashboardFileId) + ) + .where( + and( + inArray(metricFilesToDashboardFiles.metricFileId, metricAssetIds), + isNull(metricFilesToDashboardFiles.deletedAt), + isNull(dashboardFiles.deletedAt) + ) + ) + : Promise.resolve([]); + + const reportRowsPromise: Promise = metricAssetIds.length + ? db + .select({ + assetId: metricFilesToReportFiles.metricFileId, + ancestorId: reportFiles.id, + ancestorTitle: reportFiles.name, + ancestorType: sql<'report_file'>`'report_file'`.as('ancestorType'), + }) + .from(metricFilesToReportFiles) + .innerJoin(reportFiles, eq(reportFiles.id, metricFilesToReportFiles.reportFileId)) + .where( + and( + inArray(metricFilesToReportFiles.metricFileId, metricAssetIds), + isNull(metricFilesToReportFiles.deletedAt), + isNull(reportFiles.deletedAt) + ) + ) + : Promise.resolve([]); + + const [chatRows, collectionRows, dashboardRows, reportRows] = await Promise.all([ + chatRowsPromise, + collectionRowsPromise, + dashboardRowsPromise, + reportRowsPromise, + ]); + + const ancestorsByAssetId: Record = {}; + for (const asset of assets) { + ancestorsByAssetId[asset.assetId] = { + chats: [], + collections: [], + dashboards: [], + reports: [], + }; + } + + const appendRows = (rows: BatchedAncestorRow[]) => { + for (const row of rows) { + const ancestorBucket = ancestorsByAssetId[row.assetId]; + if (!ancestorBucket) { + continue; + } + + const key = ancestorTypeToKey[row.ancestorType]; + const ancestor: Ancestor = { + id: row.ancestorId, + title: row.ancestorTitle, + }; + ancestorBucket[key].push(ancestor); + } + }; + + appendRows(chatRows); + appendRows(collectionRows); + appendRows(dashboardRows); + appendRows(reportRows); + + return ancestorsByAssetId; +} + export async function getAssetChatAncestors( assetId: string, tx?: DatabaseTransaction diff --git a/packages/database/src/queries/assets/index.ts b/packages/database/src/queries/assets/index.ts index 1cb3529bb..29dd6d934 100644 --- a/packages/database/src/queries/assets/index.ts +++ b/packages/database/src/queries/assets/index.ts @@ -23,9 +23,12 @@ export { getMetricDashboardAncestors, getMetricReportAncestors, getAssetAncestors, + getAssetAncestorsForAssets, getAssetAncestorsWithTransaction, } from './asset-ancestors'; +export type { GetAssetAncestorsForAssetsInput } from './asset-ancestors'; + export { getAssetLatestVersion, GetAssetLatestVersionInputSchema, diff --git a/packages/database/src/schema.ts b/packages/database/src/schema.ts index f55b65c54..1ae78df64 100644 --- a/packages/database/src/schema.ts +++ b/packages/database/src/schema.ts @@ -1483,6 +1483,9 @@ export const assetPermissions = pgTable( index('idx_perm_active_asset_identity') .on(table.assetId, table.assetType, table.identityId, table.identityType) .where(isNull(table.deletedAt)), + index('idx_perm_active_identity_asset') + .on(table.identityType, table.identityId, table.assetType, table.assetId) + .where(isNull(table.deletedAt)), ] ); @@ -1913,6 +1916,7 @@ export const assetSearchV2 = pgTable( index('idx_as2_active_by_asset') .on(table.assetId, table.assetType) .where(isNull(table.deletedAt)), + index('idx_as2_active_by_org').on(table.organizationId).where(isNull(table.deletedAt)), ] ); diff --git a/packages/search/src/asset-search/get-asset-screenshot.ts b/packages/search/src/asset-search/get-asset-screenshot.ts new file mode 100644 index 000000000..e30e002ad --- /dev/null +++ b/packages/search/src/asset-search/get-asset-screenshot.ts @@ -0,0 +1,57 @@ +import { type StorageProvider, getProviderForOrganization } from '@buster/data-source'; +import { type AssetType, AssetTypeSchema } from '@buster/server-shared/assets'; +import { z } from 'zod'; + +const GetAssetScreenshotParamsSchema = z.object({ + assetType: AssetTypeSchema, + assetId: z.string().uuid(), + organizationId: z.string().uuid(), + expiresIn: z.number().int().positive().optional(), +}); + +export type GetAssetScreenshotParams = z.infer; + +async function resolveBucketKey( + provider: StorageProvider, + assetType: AssetType, + assetId: string, + organizationId: string +): Promise { + const baseKey = `screenshots/${organizationId}/${assetType}-${assetId}`; + + try { + const objects = await provider.list(baseKey, { maxKeys: 5 }); + if (objects.length > 0) { + const sorted = [...objects].sort((a, b) => { + const aTime = a.lastModified?.getTime?.() ?? 0; + const bTime = b.lastModified?.getTime?.() ?? 0; + return bTime - aTime; + }); + return sorted[0]?.key ?? null; + } + } catch (error) { + console.error('Failed to list screenshot objects', { + baseKey, + assetType, + assetId, + error, + }); + } + + return null; +} + +export async function getAssetScreenshotSignedUrl( + params: Readonly +): Promise { + const { assetType, assetId, expiresIn, organizationId } = + GetAssetScreenshotParamsSchema.parse(params); + const provider = await getProviderForOrganization(organizationId); + const resolvedKey = await resolveBucketKey(provider, assetType, assetId, organizationId); + + if (!resolvedKey) { + throw new Error('Screenshot not found for asset'); + } + + return provider.getSignedUrl(resolvedKey, expiresIn ?? 900); +} diff --git a/packages/search/src/asset-search/index.ts b/packages/search/src/asset-search/index.ts index 5a2bdeb5b..885821e5b 100644 --- a/packages/search/src/asset-search/index.ts +++ b/packages/search/src/asset-search/index.ts @@ -1 +1,2 @@ export * from './search'; +export * from './get-asset-screenshot'; diff --git a/packages/search/src/asset-search/search.test.ts b/packages/search/src/asset-search/search.test.ts index 34422046f..2a706ce3a 100644 --- a/packages/search/src/asset-search/search.test.ts +++ b/packages/search/src/asset-search/search.test.ts @@ -6,7 +6,7 @@ import { performTextSearch } from './search'; vi.mock('@buster/database/queries', () => ({ getUserOrganizationId: vi.fn(), searchText: vi.fn(), - getAssetAncestors: vi.fn(), + getAssetAncestorsForAssets: vi.fn(), })); vi.mock('./text-processing-helpers', () => ({ @@ -14,7 +14,11 @@ vi.mock('./text-processing-helpers', () => ({ })); // Import the mocked functions -import { getAssetAncestors, getUserOrganizationId, searchText } from '@buster/database/queries'; +import { + getAssetAncestorsForAssets, + getUserOrganizationId, + searchText, +} from '@buster/database/queries'; import { processSearchResultText } from './text-processing-helpers'; describe('search.ts - Unit Tests', () => { @@ -59,6 +63,11 @@ describe('search.ts - Unit Tests', () => { reports: [], }; + const mockAncestorsForAssets = { + 'asset-1': mockAncestors, + 'asset-2': mockAncestors, + }; + beforeEach(() => { vi.clearAllMocks(); (getUserOrganizationId as Mock).mockResolvedValue(mockUserOrg); @@ -67,7 +76,7 @@ describe('search.ts - Unit Tests', () => { processedTitle: `${title}`, processedAdditionalText: `${additionalText}`, })); - (getAssetAncestors as Mock).mockResolvedValue(mockAncestors); + (getAssetAncestorsForAssets as Mock).mockResolvedValue(mockAncestorsForAssets); }); describe('performTextSearch', () => { @@ -222,19 +231,15 @@ describe('search.ts - Unit Tests', () => { const result = await performTextSearch(mockUserId, searchRequestWithAncestors); - expect(getAssetAncestors).toHaveBeenCalledTimes(2); - expect(getAssetAncestors).toHaveBeenCalledWith( - 'asset-1', - 'chat', - mockUserId, - mockOrganizationId - ); - expect(getAssetAncestors).toHaveBeenCalledWith( - 'asset-2', - 'metric_file', - mockUserId, - mockOrganizationId - ); + expect(getAssetAncestorsForAssets).toHaveBeenCalledTimes(1); + expect(getAssetAncestorsForAssets).toHaveBeenCalledWith({ + assets: [ + { assetId: 'asset-1', assetType: 'chat' }, + { assetId: 'asset-2', assetType: 'metric_file' }, + ], + userId: mockUserId, + organizationId: mockOrganizationId, + }); expect(result.data[0]).toHaveProperty('ancestors', mockAncestors); expect(result.data[1]).toHaveProperty('ancestors', mockAncestors); @@ -243,7 +248,7 @@ describe('search.ts - Unit Tests', () => { it('should not include ancestors when not requested', async () => { const result = await performTextSearch(mockUserId, basicSearchRequest); - expect(getAssetAncestors).not.toHaveBeenCalled(); + expect(getAssetAncestorsForAssets).not.toHaveBeenCalled(); expect(result.data[0]).not.toHaveProperty('ancestors'); expect(result.data[1]).not.toHaveProperty('ancestors'); }); @@ -264,7 +269,7 @@ describe('search.ts - Unit Tests', () => { expect(result).toEqual(emptySearchResponse); expect(processSearchResultText).not.toHaveBeenCalled(); - expect(getAssetAncestors).not.toHaveBeenCalled(); + expect(getAssetAncestorsForAssets).not.toHaveBeenCalled(); }); it('should handle null/undefined additional text', async () => { @@ -342,6 +347,12 @@ describe('search.ts - Unit Tests', () => { }, }); + // Set up mock ancestors for all 12 assets + const manyAncestorsForAssets = Object.fromEntries( + Array.from({ length: 12 }, (_, i) => [`asset-${i + 1}`, mockAncestors]) + ); + (getAssetAncestorsForAssets as Mock).mockResolvedValue(manyAncestorsForAssets); + const searchRequestWithAncestors: SearchTextRequest = { ...basicSearchRequest, includeAssetAncestors: true, @@ -349,8 +360,8 @@ describe('search.ts - Unit Tests', () => { const result = await performTextSearch(mockUserId, searchRequestWithAncestors); - // Should call getAssetAncestors for each result - expect(getAssetAncestors).toHaveBeenCalledTimes(12); + // Should call getAssetAncestorsForAssets once for the batch + expect(getAssetAncestorsForAssets).toHaveBeenCalledTimes(1); // Results should have ancestors added expect(result.data).toHaveLength(12); @@ -365,7 +376,7 @@ describe('search.ts - Unit Tests', () => { includeAssetAncestors: true, }; - (getAssetAncestors as Mock).mockRejectedValue(new Error('Ancestor lookup failed')); + (getAssetAncestorsForAssets as Mock).mockRejectedValue(new Error('Ancestor lookup failed')); await expect(performTextSearch(mockUserId, searchRequestWithAncestors)).rejects.toThrow( 'Ancestor lookup failed' diff --git a/packages/search/src/asset-search/search.ts b/packages/search/src/asset-search/search.ts index 30a8db62c..a8acf88c0 100644 --- a/packages/search/src/asset-search/search.ts +++ b/packages/search/src/asset-search/search.ts @@ -1,7 +1,6 @@ import { type SearchFilters, - getAssetAncestors, - getAssetAncestorsWithTransaction, + getAssetAncestorsForAssets, getUserOrganizationId, searchText, } from '@buster/database/queries'; @@ -124,7 +123,7 @@ async function addAncestorsToSearchResults( userId: string, organizationId: string ): Promise { - const chunkSize = 25; + const chunkSize = 50; const resultsWithAncestors: SearchTextData[] = []; const totalChunks = Math.ceil(searchResults.length / chunkSize); @@ -134,21 +133,24 @@ async function addAncestorsToSearchResults( for (let i = 0; i < searchResults.length; i += chunkSize) { const chunk = searchResults.slice(i, i + chunkSize); - const chunkResults = await Promise.all( - chunk.map(async (searchResult) => { - const ancestors = await getAssetAncestors( - searchResult.assetId, - searchResult.assetType as AssetType, - userId, - organizationId - ); + const ancestorsByAssetId = await getAssetAncestorsForAssets({ + assets: chunk.map((searchResult) => ({ + assetId: searchResult.assetId, + assetType: searchResult.assetType as AssetType, + })), + userId, + organizationId, + }); - return { - ...searchResult, - ancestors, - }; - }) - ); + const chunkResults = chunk.map((searchResult) => ({ + ...searchResult, + ancestors: ancestorsByAssetId[searchResult.assetId] ?? { + chats: [], + collections: [], + dashboards: [], + reports: [], + }, + })); resultsWithAncestors.push(...chunkResults); } diff --git a/packages/server-shared/package.json b/packages/server-shared/package.json index 5451c8d1a..2345d2ad6 100644 --- a/packages/server-shared/package.json +++ b/packages/server-shared/package.json @@ -103,6 +103,10 @@ "./search": { "types": "./dist/search/index.d.ts", "default": "./dist/search/index.js" + }, + "./screenshots": { + "types": "./dist/screenshots/index.d.ts", + "default": "./dist/screenshots/index.js" } }, "module": "src/index.ts", diff --git a/packages/server-shared/src/index.ts b/packages/server-shared/src/index.ts index bdd305c45..bb823312c 100644 --- a/packages/server-shared/src/index.ts +++ b/packages/server-shared/src/index.ts @@ -22,6 +22,7 @@ export * from './metrics'; export * from './organization'; export * from './public-chat'; export * from './s3-integrations'; +export * from './screenshots'; export * from './search'; export * from './security'; // Export share module (has some naming conflicts with chats and metrics) diff --git a/packages/server-shared/src/screenshots/index.ts b/packages/server-shared/src/screenshots/index.ts new file mode 100644 index 000000000..a7007335b --- /dev/null +++ b/packages/server-shared/src/screenshots/index.ts @@ -0,0 +1 @@ +export * from './screenshots'; diff --git a/packages/server-shared/src/screenshots/screenshots.ts b/packages/server-shared/src/screenshots/screenshots.ts new file mode 100644 index 000000000..dff5b1f1d --- /dev/null +++ b/packages/server-shared/src/screenshots/screenshots.ts @@ -0,0 +1,25 @@ +import { z } from 'zod'; + +export const AssetIdParamsSchema = z.object({ + id: z.string().uuid('Asset ID must be a valid UUID'), +}); + +export const PutScreenshotRequestSchema = z.object({ + base64Image: z.string().min(1, 'Base64 image is required'), +}); + +export type PutScreenshotRequest = z.infer; + +export const PutScreenshotResponseSchema = z.object({ + success: z.literal(true), + bucketKey: z.string().min(1, 'Bucket key is required'), +}); + +export type PutScreenshotResponse = z.infer; + +export const GetScreenshotResponseSchema = z.object({ + success: z.boolean(), + url: z.string().url(), +}); + +export type GetScreenshotResponse = z.infer;