mirror of
https://github.com/chartdb/chartdb.git
synced 2025-10-28 02:23:58 +00:00
535 lines
15 KiB
TypeScript
535 lines
15 KiB
TypeScript
import React, { useCallback } from 'react';
|
|
import type {
|
|
DiffCalculatedData,
|
|
DiffContext,
|
|
DiffEvent,
|
|
} from './diff-context';
|
|
import { diffContext } from './diff-context';
|
|
|
|
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';
|
|
import type { DataType } from '@/lib/data/data-types/data-types';
|
|
import type { DBRelationship } from '@/lib/domain/db-relationship';
|
|
import type { ChartDBDiff, DiffMap } from '@/lib/domain/diff/diff';
|
|
|
|
export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|
children,
|
|
}) => {
|
|
const [newDiagram, setNewDiagram] = React.useState<Diagram | null>(null);
|
|
const [originalDiagram, setOriginalDiagram] =
|
|
React.useState<Diagram | null>(null);
|
|
const [diffMap, setDiffMap] = React.useState<DiffMap>(
|
|
new Map<string, ChartDBDiff>()
|
|
);
|
|
const [tablesChanged, setTablesChanged] = React.useState<
|
|
Map<string, boolean>
|
|
>(new Map<string, boolean>());
|
|
const [fieldsChanged, setFieldsChanged] = React.useState<
|
|
Map<string, boolean>
|
|
>(new Map<string, boolean>());
|
|
const [isSummaryOnly, setIsSummaryOnly] = React.useState<boolean>(false);
|
|
|
|
const events = useEventEmitter<DiffEvent>();
|
|
|
|
const generateFieldsToAddMap = useCallback(
|
|
({
|
|
diffMap,
|
|
newDiagram,
|
|
}: {
|
|
diffMap: DiffMap;
|
|
newDiagram: Diagram;
|
|
}) => {
|
|
const newFieldsMap = new Map<string, DBField[]>();
|
|
|
|
diffMap.forEach((diff) => {
|
|
if (diff.object === 'field' && diff.type === 'added') {
|
|
const field = newDiagram?.tables
|
|
?.find((table) => table.id === diff.tableId)
|
|
?.fields.find((f) => f.id === diff.newField.id);
|
|
|
|
if (field) {
|
|
newFieldsMap.set(diff.tableId, [
|
|
...(newFieldsMap.get(diff.tableId) ?? []),
|
|
field,
|
|
]);
|
|
}
|
|
}
|
|
});
|
|
|
|
return newFieldsMap;
|
|
},
|
|
[]
|
|
);
|
|
|
|
const findRelationshipsToAdd = useCallback(
|
|
({
|
|
diffMap,
|
|
newDiagram,
|
|
}: {
|
|
diffMap: DiffMap;
|
|
newDiagram: Diagram;
|
|
}) => {
|
|
const relationships: DBRelationship[] = [];
|
|
diffMap.forEach((diff) => {
|
|
if (diff.object === 'relationship' && diff.type === 'added') {
|
|
const relationship = newDiagram?.relationships?.find(
|
|
(rel) => rel.id === diff.newRelationship.id
|
|
);
|
|
|
|
if (relationship) {
|
|
relationships.push(relationship);
|
|
}
|
|
}
|
|
});
|
|
|
|
return relationships;
|
|
},
|
|
[]
|
|
);
|
|
|
|
const generateDiffCalculatedData = useCallback(
|
|
({
|
|
newDiagram,
|
|
diffMap,
|
|
}: {
|
|
newDiagram: Diagram;
|
|
diffMap: DiffMap;
|
|
}): DiffCalculatedData => {
|
|
return {
|
|
tablesToAdd:
|
|
newDiagram?.tables?.filter((table) => {
|
|
const tableKey = getDiffMapKey({
|
|
diffObject: 'table',
|
|
objectId: table.id,
|
|
});
|
|
|
|
return (
|
|
diffMap.has(tableKey) &&
|
|
diffMap.get(tableKey)?.type === 'added'
|
|
);
|
|
}) ?? [],
|
|
|
|
fieldsToAdd: generateFieldsToAddMap({
|
|
diffMap: diffMap,
|
|
newDiagram: newDiagram,
|
|
}),
|
|
relationshipsToAdd: findRelationshipsToAdd({
|
|
diffMap: diffMap,
|
|
newDiagram: newDiagram,
|
|
}),
|
|
};
|
|
},
|
|
[findRelationshipsToAdd, generateFieldsToAddMap]
|
|
);
|
|
|
|
const calculateDiff: DiffContext['calculateDiff'] = useCallback(
|
|
({ diagram, newDiagram: newDiagramArg, options }) => {
|
|
const {
|
|
diffMap: newDiffs,
|
|
changedTables: newChangedTables,
|
|
changedFields: newChangedFields,
|
|
} = generateDiff({ diagram, newDiagram: newDiagramArg });
|
|
|
|
setDiffMap(newDiffs);
|
|
setTablesChanged(newChangedTables);
|
|
setFieldsChanged(newChangedFields);
|
|
setNewDiagram(newDiagramArg);
|
|
setOriginalDiagram(diagram);
|
|
setIsSummaryOnly(options?.summaryOnly ?? false);
|
|
|
|
events.emit({
|
|
action: 'diff_calculated',
|
|
data: generateDiffCalculatedData({
|
|
diffMap: newDiffs,
|
|
newDiagram: newDiagramArg,
|
|
}),
|
|
});
|
|
|
|
return { foundDiff: !!newDiffs.size };
|
|
},
|
|
[setDiffMap, events, generateDiffCalculatedData]
|
|
);
|
|
|
|
const getTableNewName = useCallback<DiffContext['getTableNewName']>(
|
|
({ tableId }) => {
|
|
const tableNameKey = getDiffMapKey({
|
|
diffObject: 'table',
|
|
objectId: tableId,
|
|
attribute: 'name',
|
|
});
|
|
|
|
if (diffMap.has(tableNameKey)) {
|
|
const diff = diffMap.get(tableNameKey);
|
|
|
|
if (diff?.type === 'changed') {
|
|
return {
|
|
new: diff.newValue as string,
|
|
old: diff.oldValue as string,
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const getTableNewColor = useCallback<DiffContext['getTableNewColor']>(
|
|
({ tableId }) => {
|
|
const tableColorKey = getDiffMapKey({
|
|
diffObject: 'table',
|
|
objectId: tableId,
|
|
attribute: 'color',
|
|
});
|
|
|
|
if (diffMap.has(tableColorKey)) {
|
|
const diff = diffMap.get(tableColorKey);
|
|
|
|
if (diff?.type === 'changed') {
|
|
return {
|
|
new: diff.newValue as string,
|
|
old: diff.oldValue as string,
|
|
};
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const checkIfTableHasChange = useCallback<
|
|
DiffContext['checkIfTableHasChange']
|
|
>(({ tableId }) => tablesChanged.get(tableId) ?? false, [tablesChanged]);
|
|
|
|
const checkIfNewTable = useCallback<DiffContext['checkIfNewTable']>(
|
|
({ tableId }) => {
|
|
const tableKey = getDiffMapKey({
|
|
diffObject: 'table',
|
|
objectId: tableId,
|
|
});
|
|
|
|
return (
|
|
diffMap.has(tableKey) && diffMap.get(tableKey)?.type === 'added'
|
|
);
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const checkIfTableRemoved = useCallback<DiffContext['checkIfTableRemoved']>(
|
|
({ tableId }) => {
|
|
const tableKey = getDiffMapKey({
|
|
diffObject: 'table',
|
|
objectId: tableId,
|
|
});
|
|
|
|
return (
|
|
diffMap.has(tableKey) &&
|
|
diffMap.get(tableKey)?.type === 'removed'
|
|
);
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const checkIfFieldHasChange = useCallback<
|
|
DiffContext['checkIfFieldHasChange']
|
|
>(
|
|
({ fieldId }) => {
|
|
return fieldsChanged.get(fieldId) ?? false;
|
|
},
|
|
[fieldsChanged]
|
|
);
|
|
|
|
const checkIfFieldRemoved = useCallback<DiffContext['checkIfFieldRemoved']>(
|
|
({ fieldId }) => {
|
|
const fieldKey = getDiffMapKey({
|
|
diffObject: 'field',
|
|
objectId: fieldId,
|
|
});
|
|
|
|
return (
|
|
diffMap.has(fieldKey) &&
|
|
diffMap.get(fieldKey)?.type === 'removed'
|
|
);
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const checkIfNewField = useCallback<DiffContext['checkIfNewField']>(
|
|
({ fieldId }) => {
|
|
const fieldKey = getDiffMapKey({
|
|
diffObject: 'field',
|
|
objectId: fieldId,
|
|
});
|
|
|
|
return (
|
|
diffMap.has(fieldKey) && diffMap.get(fieldKey)?.type === 'added'
|
|
);
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const getFieldNewName = useCallback<DiffContext['getFieldNewName']>(
|
|
({ fieldId }) => {
|
|
const fieldKey = getDiffMapKey({
|
|
diffObject: 'field',
|
|
objectId: fieldId,
|
|
attribute: 'name',
|
|
});
|
|
|
|
if (diffMap.has(fieldKey)) {
|
|
const diff = diffMap.get(fieldKey);
|
|
|
|
if (diff?.type === 'changed') {
|
|
return {
|
|
old: diff.oldValue as string,
|
|
new: diff.newValue as string,
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const getFieldNewType = useCallback<DiffContext['getFieldNewType']>(
|
|
({ fieldId }) => {
|
|
const fieldKey = getDiffMapKey({
|
|
diffObject: 'field',
|
|
objectId: fieldId,
|
|
attribute: 'type',
|
|
});
|
|
|
|
if (diffMap.has(fieldKey)) {
|
|
const diff = diffMap.get(fieldKey);
|
|
|
|
if (diff?.type === 'changed') {
|
|
return {
|
|
old: diff.oldValue as DataType,
|
|
new: diff.newValue as DataType,
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const getFieldNewPrimaryKey = useCallback<
|
|
DiffContext['getFieldNewPrimaryKey']
|
|
>(
|
|
({ fieldId }) => {
|
|
const fieldKey = getDiffMapKey({
|
|
diffObject: 'field',
|
|
objectId: fieldId,
|
|
attribute: 'primaryKey',
|
|
});
|
|
|
|
if (diffMap.has(fieldKey)) {
|
|
const diff = diffMap.get(fieldKey);
|
|
|
|
if (diff?.type === 'changed') {
|
|
return {
|
|
old: diff.oldValue as boolean,
|
|
new: diff.newValue as boolean,
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const getFieldNewNullable = useCallback<DiffContext['getFieldNewNullable']>(
|
|
({ fieldId }) => {
|
|
const fieldKey = getDiffMapKey({
|
|
diffObject: 'field',
|
|
objectId: fieldId,
|
|
attribute: 'nullable',
|
|
});
|
|
|
|
if (diffMap.has(fieldKey)) {
|
|
const diff = diffMap.get(fieldKey);
|
|
|
|
if (diff?.type === 'changed') {
|
|
return {
|
|
old: diff.oldValue as boolean,
|
|
new: diff.newValue as boolean,
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const getFieldNewCharacterMaximumLength = useCallback<
|
|
DiffContext['getFieldNewCharacterMaximumLength']
|
|
>(
|
|
({ fieldId }) => {
|
|
const fieldKey = getDiffMapKey({
|
|
diffObject: 'field',
|
|
objectId: fieldId,
|
|
attribute: 'characterMaximumLength',
|
|
});
|
|
|
|
if (diffMap.has(fieldKey)) {
|
|
const diff = diffMap.get(fieldKey);
|
|
|
|
if (diff?.type === 'changed') {
|
|
return {
|
|
old: diff.oldValue as string,
|
|
new: diff.newValue as string,
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const getFieldNewScale = useCallback<DiffContext['getFieldNewScale']>(
|
|
({ fieldId }) => {
|
|
const fieldKey = getDiffMapKey({
|
|
diffObject: 'field',
|
|
objectId: fieldId,
|
|
attribute: 'scale',
|
|
});
|
|
|
|
if (diffMap.has(fieldKey)) {
|
|
const diff = diffMap.get(fieldKey);
|
|
|
|
if (diff?.type === 'changed') {
|
|
return {
|
|
old: diff.oldValue as number,
|
|
new: diff.newValue as number,
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const getFieldNewPrecision = useCallback<
|
|
DiffContext['getFieldNewPrecision']
|
|
>(
|
|
({ fieldId }) => {
|
|
const fieldKey = getDiffMapKey({
|
|
diffObject: 'field',
|
|
objectId: fieldId,
|
|
attribute: 'precision',
|
|
});
|
|
|
|
if (diffMap.has(fieldKey)) {
|
|
const diff = diffMap.get(fieldKey);
|
|
|
|
if (diff?.type === 'changed') {
|
|
return {
|
|
old: diff.oldValue as number,
|
|
new: diff.newValue as number,
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const checkIfNewRelationship = useCallback<
|
|
DiffContext['checkIfNewRelationship']
|
|
>(
|
|
({ relationshipId }) => {
|
|
const relationshipKey = getDiffMapKey({
|
|
diffObject: 'relationship',
|
|
objectId: relationshipId,
|
|
});
|
|
|
|
return (
|
|
diffMap.has(relationshipKey) &&
|
|
diffMap.get(relationshipKey)?.type === 'added'
|
|
);
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const checkIfRelationshipRemoved = useCallback<
|
|
DiffContext['checkIfRelationshipRemoved']
|
|
>(
|
|
({ relationshipId }) => {
|
|
const relationshipKey = getDiffMapKey({
|
|
diffObject: 'relationship',
|
|
objectId: relationshipId,
|
|
});
|
|
|
|
return (
|
|
diffMap.has(relationshipKey) &&
|
|
diffMap.get(relationshipKey)?.type === 'removed'
|
|
);
|
|
},
|
|
[diffMap]
|
|
);
|
|
|
|
const resetDiff = useCallback<DiffContext['resetDiff']>(() => {
|
|
setDiffMap(new Map<string, ChartDBDiff>());
|
|
setTablesChanged(new Map<string, boolean>());
|
|
setFieldsChanged(new Map<string, boolean>());
|
|
setNewDiagram(null);
|
|
setOriginalDiagram(null);
|
|
setIsSummaryOnly(false);
|
|
}, []);
|
|
|
|
return (
|
|
<diffContext.Provider
|
|
value={{
|
|
newDiagram,
|
|
originalDiagram,
|
|
diffMap,
|
|
hasDiff: diffMap.size > 0,
|
|
isSummaryOnly,
|
|
|
|
calculateDiff,
|
|
resetDiff,
|
|
|
|
// table diff
|
|
getTableNewName,
|
|
checkIfNewTable,
|
|
checkIfTableRemoved,
|
|
checkIfTableHasChange,
|
|
getTableNewColor,
|
|
|
|
// field diff
|
|
checkIfFieldHasChange,
|
|
checkIfFieldRemoved,
|
|
checkIfNewField,
|
|
getFieldNewName,
|
|
getFieldNewType,
|
|
getFieldNewPrimaryKey,
|
|
getFieldNewNullable,
|
|
getFieldNewCharacterMaximumLength,
|
|
getFieldNewScale,
|
|
getFieldNewPrecision,
|
|
|
|
// relationship diff
|
|
checkIfNewRelationship,
|
|
checkIfRelationshipRemoved,
|
|
|
|
events,
|
|
}}
|
|
>
|
|
{children}
|
|
</diffContext.Provider>
|
|
);
|
|
};
|