mirror of https://github.com/buster-so/buster.git
create new schema for reports
This commit is contained in:
parent
9173fad35d
commit
aae819e151
|
@ -3,6 +3,7 @@ import {
|
|||
getCollectionTitle,
|
||||
getDashboardTitle,
|
||||
getMetricTitle,
|
||||
getReportTitle,
|
||||
getUserOrganizationId,
|
||||
} from '@buster/database';
|
||||
import { GetTitleRequestSchema, type GetTitleResponse } from '@buster/server-shared/title';
|
||||
|
@ -37,7 +38,8 @@ const app = new Hono()
|
|||
title = await getDashboardTitle({ assetId, organizationId: userOrg?.organizationId });
|
||||
break;
|
||||
case 'report':
|
||||
throw new HTTPException(400, { message: 'Report titles are not supported yet' });
|
||||
title = await getReportTitle({ assetId, organizationId: userOrg?.organizationId });
|
||||
break;
|
||||
default: {
|
||||
const _exhaustive: never = assetType;
|
||||
throw new HTTPException(400, { message: `Unsupported asset type: ${assetType}` });
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
CREATE TABLE "report_files" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"name" varchar NOT NULL,
|
||||
"content" jsonb NOT NULL,
|
||||
"organization_id" uuid NOT NULL,
|
||||
"created_by" uuid NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"deleted_at" timestamp with time zone,
|
||||
"publicly_accessible" boolean DEFAULT false NOT NULL,
|
||||
"publicly_enabled_by" uuid,
|
||||
"public_expiry_date" timestamp with time zone,
|
||||
"version_history" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"public_password" text,
|
||||
"workspace_sharing" "workspace_sharing_enum" DEFAULT 'none' NOT NULL,
|
||||
"workspace_sharing_enabled_by" uuid,
|
||||
"workspace_sharing_enabled_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
|
||||
ALTER TABLE "report_files" ADD CONSTRAINT "report_files_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE cascade;--> statement-breakpoint
|
||||
ALTER TABLE "report_files" ADD CONSTRAINT "report_files_publicly_enabled_by_fkey" FOREIGN KEY ("publicly_enabled_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE cascade;--> statement-breakpoint
|
||||
ALTER TABLE "report_files" ADD CONSTRAINT "report_files_workspace_sharing_enabled_by_fkey" FOREIGN KEY ("workspace_sharing_enabled_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE cascade;--> statement-breakpoint
|
||||
|
||||
CREATE INDEX "report_files_created_by_idx" ON "report_files" USING btree ("created_by" uuid_ops);--> statement-breakpoint
|
||||
CREATE INDEX "report_files_deleted_at_idx" ON "report_files" USING btree ("deleted_at" timestamptz_ops);--> statement-breakpoint
|
||||
CREATE INDEX "report_files_organization_id_idx" ON "report_files" USING btree ("organization_id" uuid_ops);
|
File diff suppressed because it is too large
Load Diff
|
@ -596,6 +596,13 @@
|
|||
"when": 1753076918000,
|
||||
"tag": "0084_github_integrations",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 85,
|
||||
"version": "7",
|
||||
"when": 1754337436368,
|
||||
"tag": "0085_create_report_table",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -8,3 +8,4 @@ export * from './organizations';
|
|||
export * from './dashboards';
|
||||
export * from './metrics';
|
||||
export * from './collections';
|
||||
export * from './reports';
|
||||
|
|
|
@ -11,7 +11,7 @@ export const GetMetricTitleInputSchema = z.object({
|
|||
export type GetMetricTitleInput = z.infer<typeof GetMetricTitleInputSchema>;
|
||||
|
||||
// Updated return type to remove null since we now throw an error instead
|
||||
export async function getMetricTitle(input: GetMetricTitleInput): Promise<string> {
|
||||
export async function getMetricTitle(input: GetMetricTitleInput, isAdmin = false): Promise<string> {
|
||||
const validated = GetMetricTitleInputSchema.parse(input);
|
||||
|
||||
const [metric] = await db
|
||||
|
@ -30,7 +30,11 @@ export async function getMetricTitle(input: GetMetricTitleInput): Promise<string
|
|||
}
|
||||
|
||||
// Throw error for permission failure instead of returning null
|
||||
if (!metric.publiclyAccessible && metric.organizationId !== validated.organizationId) {
|
||||
if (
|
||||
!isAdmin &&
|
||||
!metric.publiclyAccessible &&
|
||||
metric.organizationId !== validated.organizationId
|
||||
) {
|
||||
throw new Error(
|
||||
`Access denied: Metric with ID ${validated.assetId} is not publicly accessible and does not belong to the specified organization`
|
||||
);
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import { and, eq, isNull } from 'drizzle-orm';
|
||||
import { z } from 'zod';
|
||||
import { db } from '../../connection';
|
||||
import { reportFiles } from '../../schema';
|
||||
|
||||
export const GetReportTitleInputSchema = z.object({
|
||||
assetId: z.string().uuid(),
|
||||
organizationId: z.string().uuid().optional(),
|
||||
});
|
||||
|
||||
export type GetReportTitleInput = z.infer<typeof GetReportTitleInputSchema>;
|
||||
|
||||
// Updated return type to remove null since we now throw an error instead
|
||||
export async function getReportTitle(input: GetReportTitleInput): Promise<string> {
|
||||
const validated = GetReportTitleInputSchema.parse(input);
|
||||
|
||||
const [report] = await db
|
||||
.select({
|
||||
name: reportFiles.name,
|
||||
publiclyAccessible: reportFiles.publiclyAccessible,
|
||||
organizationId: reportFiles.organizationId,
|
||||
})
|
||||
.from(reportFiles)
|
||||
.where(and(eq(reportFiles.id, validated.assetId), isNull(reportFiles.deletedAt)))
|
||||
.limit(1);
|
||||
|
||||
// Throw error instead of returning null
|
||||
if (!report) {
|
||||
throw new Error(`Report with ID ${validated.assetId} not found`);
|
||||
}
|
||||
|
||||
// Throw error for permission failure instead of returning null
|
||||
if (!report.publiclyAccessible && report.organizationId !== validated.organizationId) {
|
||||
throw new Error(
|
||||
`Access denied: Report with ID ${validated.assetId} is not publicly accessible and does not belong to the specified organization`
|
||||
);
|
||||
}
|
||||
|
||||
return report.name;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './get-report-title';
|
|
@ -988,6 +988,67 @@ export const dashboardFiles = pgTable(
|
|||
]
|
||||
);
|
||||
|
||||
export const reportFiles = pgTable(
|
||||
'report_files',
|
||||
{
|
||||
id: uuid().defaultRandom().primaryKey().notNull(),
|
||||
name: varchar().notNull(),
|
||||
content: jsonb().notNull(),
|
||||
organizationId: uuid('organization_id').notNull(),
|
||||
createdBy: uuid('created_by').notNull(),
|
||||
createdAt: timestamp('created_at', { withTimezone: true, mode: 'string' })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true, mode: 'string' })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
deletedAt: timestamp('deleted_at', { withTimezone: true, mode: 'string' }),
|
||||
publiclyAccessible: boolean('publicly_accessible').default(false).notNull(),
|
||||
publiclyEnabledBy: uuid('publicly_enabled_by'),
|
||||
publicExpiryDate: timestamp('public_expiry_date', {
|
||||
withTimezone: true,
|
||||
mode: 'string',
|
||||
}),
|
||||
versionHistory: jsonb('version_history').default({}).notNull(),
|
||||
publicPassword: text('public_password'),
|
||||
workspaceSharing: workspaceSharingEnum('workspace_sharing').default('none').notNull(),
|
||||
workspaceSharingEnabledBy: uuid('workspace_sharing_enabled_by'),
|
||||
workspaceSharingEnabledAt: timestamp('workspace_sharing_enabled_at', {
|
||||
withTimezone: true,
|
||||
mode: 'string',
|
||||
}),
|
||||
},
|
||||
(table) => [
|
||||
index('report_files_created_by_idx').using(
|
||||
'btree',
|
||||
table.createdBy.asc().nullsLast().op('uuid_ops')
|
||||
),
|
||||
index('report_files_deleted_at_idx').using(
|
||||
'btree',
|
||||
table.deletedAt.asc().nullsLast().op('timestamptz_ops')
|
||||
),
|
||||
index('report_files_organization_id_idx').using(
|
||||
'btree',
|
||||
table.organizationId.asc().nullsLast().op('uuid_ops')
|
||||
),
|
||||
foreignKey({
|
||||
columns: [table.createdBy],
|
||||
foreignColumns: [users.id],
|
||||
name: 'report_files_created_by_fkey',
|
||||
}).onUpdate('cascade'),
|
||||
foreignKey({
|
||||
columns: [table.publiclyEnabledBy],
|
||||
foreignColumns: [users.id],
|
||||
name: 'report_files_publicly_enabled_by_fkey',
|
||||
}).onUpdate('cascade'),
|
||||
foreignKey({
|
||||
columns: [table.workspaceSharingEnabledBy],
|
||||
foreignColumns: [users.id],
|
||||
name: 'report_files_workspace_sharing_enabled_by_fkey',
|
||||
}).onUpdate('cascade'),
|
||||
]
|
||||
);
|
||||
|
||||
export const chats = pgTable(
|
||||
'chats',
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue