mirror of
				https://github.com/chartdb/chartdb.git
				synced 2025-11-04 05:53:15 +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>
 | 
						|
    );
 | 
						|
};
 |