From aa34a56f4069beeffc5eebedc89a95252cbf9ae5 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Fri, 18 Jul 2025 13:04:12 -0600 Subject: [PATCH] override cache for defaults --- .../v2/dictionaries/color-palettes/config.ts | 6 +- .../v2/dictionaries/color-palettes/index.ts | 4 +- apps/server/src/utils/admin.ts | 3 +- .../src/queries/organizations/index.ts | 2 +- .../organizations/update-organization.ts | 100 ------------------ .../update-organization/index.ts | 2 + .../organization-color-palettes.ts | 53 ++++++++++ .../update-organization.ts | 55 ++++++++++ 8 files changed, 119 insertions(+), 106 deletions(-) delete mode 100644 packages/database/src/queries/organizations/update-organization.ts create mode 100644 packages/database/src/queries/organizations/update-organization/index.ts create mode 100644 packages/database/src/queries/organizations/update-organization/organization-color-palettes.ts create mode 100644 packages/database/src/queries/organizations/update-organization/update-organization.ts diff --git a/apps/server/src/api/v2/dictionaries/color-palettes/config.ts b/apps/server/src/api/v2/dictionaries/color-palettes/config.ts index 56e230c2d..25eb38876 100644 --- a/apps/server/src/api/v2/dictionaries/color-palettes/config.ts +++ b/apps/server/src/api/v2/dictionaries/color-palettes/config.ts @@ -1,4 +1,6 @@ +import type { DEFAULT_COLOR_PALETTE_ID as DEFAULT_COLOR_PALETTE_ID_DATABASE } from '@buster/database'; import { DEFAULT_CHART_THEME } from '@buster/server-shared/metrics'; + import type { ColorPalette } from '@buster/server-shared/organization'; const SOFT_THEME = [ @@ -309,7 +311,7 @@ const PINK_THEME = [ '#ad1457', ]; -export const DEFAULT_COLOR_PALETTE_ID = '__DEFAULT__'; +export const DEFAULT_COLOR_PALETTE_ID: typeof DEFAULT_COLOR_PALETTE_ID_DATABASE = '__DEFAULT__'; const createDefaultId = (name: string, index: number) => { return `${DEFAULT_COLOR_PALETTE_ID}${name.toLowerCase().replace(/ /g, '-')}-${index}`; @@ -430,3 +432,5 @@ export const MONOCHROME_THEMES = [ })); export const ALL_THEMES: ColorPalette[] = [...COLORFUL_THEMES, ...MONOCHROME_THEMES]; + +export const ALL_DICTIONARY_THEMES: ColorPalette[] = ALL_THEMES; diff --git a/apps/server/src/api/v2/dictionaries/color-palettes/index.ts b/apps/server/src/api/v2/dictionaries/color-palettes/index.ts index 5676849e9..867a2747d 100644 --- a/apps/server/src/api/v2/dictionaries/color-palettes/index.ts +++ b/apps/server/src/api/v2/dictionaries/color-palettes/index.ts @@ -1,11 +1,11 @@ import type { ColorPaletteDictionariesResponse } from '@buster/server-shared/dictionary'; import { Hono } from 'hono'; -import { ALL_THEMES } from './config'; +import { ALL_DICTIONARY_THEMES } from './config'; const app = new Hono(); app.get('/', async (c) => { - const response: ColorPaletteDictionariesResponse = ALL_THEMES; + const response: ColorPaletteDictionariesResponse = ALL_DICTIONARY_THEMES; return c.json(response); }); diff --git a/apps/server/src/utils/admin.ts b/apps/server/src/utils/admin.ts index 4a1d724f7..e5fac8d51 100644 --- a/apps/server/src/utils/admin.ts +++ b/apps/server/src/utils/admin.ts @@ -1,6 +1,5 @@ import type { OrganizationRole } from '@buster/server-shared/organization'; -import { OrganizationRoleEnum } from '@buster/server-shared/organization'; export const isOrganizationAdmin = (role: OrganizationRole) => { - return role === OrganizationRoleEnum.workspace_admin || role === OrganizationRoleEnum.data_admin; + return role === 'workspace_admin' || role === 'data_admin'; }; diff --git a/packages/database/src/queries/organizations/index.ts b/packages/database/src/queries/organizations/index.ts index dee76b2b5..cbbc781ea 100644 --- a/packages/database/src/queries/organizations/index.ts +++ b/packages/database/src/queries/organizations/index.ts @@ -9,4 +9,4 @@ export { type UserToOrganization, } from './organizations'; -export { updateOrganization } from './update-organization'; +export { updateOrganization, type DEFAULT_COLOR_PALETTE_ID } from './update-organization'; diff --git a/packages/database/src/queries/organizations/update-organization.ts b/packages/database/src/queries/organizations/update-organization.ts deleted file mode 100644 index 56a3644b0..000000000 --- a/packages/database/src/queries/organizations/update-organization.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { and, eq, isNull } from 'drizzle-orm'; -import { z } from 'zod'; -import { db } from '../../connection'; -import { organizations } from '../../schema'; -import type { OrganizationColorPalettes } from '../../schema-types'; - -// Hex color validation schema for 3 or 6 digit hex codes -const HexColorSchema = z - .string() - .regex( - /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/, - 'Must be a valid 3 or 6 digit hex color code (e.g., #fff or #ffffff)' - ); - -// Organization Color Palette schema -const OrganizationColorPaletteSchema = z.object({ - id: z.string().min(1).max(255), - colors: z.array(HexColorSchema), - name: z.string().min(1).max(255), -}); - -// Organization Color Palettes schema (extracted to its own schema) -const OrganizationColorPalettesSchema = z - .object({ - selectedId: z.string().min(1).max(255).nullable(), - palettes: z.array(OrganizationColorPaletteSchema).refine( - (palettes) => { - if (!palettes || palettes.length === 0) return true; - const ids = palettes.map((palette) => palette.id); - const uniqueIds = new Set(ids); - return ids.length === uniqueIds.size; - }, - { - message: 'All color palette IDs must be unique', - } - ), - }) - .refine( - (data) => { - // If selectedId is null, no validation needed - if (data.selectedId === null) return true; - - // If selectedId is provided, it must exist in the palettes array - const paletteIds = data.palettes.map((palette) => palette.id); - return paletteIds.includes(data.selectedId); - }, - { - message: 'Selected ID must exist in the palettes array', - path: ['selectedId'], // Point the error to the selectedId field - } - ); - -// Input validation schema -const UpdateOrganizationInputSchema = z.object({ - organizationId: z.string().uuid('Organization ID must be a valid UUID'), - organizationColorPalettes: OrganizationColorPalettesSchema, -}); - -type UpdateOrganizationInput = z.infer; - -/** - * Updates organization settings - * Currently supports updating organization color palettes - */ -export const updateOrganization = async (params: UpdateOrganizationInput): Promise => { - // Validate and destructure input - const { organizationId, organizationColorPalettes } = UpdateOrganizationInputSchema.parse(params); - - try { - // Build update data - const updateData: { - updatedAt: string; - organizationColorPalettes?: OrganizationColorPalettes; - } = { - updatedAt: new Date().toISOString(), - }; - - if (organizationColorPalettes !== undefined) { - updateData.organizationColorPalettes = organizationColorPalettes; - } - - // Update organization in database - await db - .update(organizations) - .set(updateData) - .where(and(eq(organizations.id, organizationId), isNull(organizations.deletedAt))); - } catch (error) { - console.error('Error updating organization:', { - organizationId, - error: error instanceof Error ? error.message : error, - }); - - // Re-throw with more context - if (error instanceof Error) { - throw error; - } - - throw new Error('Failed to update organization'); - } -}; diff --git a/packages/database/src/queries/organizations/update-organization/index.ts b/packages/database/src/queries/organizations/update-organization/index.ts new file mode 100644 index 000000000..6ad1333b5 --- /dev/null +++ b/packages/database/src/queries/organizations/update-organization/index.ts @@ -0,0 +1,2 @@ +export * from './update-organization'; +export type { DEFAULT_COLOR_PALETTE_ID } from './organization-color-palettes'; diff --git a/packages/database/src/queries/organizations/update-organization/organization-color-palettes.ts b/packages/database/src/queries/organizations/update-organization/organization-color-palettes.ts new file mode 100644 index 000000000..7cc5804b0 --- /dev/null +++ b/packages/database/src/queries/organizations/update-organization/organization-color-palettes.ts @@ -0,0 +1,53 @@ +import { z } from 'zod'; + +export const DEFAULT_COLOR_PALETTE_ID = '__DEFAULT__'; + +// Hex color validation schema for 3 or 6 digit hex codes +const HexColorSchema = z + .string() + .regex( + /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/, + 'Must be a valid 3 or 6 digit hex color code (e.g., #fff or #ffffff)' + ); + +// Organization Color Palette schema +const OrganizationColorPaletteSchema = z.object({ + id: z.string().min(1).max(255), + colors: z.array(HexColorSchema), + name: z.string().min(1).max(255), +}); + +// Organization Color Palettes schema (extracted to its own schema) +export const OrganizationColorPalettesSchema = z + .object({ + selectedId: z.string().min(1).max(255).nullable(), + palettes: z.array(OrganizationColorPaletteSchema).refine( + (palettes) => { + if (!palettes || palettes.length === 0) return true; + const ids = palettes.map((palette) => palette.id); + const uniqueIds = new Set(ids); + return ids.length === uniqueIds.size; + }, + { + message: 'All color palette IDs must be unique', + } + ), + }) + .refine( + (data) => { + // If selectedId is null, no validation needed + console.log('data.selectedId', data.selectedId); + if (data.selectedId === null || data.selectedId.startsWith(DEFAULT_COLOR_PALETTE_ID)) { + return true; + } + + // If selectedId is provided, it must exist in the palettes array + const paletteIds = data.palettes.map((palette) => palette.id); + return paletteIds.includes(data.selectedId); + }, + { + message: + 'Selected ID must exist in the palettes array or be a default palette from the dictionary endpoint', + path: ['selectedId'], // Point the error to the selectedId field + } + ); diff --git a/packages/database/src/queries/organizations/update-organization/update-organization.ts b/packages/database/src/queries/organizations/update-organization/update-organization.ts new file mode 100644 index 000000000..572c9132d --- /dev/null +++ b/packages/database/src/queries/organizations/update-organization/update-organization.ts @@ -0,0 +1,55 @@ +import { and, eq, isNull } from 'drizzle-orm'; +import { z } from 'zod'; +import { db } from '../../../connection'; +import { organizations } from '../../../schema'; +import type { OrganizationColorPalettes } from '../../../schema-types'; +import { OrganizationColorPalettesSchema } from './organization-color-palettes'; + +// Input validation schema +const UpdateOrganizationInputSchema = z.object({ + organizationId: z.string().uuid('Organization ID must be a valid UUID'), + organizationColorPalettes: OrganizationColorPalettesSchema, +}); + +type UpdateOrganizationInput = z.infer; + +/** + * Updates organization settings + * Currently supports updating organization color palettes + */ +export const updateOrganization = async (params: UpdateOrganizationInput): Promise => { + // Validate and destructure input + const { organizationId, organizationColorPalettes } = UpdateOrganizationInputSchema.parse(params); + + try { + // Build update data + const updateData: { + updatedAt: string; + organizationColorPalettes?: OrganizationColorPalettes; + } = { + updatedAt: new Date().toISOString(), + }; + + if (organizationColorPalettes !== undefined) { + updateData.organizationColorPalettes = organizationColorPalettes; + } + + // Update organization in database + await db + .update(organizations) + .set(updateData) + .where(and(eq(organizations.id, organizationId), isNull(organizations.deletedAt))); + } catch (error) { + console.error('Error updating organization:', { + organizationId, + error: error instanceof Error ? error.message : error, + }); + + // Re-throw with more context + if (error instanceof Error) { + throw error; + } + + throw new Error('Failed to update organization'); + } +};