Files
chartdb/src/context/diff-context/diff-provider.tsx
2025-09-28 17:02:56 +03:00

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>
);
};