From 4be3592cf4d160be83ddf1db01ffe9afdef119fa Mon Sep 17 00:00:00 2001 From: Guy Ben-Aharon Date: Mon, 18 Nov 2024 13:22:10 +0200 Subject: [PATCH] fix(share): fix export to handle broken indexes & relationships (#416) --- .../export-diagram-dialog.tsx | 34 ++++- src/i18n/locales/de.ts | 5 + src/i18n/locales/en.ts | 5 + src/i18n/locales/es.ts | 5 + src/i18n/locales/fr.ts | 5 + src/i18n/locales/hi.ts | 5 + src/i18n/locales/ja.ts | 5 + src/i18n/locales/ko_KR.ts | 5 + src/i18n/locales/mr.ts | 5 + src/i18n/locales/ne.ts | 5 + src/i18n/locales/pt_BR.ts | 5 + src/i18n/locales/ru.ts | 6 + src/i18n/locales/uk.ts | 5 + src/i18n/locales/zh_CN.ts | 6 + src/i18n/locales/zh_TW.ts | 6 + src/lib/clone.ts | 120 +++++++++++++----- 16 files changed, 189 insertions(+), 38 deletions(-) diff --git a/src/dialogs/export-diagram-dialog/export-diagram-dialog.tsx b/src/dialogs/export-diagram-dialog/export-diagram-dialog.tsx index 8fe14126..17274455 100644 --- a/src/dialogs/export-diagram-dialog/export-diagram-dialog.tsx +++ b/src/dialogs/export-diagram-dialog/export-diagram-dialog.tsx @@ -18,6 +18,8 @@ import { useChartDB } from '@/hooks/use-chartdb'; import { diagramToJSONOutput } from '@/lib/export-import-utils'; import { Spinner } from '@/components/spinner/spinner'; import { waitFor } from '@/lib/utils'; +import { AlertCircle } from 'lucide-react'; +import { Alert, AlertDescription, AlertTitle } from '@/components/alert/alert'; export interface ExportDiagramDialogProps extends BaseDialogProps {} @@ -28,10 +30,12 @@ export const ExportDiagramDialog: React.FC = ({ const { diagramName, currentDiagram } = useChartDB(); const [isLoading, setIsLoading] = useState(false); const { closeExportDiagramDialog } = useDialog(); + const [error, setError] = useState(false); useEffect(() => { if (!dialog.open) return; setIsLoading(false); + setError(false); }, [dialog.open]); const downloadOutput = useCallback( @@ -47,12 +51,19 @@ export const ExportDiagramDialog: React.FC = ({ const handleExport = useCallback(async () => { setIsLoading(true); await waitFor(1000); - const json = diagramToJSONOutput(currentDiagram); - const blob = new Blob([json], { type: 'application/json' }); - const dataUrl = URL.createObjectURL(blob); - downloadOutput(dataUrl); - setIsLoading(false); - closeExportDiagramDialog(); + try { + const json = diagramToJSONOutput(currentDiagram); + const blob = new Blob([json], { type: 'application/json' }); + const dataUrl = URL.createObjectURL(blob); + downloadOutput(dataUrl); + setIsLoading(false); + closeExportDiagramDialog(); + } catch (e) { + setError(true); + setIsLoading(false); + + throw e; + } }, [downloadOutput, currentDiagram, closeExportDiagramDialog]); const outputTypeOptions: SelectBoxOption[] = useMemo( @@ -90,6 +101,17 @@ export const ExportDiagramDialog: React.FC = ({ value="json" /> + {error ? ( + + + + {t('export_diagram_dialog.error.title')} + + + {t('export_diagram_dialog.error.description')} + + + ) : null} diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index 066f680f..2db2cf4c 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -344,6 +344,11 @@ export const de: LanguageTranslation = { format_json: 'JSON', cancel: 'Cancel', export: 'Export', + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, // TODO: Translate import_diagram_dialog: { diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index a858c903..991b1b13 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -340,6 +340,11 @@ export const en = { format_json: 'JSON', cancel: 'Cancel', export: 'Export', + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, import_diagram_dialog: { diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index d10cc235..0f6da176 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -344,6 +344,11 @@ export const es: LanguageTranslation = { format_json: 'JSON', cancel: 'Cancel', export: 'Export', + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, // TODO: Translate import_diagram_dialog: { diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index 4f03542d..0ca62103 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -346,6 +346,11 @@ export const fr: LanguageTranslation = { format_json: 'JSON', cancel: 'Cancel', export: 'Export', + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, // TODO: Translate import_diagram_dialog: { diff --git a/src/i18n/locales/hi.ts b/src/i18n/locales/hi.ts index 54d4d67a..96742d09 100644 --- a/src/i18n/locales/hi.ts +++ b/src/i18n/locales/hi.ts @@ -346,6 +346,11 @@ export const hi: LanguageTranslation = { format_json: 'JSON', cancel: 'Cancel', export: 'Export', + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, // TODO: Translate import_diagram_dialog: { diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts index ef73edf2..8072bf0d 100644 --- a/src/i18n/locales/ja.ts +++ b/src/i18n/locales/ja.ts @@ -348,6 +348,11 @@ export const ja: LanguageTranslation = { format_json: 'JSON', cancel: 'Cancel', export: 'Export', + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, // TODO: Translate import_diagram_dialog: { diff --git a/src/i18n/locales/ko_KR.ts b/src/i18n/locales/ko_KR.ts index fe23c2b7..8da75fd6 100644 --- a/src/i18n/locales/ko_KR.ts +++ b/src/i18n/locales/ko_KR.ts @@ -342,6 +342,11 @@ export const ko_KR: LanguageTranslation = { format_json: 'JSON', cancel: 'Cancel', export: 'Export', + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, // TODO: Translate import_diagram_dialog: { diff --git a/src/i18n/locales/mr.ts b/src/i18n/locales/mr.ts index 4c56768b..fd25320a 100644 --- a/src/i18n/locales/mr.ts +++ b/src/i18n/locales/mr.ts @@ -349,6 +349,11 @@ export const mr: LanguageTranslation = { format_json: 'JSON', cancel: 'Cancel', export: 'Export', + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, // TO diff --git a/src/i18n/locales/ne.ts b/src/i18n/locales/ne.ts index 1f5b5e34..ae51d497 100644 --- a/src/i18n/locales/ne.ts +++ b/src/i18n/locales/ne.ts @@ -343,6 +343,11 @@ export const ne: LanguageTranslation = { format_json: 'JSON', cancel: 'रद्द गर्नुहोस्', export: 'निर्यात गर्नुहोस्', + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, import_diagram_dialog: { diff --git a/src/i18n/locales/pt_BR.ts b/src/i18n/locales/pt_BR.ts index 70cbacf4..174563f0 100644 --- a/src/i18n/locales/pt_BR.ts +++ b/src/i18n/locales/pt_BR.ts @@ -343,6 +343,11 @@ export const pt_BR: LanguageTranslation = { format_json: 'JSON', cancel: 'Cancel', export: 'Export', + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, // TODO: Translate import_diagram_dialog: { diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts index fc38a19c..75e8511e 100644 --- a/src/i18n/locales/ru.ts +++ b/src/i18n/locales/ru.ts @@ -339,6 +339,12 @@ export const ru: LanguageTranslation = { format_json: 'JSON', cancel: 'Отменить', export: 'Экспортировать', + // TODO: Translate + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, import_diagram_dialog: { title: 'Импорт кода диаграммы', diff --git a/src/i18n/locales/uk.ts b/src/i18n/locales/uk.ts index 96f304eb..b36d25d8 100644 --- a/src/i18n/locales/uk.ts +++ b/src/i18n/locales/uk.ts @@ -343,6 +343,11 @@ export const uk: LanguageTranslation = { format_json: 'JSON', cancel: 'Cancel', export: 'Export', + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, // TODO: Translate import_diagram_dialog: { diff --git a/src/i18n/locales/zh_CN.ts b/src/i18n/locales/zh_CN.ts index 44607b40..203e7352 100644 --- a/src/i18n/locales/zh_CN.ts +++ b/src/i18n/locales/zh_CN.ts @@ -334,6 +334,12 @@ export const zh_CN: LanguageTranslation = { format_json: 'JSON', cancel: '取消', export: '导出', + // TODO: translate + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, import_diagram_dialog: { diff --git a/src/i18n/locales/zh_TW.ts b/src/i18n/locales/zh_TW.ts index 6b845e17..0536c72e 100644 --- a/src/i18n/locales/zh_TW.ts +++ b/src/i18n/locales/zh_TW.ts @@ -333,6 +333,12 @@ export const zh_TW: LanguageTranslation = { format_json: 'JSON', cancel: '取消', export: '匯出', + // TODO: Translate + error: { + title: 'Error exporting diagram', + description: + 'Something went wrong. Need help? chartdb.io@gmail.com', + }, }, import_diagram_dialog: { diff --git a/src/lib/clone.ts b/src/lib/clone.ts index d703f127..aea371da 100644 --- a/src/lib/clone.ts +++ b/src/lib/clone.ts @@ -63,28 +63,51 @@ export const cloneTable = ( ...options.idsMap, ]); - const getNewId = (id: string) => { + const getNewId = (id: string): string | null => { const newId = idsMap.get(id); if (!newId) { - throw new Error(`Id not found for ${id}`); + return null; } return newId; }; - const newTable: DBTable = { ...table, id: getNewId(table.id) }; - newTable.fields = table.fields.map( - (field): DBField => ({ - ...field, - id: getNewId(field.id), + const tableId = getNewId(table.id); + if (!tableId) { + throw new Error('Table id not found'); + } + + const newTable: DBTable = { ...table, id: tableId }; + newTable.fields = table.fields + .map((field): DBField | null => { + const id = getNewId(field.id); + + if (!id) { + return null; + } + + return { + ...field, + id, + }; }) - ); - newTable.indexes = table.indexes.map( - (index): DBIndex => ({ - ...index, - fieldIds: index.fieldIds.map((id) => getNewId(id)), - id: getNewId(index.id), + .filter((field): field is DBField => field !== null); + newTable.indexes = table.indexes + .map((index): DBIndex | null => { + const id = getNewId(index.id); + + if (!id) { + return null; + } + + return { + ...index, + fieldIds: index.fieldIds + .map((id) => getNewId(id)) + .filter((fieldId): fieldId is string => fieldId !== null), + id, + }; }) - ); + .filter((index): index is DBIndex => index !== null); return newTable; }; @@ -102,10 +125,10 @@ export const cloneDiagram = ( const idsMap = generateIdsMapFromDiagram(diagram, generateId); - const getNewId = (id: string) => { + const getNewId = (id: string): string | null => { const newId = idsMap.get(id); if (!newId) { - throw new Error(`Id not found for ${id}`); + return null; } return newId; }; @@ -116,26 +139,59 @@ export const cloneDiagram = ( ) ?? []; const relationships: DBRelationship[] = - diagram.relationships?.map( - (relationship): DBRelationship => ({ - ...relationship, - id: getNewId(relationship.id), - sourceTableId: getNewId(relationship.sourceTableId), - targetTableId: getNewId(relationship.targetTableId), - sourceFieldId: getNewId(relationship.sourceFieldId), - targetFieldId: getNewId(relationship.targetFieldId), + diagram.relationships + ?.map((relationship): DBRelationship | null => { + const id = getNewId(relationship.id); + const sourceTableId = getNewId(relationship.sourceTableId); + const targetTableId = getNewId(relationship.targetTableId); + const sourceFieldId = getNewId(relationship.sourceFieldId); + const targetFieldId = getNewId(relationship.targetFieldId); + + if ( + !id || + !sourceTableId || + !targetTableId || + !sourceFieldId || + !targetFieldId + ) { + return null; + } + + return { + ...relationship, + id, + sourceTableId, + targetTableId, + sourceFieldId, + targetFieldId, + }; }) - ) ?? []; + .filter( + (relationship): relationship is DBRelationship => + relationship !== null + ) ?? []; const dependencies: DBDependency[] = - diagram.dependencies?.map( - (dependency): DBDependency => ({ - ...dependency, - id: getNewId(dependency.id), - dependentTableId: getNewId(dependency.dependentTableId), - tableId: getNewId(dependency.tableId), + diagram.dependencies + ?.map((dependency): DBDependency | null => { + const id = getNewId(dependency.id); + const dependentTableId = getNewId(dependency.dependentTableId); + const tableId = getNewId(dependency.tableId); + + if (!id || !dependentTableId || !tableId) { + return null; + } + + return { + ...dependency, + id, + dependentTableId, + tableId, + }; }) - ) ?? []; + .filter( + (dependency): dependency is DBDependency => dependency !== null + ) ?? []; return { ...diagram,