From 433c68a33d79fba446166f649dba18833aeb8712 Mon Sep 17 00:00:00 2001 From: Guy Ben-Aharon Date: Tue, 10 Jun 2025 11:04:51 +0300 Subject: [PATCH] lib refactor (#744) --- src/context/diff-context/diff-provider.tsx | 5 +- .../import-database/import-database.tsx | 4 +- src/lib/clone.ts | 25 ++++--- src/lib/data/import-metadata/utils.ts | 7 +- src/lib/domain/db-dependency.ts | 8 +-- src/lib/domain/db-field.ts | 28 ++++---- src/lib/domain/db-relationship.ts | 8 +-- src/lib/domain/db-table.ts | 24 +++---- .../domain/diff}/diff-check/diff-check.ts | 0 src/lib/domain/diff/diff.ts | 66 ++++++++++++++----- src/lib/domain/diff/field-diff.ts | 40 ++++++----- src/lib/domain/diff/index-diff.ts | 35 ++++++---- src/lib/domain/diff/relationship-diff.ts | 32 +++++---- src/lib/domain/diff/table-diff.ts | 38 +++++++---- src/lib/domain/index.ts | 13 ++++ src/lib/export-import-utils.ts | 15 +++-- .../table-field-modal/table-field-modal.tsx | 2 +- .../table-list-item-content.tsx | 2 +- .../tables-section/table-list/table-list.tsx | 4 +- src/templates-data/template-utils.ts | 2 +- 20 files changed, 227 insertions(+), 131 deletions(-) rename src/{context/diff-context => lib/domain/diff}/diff-check/diff-check.ts (100%) create mode 100644 src/lib/domain/index.ts diff --git a/src/context/diff-context/diff-provider.tsx b/src/context/diff-context/diff-provider.tsx index 8043dc76..7ac852f1 100644 --- a/src/context/diff-context/diff-provider.tsx +++ b/src/context/diff-context/diff-provider.tsx @@ -6,7 +6,10 @@ import type { } from './diff-context'; import { diffContext } from './diff-context'; -import { generateDiff, getDiffMapKey } from './diff-check/diff-check'; +import { + generateDiff, + getDiffMapKey, +} from '@/lib/domain/diff/diff-check/diff-check'; import type { Diagram } from '@/lib/domain/diagram'; import { useEventEmitter } from 'ahooks'; import type { DBField } from '@/lib/domain/db-field'; diff --git a/src/dialogs/common/import-database/import-database.tsx b/src/dialogs/common/import-database/import-database.tsx index 847aac11..61869823 100644 --- a/src/dialogs/common/import-database/import-database.tsx +++ b/src/dialogs/common/import-database/import-database.tsx @@ -36,6 +36,7 @@ import { useDebounce } from '@/hooks/use-debounce-v2'; import { InstructionsSection } from './instructions-section/instructions-section'; import { parseSQLError } from '@/lib/data/sql-import'; import type { editor } from 'monaco-editor'; +import { waitFor } from '@/lib/utils'; const errorScriptOutputMessage = 'Invalid JSON. Please correct it or contact us at support@chartdb.io for help.'; @@ -211,7 +212,8 @@ export const ImportDatabase: React.FC = ({ const handleCheckJson = useCallback(async () => { setIsCheckingJson(true); - const fixedJson = await fixMetadataJson(scriptResult); + await waitFor(1000); + const fixedJson = fixMetadataJson(scriptResult); if (isStringMetadataJson(fixedJson)) { setScriptResult(fixedJson); diff --git a/src/lib/clone.ts b/src/lib/clone.ts index faf71bae..14200a67 100644 --- a/src/lib/clone.ts +++ b/src/lib/clone.ts @@ -124,7 +124,7 @@ export const cloneDiagram = ( } = { generateId: defaultGenerateId, } -): Diagram => { +): { diagram: Diagram; idsMap: Map } => { const { generateId } = options; const diagramId = generateId(); @@ -214,13 +214,20 @@ export const cloneDiagram = ( .filter((area): area is Area => area !== null) ?? []; return { - ...diagram, - id: diagramId, - dependencies, - relationships, - tables, - areas, - createdAt: new Date(), - updatedAt: new Date(), + diagram: { + ...diagram, + id: diagramId, + dependencies, + relationships, + tables, + areas, + createdAt: diagram.createdAt + ? new Date(diagram.createdAt) + : new Date(), + updatedAt: diagram.updatedAt + ? new Date(diagram.updatedAt) + : new Date(), + }, + idsMap, }; }; diff --git a/src/lib/data/import-metadata/utils.ts b/src/lib/data/import-metadata/utils.ts index 009ab71a..fe690176 100644 --- a/src/lib/data/import-metadata/utils.ts +++ b/src/lib/data/import-metadata/utils.ts @@ -1,11 +1,6 @@ -import { waitFor } from '@/lib/utils'; import { isDatabaseMetadata } from './metadata-types/database-metadata'; -export const fixMetadataJson = async ( - metadataJson: string -): Promise => { - await waitFor(1000); - +export const fixMetadataJson = (metadataJson: string): string => { // Replace problematic array default values with null metadataJson = metadataJson.replace( /"default": "?'?\[[^\]]*\]'?"?(\\")?(,|\})/gs, diff --git a/src/lib/domain/db-dependency.ts b/src/lib/domain/db-dependency.ts index 7b9c2e21..e9f19060 100644 --- a/src/lib/domain/db-dependency.ts +++ b/src/lib/domain/db-dependency.ts @@ -11,18 +11,18 @@ import type { AST } from 'node-sql-parser'; export interface DBDependency { id: string; - schema?: string; + schema?: string | null; tableId: string; - dependentSchema?: string; + dependentSchema?: string | null; dependentTableId: string; createdAt: number; } export const dbDependencySchema: z.ZodType = z.object({ id: z.string(), - schema: z.string().optional(), + schema: z.string().or(z.null()).optional(), tableId: z.string(), - dependentSchema: z.string().optional(), + dependentSchema: z.string().or(z.null()).optional(), dependentTableId: z.string(), createdAt: z.number(), }); diff --git a/src/lib/domain/db-field.ts b/src/lib/domain/db-field.ts index da043550..473fe888 100644 --- a/src/lib/domain/db-field.ts +++ b/src/lib/domain/db-field.ts @@ -14,14 +14,14 @@ export interface DBField { primaryKey: boolean; unique: boolean; nullable: boolean; - increment?: boolean; + increment?: boolean | null; createdAt: number; - characterMaximumLength?: string; - precision?: number; - scale?: number; - default?: string; - collation?: string; - comments?: string; + characterMaximumLength?: string | null; + precision?: number | null; + scale?: number | null; + default?: string | null; + collation?: string | null; + comments?: string | null; } export const dbFieldSchema: z.ZodType = z.object({ @@ -31,14 +31,14 @@ export const dbFieldSchema: z.ZodType = z.object({ primaryKey: z.boolean(), unique: z.boolean(), nullable: z.boolean(), - increment: z.boolean().optional(), + increment: z.boolean().or(z.null()).optional(), createdAt: z.number(), - characterMaximumLength: z.string().optional(), - precision: z.number().optional(), - scale: z.number().optional(), - default: z.string().optional(), - collation: z.string().optional(), - comments: z.string().optional(), + characterMaximumLength: z.string().or(z.null()).optional(), + precision: z.number().or(z.null()).optional(), + scale: z.number().or(z.null()).optional(), + default: z.string().or(z.null()).optional(), + collation: z.string().or(z.null()).optional(), + comments: z.string().or(z.null()).optional(), }); export const createFieldsFromMetadata = ({ diff --git a/src/lib/domain/db-relationship.ts b/src/lib/domain/db-relationship.ts index d5db0986..4bb076ec 100644 --- a/src/lib/domain/db-relationship.ts +++ b/src/lib/domain/db-relationship.ts @@ -11,9 +11,9 @@ import { generateId } from '@/lib/utils'; export interface DBRelationship { id: string; name: string; - sourceSchema?: string; + sourceSchema?: string | null; sourceTableId: string; - targetSchema?: string; + targetSchema?: string | null; targetTableId: string; sourceFieldId: string; targetFieldId: string; @@ -25,9 +25,9 @@ export interface DBRelationship { export const dbRelationshipSchema: z.ZodType = z.object({ id: z.string(), name: z.string(), - sourceSchema: z.string().optional(), + sourceSchema: z.string().or(z.null()).optional(), sourceTableId: z.string(), - targetSchema: z.string().optional(), + targetSchema: z.string().or(z.null()).optional(), targetTableId: z.string(), sourceFieldId: z.string(), targetFieldId: z.string(), diff --git a/src/lib/domain/db-table.ts b/src/lib/domain/db-table.ts index e9689b81..f9e6056e 100644 --- a/src/lib/domain/db-table.ts +++ b/src/lib/domain/db-table.ts @@ -34,37 +34,37 @@ export const TABLE_MINIMIZED_FIELDS = 10; export interface DBTable { id: string; name: string; - schema?: string; + schema?: string | null; x: number; y: number; fields: DBField[]; indexes: DBIndex[]; color: string; isView: boolean; - isMaterializedView?: boolean; + isMaterializedView?: boolean | null; createdAt: number; - width?: number; - comments?: string; - order?: number; - expanded?: boolean; + width?: number | null; + comments?: string | null; + order?: number | null; + expanded?: boolean | null; } export const dbTableSchema: z.ZodType = z.object({ id: z.string(), name: z.string(), - schema: z.string().optional(), + schema: z.string().or(z.null()).optional(), x: z.number(), y: z.number(), fields: z.array(dbFieldSchema), indexes: z.array(dbIndexSchema), color: z.string(), isView: z.boolean(), - isMaterializedView: z.boolean().optional(), + isMaterializedView: z.boolean().or(z.null()).optional(), createdAt: z.number(), - width: z.number().optional(), - comments: z.string().optional(), - order: z.number().optional(), - expanded: z.boolean().optional(), + width: z.number().or(z.null()).optional(), + comments: z.string().or(z.null()).optional(), + order: z.number().or(z.null()).optional(), + expanded: z.boolean().or(z.null()).optional(), }); export const shouldShowTablesBySchemaFilter = ( diff --git a/src/context/diff-context/diff-check/diff-check.ts b/src/lib/domain/diff/diff-check/diff-check.ts similarity index 100% rename from src/context/diff-context/diff-check/diff-check.ts rename to src/lib/domain/diff/diff-check/diff-check.ts diff --git a/src/lib/domain/diff/diff.ts b/src/lib/domain/diff/diff.ts index 2d158fd5..a93b6d47 100644 --- a/src/lib/domain/diff/diff.ts +++ b/src/lib/domain/diff/diff.ts @@ -1,27 +1,59 @@ import { z } from 'zod'; import type { FieldDiff } from './field-diff'; -import { fieldDiffSchema } from './field-diff'; +import { createFieldDiffSchema } from './field-diff'; import type { IndexDiff } from './index-diff'; -import { indexDiffSchema } from './index-diff'; +import { createIndexDiffSchema } from './index-diff'; import type { RelationshipDiff } from './relationship-diff'; -import { relationshipDiffSchema } from './relationship-diff'; +import { createRelationshipDiffSchema } from './relationship-diff'; import type { TableDiff } from './table-diff'; -import { tableDiffSchema } from './table-diff'; +import { createTableDiffSchema } from './table-diff'; +import type { DBField, DBIndex, DBRelationship, DBTable } from '..'; -export type ChartDBDiff = TableDiff | FieldDiff | IndexDiff | RelationshipDiff; +export type ChartDBDiff< + TTable = DBTable, + TField = DBField, + TIndex = DBIndex, + TRelationship = DBRelationship, +> = + | TableDiff + | FieldDiff + | IndexDiff + | RelationshipDiff; -export const chartDBDiffSchema: z.ZodType = z.union([ - tableDiffSchema, - fieldDiffSchema, - indexDiffSchema, - relationshipDiffSchema, -]); +export const createChartDBDiffSchema = < + TTable = DBTable, + TField = DBField, + TIndex = DBIndex, + TRelationship = DBRelationship, +>( + tableSchema: z.ZodType, + fieldSchema: z.ZodType, + indexSchema: z.ZodType, + relationshipSchema: z.ZodType +): z.ZodType> => { + return z.union([ + createTableDiffSchema(tableSchema), + createFieldDiffSchema(fieldSchema), + createIndexDiffSchema(indexSchema), + createRelationshipDiffSchema(relationshipSchema), + ]) as z.ZodType>; +}; -export type DiffMap = Map; +export type DiffMap< + TTable = DBTable, + TField = DBField, + TIndex = DBIndex, + TRelationship = DBRelationship, +> = Map>; -export type DiffObject = - | TableDiff['object'] - | FieldDiff['object'] - | IndexDiff['object'] - | RelationshipDiff['object']; +export type DiffObject< + TTable = DBTable, + TField = DBField, + TIndex = DBIndex, + TRelationship = DBRelationship, +> = + | TableDiff['object'] + | FieldDiff['object'] + | IndexDiff['object'] + | RelationshipDiff['object']; diff --git a/src/lib/domain/diff/field-diff.ts b/src/lib/domain/diff/field-diff.ts index ba217e52..f8b33fc8 100644 --- a/src/lib/domain/diff/field-diff.ts +++ b/src/lib/domain/diff/field-diff.ts @@ -5,7 +5,6 @@ import { dataTypeSchema, } from '@/lib/data/data-types/data-types'; import type { DBField } from '../db-field'; -import { dbFieldSchema } from '../db-field'; export type FieldDiffAttribute = | 'name' @@ -24,19 +23,23 @@ export const fieldDiffAttributeSchema: z.ZodType = z.union([ z.literal('comments'), ]); -export interface FieldDiffAdded { +export interface FieldDiffAdded { object: 'field'; type: 'added'; tableId: string; - newField: DBField; + newField: T; } -export const fieldDiffAddedSchema: z.ZodType = z.object({ - object: z.literal('field'), - type: z.literal('added'), - tableId: z.string(), - newField: dbFieldSchema, -}); +export const createFieldDiffAddedSchema = ( + fieldSchema: z.ZodType +): z.ZodType> => { + return z.object({ + object: z.literal('field'), + type: z.literal('added'), + tableId: z.string(), + newField: fieldSchema, + }) as z.ZodType>; +}; export interface FieldDiffRemoved { object: 'field'; @@ -72,10 +75,17 @@ export const fieldDiffChangedSchema: z.ZodType = z.object({ newValue: z.union([z.string(), z.boolean(), dataTypeSchema]), }); -export type FieldDiff = FieldDiffAdded | FieldDiffRemoved | FieldDiffChanged; +export type FieldDiff = + | FieldDiffAdded + | FieldDiffRemoved + | FieldDiffChanged; -export const fieldDiffSchema: z.ZodType = z.union([ - fieldDiffRemovedSchema, - fieldDiffChangedSchema, - fieldDiffAddedSchema, -]); +export const createFieldDiffSchema = ( + fieldSchema: z.ZodType +): z.ZodType> => { + return z.union([ + fieldDiffRemovedSchema, + fieldDiffChangedSchema, + createFieldDiffAddedSchema(fieldSchema), + ]) as z.ZodType>; +}; diff --git a/src/lib/domain/diff/index-diff.ts b/src/lib/domain/diff/index-diff.ts index 4c398828..0bd42f5b 100644 --- a/src/lib/domain/diff/index-diff.ts +++ b/src/lib/domain/diff/index-diff.ts @@ -1,20 +1,23 @@ import { z } from 'zod'; import type { DBIndex } from '../db-index'; -import { dbIndexSchema } from '../db-index'; -export interface IndexDiffAdded { +export interface IndexDiffAdded { object: 'index'; type: 'added'; tableId: string; - newIndex: DBIndex; + newIndex: T; } -export const indexDiffAddedSchema: z.ZodType = z.object({ - object: z.literal('index'), - type: z.literal('added'), - tableId: z.string(), - newIndex: dbIndexSchema, -}); +export const createIndexDiffAddedSchema = ( + indexSchema: z.ZodType +): z.ZodType> => { + return z.object({ + object: z.literal('index'), + type: z.literal('added'), + tableId: z.string(), + newIndex: indexSchema, + }) as z.ZodType>; +}; export interface IndexDiffRemoved { object: 'index'; @@ -30,9 +33,13 @@ export const indexDiffRemovedSchema: z.ZodType = z.object({ tableId: z.string(), }); -export type IndexDiff = IndexDiffAdded | IndexDiffRemoved; +export type IndexDiff = IndexDiffAdded | IndexDiffRemoved; -export const indexDiffSchema: z.ZodType = z.union([ - indexDiffAddedSchema, - indexDiffRemovedSchema, -]); +export const createIndexDiffSchema = ( + indexSchema: z.ZodType +): z.ZodType> => { + return z.union([ + createIndexDiffAddedSchema(indexSchema), + indexDiffRemovedSchema, + ]) as z.ZodType>; +}; diff --git a/src/lib/domain/diff/relationship-diff.ts b/src/lib/domain/diff/relationship-diff.ts index d342fbdb..0eafe86b 100644 --- a/src/lib/domain/diff/relationship-diff.ts +++ b/src/lib/domain/diff/relationship-diff.ts @@ -1,19 +1,21 @@ import { z } from 'zod'; import type { DBRelationship } from '../db-relationship'; -import { dbRelationshipSchema } from '../db-relationship'; -export interface RelationshipDiffAdded { +export interface RelationshipDiffAdded { object: 'relationship'; type: 'added'; - newRelationship: DBRelationship; + newRelationship: T; } -export const relationshipDiffAddedSchema: z.ZodType = - z.object({ +export const createRelationshipDiffAddedSchema = ( + relationshipSchema: z.ZodType +): z.ZodType> => { + return z.object({ object: z.literal('relationship'), type: z.literal('added'), - newRelationship: dbRelationshipSchema, - }); + newRelationship: relationshipSchema, + }) as z.ZodType>; +}; export interface RelationshipDiffRemoved { object: 'relationship'; @@ -28,9 +30,15 @@ export const relationshipDiffRemovedSchema: z.ZodType = relationshipId: z.string(), }); -export type RelationshipDiff = RelationshipDiffAdded | RelationshipDiffRemoved; +export type RelationshipDiff = + | RelationshipDiffAdded + | RelationshipDiffRemoved; -export const relationshipDiffSchema: z.ZodType = z.union([ - relationshipDiffAddedSchema, - relationshipDiffRemovedSchema, -]); +export const createRelationshipDiffSchema = ( + relationshipSchema: z.ZodType +): z.ZodType> => { + return z.union([ + createRelationshipDiffAddedSchema(relationshipSchema), + relationshipDiffRemovedSchema, + ]) as z.ZodType>; +}; diff --git a/src/lib/domain/diff/table-diff.ts b/src/lib/domain/diff/table-diff.ts index dd27ff97..fa492bf5 100644 --- a/src/lib/domain/diff/table-diff.ts +++ b/src/lib/domain/diff/table-diff.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; import type { DBTable } from '../db-table'; -import { dbTableSchema } from '../db-table'; export type TableDiffAttribute = keyof Pick< DBTable, @@ -43,22 +42,33 @@ export const TableDiffRemovedSchema: z.ZodType = z.object({ tableId: z.string(), }); -export interface TableDiffAdded { +export interface TableDiffAdded { object: 'table'; type: 'added'; - tableAdded: DBTable; + tableAdded: T; } -export const TableDiffAddedSchema: z.ZodType = z.object({ - object: z.literal('table'), - type: z.literal('added'), - tableAdded: dbTableSchema, -}); +export const createTableDiffAddedSchema = ( + tableSchema: z.ZodType +): z.ZodType> => { + return z.object({ + object: z.literal('table'), + type: z.literal('added'), + tableAdded: tableSchema, + }) as z.ZodType>; +}; -export type TableDiff = TableDiffChanged | TableDiffRemoved | TableDiffAdded; +export type TableDiff = + | TableDiffChanged + | TableDiffRemoved + | TableDiffAdded; -export const tableDiffSchema: z.ZodType = z.union([ - TableDiffChangedSchema, - TableDiffRemovedSchema, - TableDiffAddedSchema, -]); +export const createTableDiffSchema = ( + tableSchema: z.ZodType +): z.ZodType> => { + return z.union([ + TableDiffChangedSchema, + TableDiffRemovedSchema, + createTableDiffAddedSchema(tableSchema), + ]) as z.ZodType>; +}; diff --git a/src/lib/domain/index.ts b/src/lib/domain/index.ts new file mode 100644 index 00000000..c7e874b8 --- /dev/null +++ b/src/lib/domain/index.ts @@ -0,0 +1,13 @@ +export * from './area'; +export * from './config'; +export * from './database-clients'; +export * from './database-edition'; +export * from './database-type'; +export * from './db-custom-type'; +export * from './db-dependency'; +export * from './db-field'; +export * from './db-index'; +export * from './db-relationship'; +export * from './db-schema'; +export * from './db-table'; +export * from './diagram'; diff --git a/src/lib/export-import-utils.ts b/src/lib/export-import-utils.ts index a3497016..0397810e 100644 --- a/src/lib/export-import-utils.ts +++ b/src/lib/export-import-utils.ts @@ -7,16 +7,23 @@ export const runningIdGenerator = (): (() => string) => { return () => (id++).toString(); }; -const cloneDiagramWithRunningIds = (diagram: Diagram) => - cloneDiagram(diagram, { generateId: runningIdGenerator() }); +export const cloneDiagramWithRunningIds = ( + diagram: Diagram +): { diagram: Diagram; idsMap: Map } => { + const { diagram: clonedDiagram, idsMap } = cloneDiagram(diagram, { + generateId: runningIdGenerator(), + }); + + return { diagram: clonedDiagram, idsMap }; +}; const cloneDiagramWithIds = (diagram: Diagram): Diagram => ({ - ...cloneDiagram(diagram), + ...cloneDiagram(diagram).diagram, id: generateDiagramId(), }); export const diagramToJSONOutput = (diagram: Diagram): string => { - const clonedDiagram = cloneDiagramWithRunningIds(diagram); + const clonedDiagram = cloneDiagramWithRunningIds(diagram).diagram; return JSON.stringify(clonedDiagram, null, 2); }; diff --git a/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-field/table-field-modal/table-field-modal.tsx b/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-field/table-field-modal/table-field-modal.tsx index c8cecf79..84959e5e 100644 --- a/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-field/table-field-modal/table-field-modal.tsx +++ b/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-field/table-field-modal/table-field-modal.tsx @@ -141,7 +141,7 @@ export const TableFieldPopover: React.FC = ({ )}