private identifyChangedRelations()

in source/packages/services/assetlibrary/src/types/types.full.dao.ts [491:672]


    private identifyChangedRelations(
        existing: TypeRelationsModel,
        updated: TypeRelationsModel
    ): ChangedRelations {
        logger.debug(
            `types.full.dao identifyChangedRelations: in: existing: ${JSON.stringify(
                existing
            )}, updated: ${JSON.stringify(updated)}`
        );

        // before we diff, sort the relations.  this avoids reporting any unnecessary modifications where just the ordering may have changed
        const sortRelationTarget = (a: RelationTarget, b: RelationTarget) => {
            const aName = isRelationTargetExpanded(a) ? a.name : a;
            const bName = isRelationTargetExpanded(b) ? b.name : b;
            return aName.localeCompare(bName);
        };
        if (existing?.in) {
            for (const key of Object.keys(existing.in)) {
                existing.in[key] = existing.in[key].sort(sortRelationTarget);
            }
        }
        if (existing?.out) {
            for (const key of Object.keys(existing.out)) {
                existing.out[key] = existing.out[key].sort(sortRelationTarget);
            }
        }
        if (updated?.in) {
            for (const key of Object.keys(updated.in)) {
                updated.in[key] = updated.in[key].sort(sortRelationTarget);
            }
        }
        if (updated?.out) {
            for (const key of Object.keys(updated.out)) {
                updated.out[key] = updated.out[key].sort(sortRelationTarget);
            }
        }

        // perform a diff of the relations so we can determine what specifically to add and remove
        logger.silly(
            `types.full.dao identifyChangedRelations: sorted: existing: ${JSON.stringify(
                existing
            )}, updated: ${JSON.stringify(updated)}`
        );
        const diff = jsonpatch.compare(existing, updated);
        logger.silly(`types.full.dao identifyChangedRelations: diff: ${JSON.stringify(diff)}`);

        // based on the changes, build up the relation changes query
        const toRemove = new TypeRelationsModel();
        toRemove.in = {};
        toRemove.out = {};
        const toAdd = new TypeRelationsModel();
        toAdd.in = {};
        toAdd.out = {};
        const processed4Levels: string[] = [];
        diff.forEach((d) => {
            const p = d.path.split('/');

            if (p.length === 2) {
                //////////////////// entire in/out
                if (d.op === 'remove') {
                    if (p[1] === 'in') {
                        for (const key of Object.keys(existing.in)) {
                            toRemove.in[key] = existing['in'][key];
                        }
                    } else if (p[1] === 'out') {
                        for (const key of Object.keys(existing.out)) {
                            toRemove.out[key] = existing['out'][key];
                        }
                    }
                } else if (d.op === 'add') {
                    if (p[1] === 'in') {
                        for (const key of Object.keys(updated.in)) {
                            toAdd.in[key] = updated['in'][key];
                        }
                    } else if (p[1] === 'out') {
                        for (const key of Object.keys(updated.out)) {
                            toAdd.out[key] = updated['out'][key];
                        }
                    }
                } else {
                    logger.warn(
                        `types.full.dao identifyChangedRelations: Unsupported diff op: ${JSON.stringify(
                            d
                        )}`
                    );
                }
            } else if (p.length === 3) {
                //////////////////// specific relation changed
                if (d.op === 'remove') {
                    if (p[1] === 'in') {
                        toRemove.in[p[2]] = existing['in'][p[2]];
                    } else if (p[1] === 'out') {
                        toRemove.out[p[2]] = existing['out'][p[2]];
                    }
                } else if (d.op === 'add') {
                    if (p[1] === 'in') {
                        toAdd.in[p[2]] = updated['in'][p[2]];
                    } else if (p[1] === 'out') {
                        toAdd.out[p[2]] = updated['out'][p[2]];
                    }
                } else {
                    logger.warn(
                        `types.full.dao identifyChangedRelations: Unsupported diff op: ${JSON.stringify(
                            d
                        )}`
                    );
                }
            } else if (p.length === 4) {
                //////////////////// a target type of a relation has changed

                // the diff output isn't great to work with for array diffs, therefore calculate the diff ourselves
                if (processed4Levels.indexOf(`${p[1]}/${p[2]}`) < 0) {
                    processed4Levels.push(`${p[1]}/${p[2]}`);

                    const existingTypes =
                        p[1] === 'in' ? existing['in'][p[2]] : existing['out'][p[2]];
                    const updatedTypes =
                        p[1] === 'in' ? updated['in'][p[2]] : updated['out'][p[2]];

                    // first find removed
                    const findDifferences = (
                        sourceArray: RelationTarget[],
                        targetArray: RelationTarget[],
                        differences: TypeRelationsModel
                    ) => {
                        sourceArray.forEach((source) => {
                            const sourceIsExpanded = isRelationTargetExpanded(source);
                            const indexOf = (target: RelationTarget) => {
                                const targetIsExpanded = isRelationTargetExpanded(target);
                                if (sourceIsExpanded !== targetIsExpanded) {
                                    return false;
                                }
                                if (sourceIsExpanded) {
                                    return (
                                        (source as RelationTargetExpanded).name ===
                                        (target as RelationTargetExpanded).name
                                    );
                                } else {
                                    return source === target;
                                }
                            };
                            if (targetArray.findIndex(indexOf) < 0) {
                                if (p[1] === 'in') {
                                    if (differences.in[p[2]] === undefined) {
                                        differences.in[p[2]] = [];
                                    }
                                    differences.in[p[2]].push(source);
                                } else {
                                    if (differences.out[p[2]] === undefined) {
                                        differences.out[p[2]] = [];
                                    }
                                    differences.out[p[2]].push(source);
                                }
                            }
                        });
                    };
                    if (existingTypes) {
                        findDifferences(existingTypes, updatedTypes, toRemove);
                    }

                    // then find added
                    if (updatedTypes) {
                        findDifferences(updatedTypes, existingTypes, toAdd);
                    }
                }
            } else {
                logger.warn(
                    `types.full.dao identifyChangedRelations: Unsupported diff op: ${JSON.stringify(
                        d
                    )}`
                );
            }
        });

        const response = {
            add: toAdd,
            remove: toRemove,
        };

        logger.debug(`types.full.dao identifyChangedRelations: exit: ${JSON.stringify(response)}`);
        return response;
    }