From e7240a00a7be4b36b57bafe0c9148b197361419d Mon Sep 17 00:00:00 2001 From: Ronald Arias Date: Wed, 4 Dec 2024 14:59:40 -0500 Subject: [PATCH 1/3] add object diff to log with duplicate entity report --- CHANGELOG.md | 5 + .../src/execution/duplicateKeyTracker.test.ts | 110 ++++++++++++++++ .../src/execution/duplicateKeyTracker.ts | 124 +++++++++++++++++- 3 files changed, 235 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bab17024d..fd379d250 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to # Unreleased +# 15.2.0 - 2024-12-04 + +- runtime: Add diff object in the duplicate entity report for rawData and + properties mismatch + # 15.1.0 - 2024-11-21 - http-client: added retryCalculateDelay method to allow for custom delay diff --git a/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.test.ts b/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.test.ts index 23de3cb2f..2065e4a8f 100644 --- a/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.test.ts +++ b/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.test.ts @@ -1,6 +1,7 @@ import { InMemoryDuplicateKeyTracker, createDuplicateEntityReport, + diffObjects, } from './duplicateKeyTracker'; import { InMemoryGraphObjectStore } from '../storage'; import { Entity } from '@jupiterone/integration-sdk-core'; @@ -132,6 +133,10 @@ describe('createDuplicateEntityReport', () => { _key: 'test-key', propertiesMatch: false, rawDataMatch: true, + propertiesDiff: JSON.stringify({ + _class: { type: 'value_mismatch' }, + _type: { type: 'value_mismatch' }, + }), }); }); @@ -185,6 +190,9 @@ describe('createDuplicateEntityReport', () => { _key: 'test-key', propertiesMatch: true, rawDataMatch: false, + rawDataDiff: JSON.stringify({ + data: { type: 'missing_in_src' }, + }), }); }); @@ -241,6 +249,108 @@ describe('createDuplicateEntityReport', () => { _key: 'test-key', propertiesMatch: true, rawDataMatch: false, + rawDataDiff: JSON.stringify({ + data: { type: 'missing_in_src' }, + }), }); }); }); + +describe('diffObjects', () => { + test('returns an empty diff for identical objects', () => { + const src = { name: 'Alice', age: 30 }; + const dest = { name: 'Alice', age: 30 }; + + expect(diffObjects(src, dest)).toEqual({}); + }); + + test('detects missing keys in src', () => { + const src = { name: 'Alice' }; + const dest = { name: 'Alice', age: 30 }; + + expect(diffObjects(src, dest)).toEqual({ + age: { type: 'missing_in_src' }, + }); + }); + + test('detects missing keys in dest', () => { + const src = { name: 'Alice', age: 30 }; + const dest = { name: 'Alice' }; + + expect(diffObjects(src, dest)).toEqual({ + age: { type: 'missing_in_dest' }, + }); + }); + + test('detects type mismatches', () => { + const src = { age: 30 }; + const dest = { age: '30' }; + + expect(diffObjects(src, dest)).toEqual({ + age: { + type: 'type_mismatch', + valueTypes: { src: 'number', dest: 'string' }, + }, + }); + }); + + test('detects value mismatches', () => { + const src = { age: 30 }; + const dest = { age: 31 }; + + expect(diffObjects(src, dest)).toEqual({ + age: { type: 'value_mismatch' }, + }); + }); + + test('handles nested object differences', () => { + const src = { user: { name: 'Alice', age: 30 } }; + const dest = { user: { name: 'Alice', age: 31 } }; + + expect(diffObjects(src, dest)).toEqual({ + 'user.age': { type: 'value_mismatch' }, + }); + }); + + test('handles missing nested keys in src', () => { + const src = { user: { name: 'Alice' } }; + const dest = { user: { name: 'Alice', age: 30 } }; + + expect(diffObjects(src, dest)).toEqual({ + 'user.age': { type: 'missing_in_src' }, + }); + }); + + test('handles missing nested keys in dest', () => { + const src = { user: { name: 'Alice', age: 30 } }; + const dest = { user: { name: 'Alice' } }; + + expect(diffObjects(src, dest)).toEqual({ + 'user.age': { type: 'missing_in_dest' }, + }); + }); + + test('handles array comparison', () => { + const src = { tags: ['a', 'b', 'c'], other: ['a', 'b', 'c'] }; + const dest = { tags: ['a', 'b', 'd'], other: ['a', 'b', 'c'] }; + + expect(diffObjects(src, dest)).toEqual({ + tags: { + type: 'value_mismatch', + }, + }); + }); + + test('handles empty objects', () => { + const src = {}; + const dest = {}; + + expect(diffObjects(src, dest)).toEqual({}); + }); + + test('handles null and undefined objects', () => { + expect(diffObjects(null, undefined)).toEqual({}); + expect(diffObjects(undefined, {})).toEqual({}); + expect(diffObjects({}, null)).toEqual({}); + }); +}); diff --git a/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.ts b/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.ts index 676cd59fd..37ae752e9 100644 --- a/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.ts +++ b/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.ts @@ -132,8 +132,98 @@ export type DuplicateEntityReport = { _key: string; rawDataMatch: boolean; propertiesMatch: boolean; + rawDataDiff?: string; + propertiesDiff?: string; }; +type DiffType = + | 'missing_in_src' + | 'missing_in_dest' + | 'type_mismatch' + | 'value_mismatch'; + +interface ObjectDiff { + [key: string]: { + type: DiffType; + valueTypes?: { src: string; dest: string }; + }; +} + +/** + * Compares two objects and returns the differences between them. + * + * @param {unknown} src - The source object to compare. + * @param {unknown} dest - The destination object to compare. + * @param {string} [path=''] - The base path for keys, used for tracking nested object differences. + * @returns {ObjectDiff} An object representing the differences between `src` and `dest`. + * Each key corresponds to a path in the objects, with details about the type of difference. + * + * @example + * const src = { a: 1, b: { c: 2 } }; + * const dest = { a: 1, b: { c: 3 }, d: 4 }; + * const result = diffObjects(src, dest); + * console.log(result); + * // Output: + * // { + * // "b.c": { type: "value_mismatch" }, + * // "d": { type: "missing_in_src" } + * // } + */ +export function diffObjects( + src: unknown, + dest: unknown, + path: string = '', +): ObjectDiff { + const diff = {}; + + // Helper to add differences + const addDiff = ( + key: string, + type: DiffType, + valueTypes?: { src: string; dest: string }, + ) => { + diff[key] = { type, valueTypes }; + }; + + // Iterate through the keys of both objects + const allKeys = new Set([ + ...Object.keys(src || {}), + ...Object.keys(dest || {}), + ]); + + const isObject = (val: unknown): val is Record => + typeof val === 'object' && val !== null; + + for (const key of allKeys) { + const fullPath = path ? `${path}.${key}` : key; + const valSrc = src?.[key]; + const valDest = dest?.[key]; + + if (valSrc === undefined) { + addDiff(fullPath, 'missing_in_src'); + } else if (valDest === undefined) { + addDiff(fullPath, 'missing_in_dest'); + } else if (typeof valSrc !== typeof valDest) { + addDiff(fullPath, 'type_mismatch', { + src: typeof valSrc, + dest: typeof valDest, + }); + } else if (Array.isArray(valSrc) && Array.isArray(valDest)) { + if (JSON.stringify(valSrc) !== JSON.stringify(valDest)) { + addDiff(fullPath, 'value_mismatch'); + } + } else if (isObject(valSrc) && isObject(valDest)) { + // Recursive comparison for nested objects + const nestedDiff = diffObjects(valSrc, valDest, fullPath); + Object.assign(diff, nestedDiff); + } else if (valSrc !== valDest) { + addDiff(fullPath, 'value_mismatch'); + } + } + + return diff; +} + /** * compareEntities compares two entities and produces a DuplicateEntityReport describing their * similarities and differences. @@ -144,12 +234,38 @@ export type DuplicateEntityReport = { function compareEntities(a: Entity, b: Entity): DuplicateEntityReport { const aClone = JSON.parse(JSON.stringify(a)); const bClone = JSON.parse(JSON.stringify(b)); - aClone._rawData = undefined; - bClone._rawData = undefined; + delete aClone._rawData; + delete bClone._rawData; + + const rawDataMatch = isDeepStrictEqual(a._rawData, b._rawData); + const propertiesMatch = isDeepStrictEqual(aClone, bClone); + + let rawDataDiff: ObjectDiff | undefined; + if (!rawDataMatch) { + try { + rawDataDiff = diffObjects( + a._rawData?.[0].rawData, + b._rawData?.[0].rawData, + ); + } catch (e) { + // ignore + } + } + + let propertiesDiff: ObjectDiff | undefined; + if (!propertiesMatch) { + try { + propertiesDiff = diffObjects(aClone, bClone); + } catch (e) { + // ignore + } + } return { _key: a._key, - rawDataMatch: isDeepStrictEqual(a._rawData, b._rawData), - propertiesMatch: isDeepStrictEqual(aClone, bClone), + rawDataMatch, + propertiesMatch, + ...(rawDataDiff && { rawDataDiff: JSON.stringify(rawDataDiff) }), + ...(propertiesDiff && { propertiesDiff: JSON.stringify(propertiesDiff) }), }; } From d7797fcb99938ba06fdbd962f7b92b7f14bbafe1 Mon Sep 17 00:00:00 2001 From: Ronald Arias Date: Fri, 6 Dec 2024 11:31:27 -0500 Subject: [PATCH 2/3] address review comments --- .../src/execution/duplicateKeyTracker.test.ts | 104 +++++++++--------- .../src/execution/duplicateKeyTracker.ts | 83 +++++++------- 2 files changed, 95 insertions(+), 92 deletions(-) diff --git a/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.test.ts b/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.test.ts index 2065e4a8f..0a08e53d6 100644 --- a/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.test.ts +++ b/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.test.ts @@ -74,7 +74,7 @@ describe('createDuplicateEntityReport', () => { expect(der).toMatchObject({ _key: 'test-key', - propertiesMatch: true, + entityPropertiesMatch: true, rawDataMatch: true, }); }); @@ -131,11 +131,11 @@ describe('createDuplicateEntityReport', () => { expect(der).toMatchObject({ _key: 'test-key', - propertiesMatch: false, + entityPropertiesMatch: false, rawDataMatch: true, - propertiesDiff: JSON.stringify({ - _class: { type: 'value_mismatch' }, - _type: { type: 'value_mismatch' }, + entityPropertiesDiff: JSON.stringify({ + _class: { diffType: 'array_values_mismatch' }, + _type: { diffType: 'value_mismatch' }, }), }); }); @@ -188,10 +188,10 @@ describe('createDuplicateEntityReport', () => { expect(der).toMatchObject({ _key: 'test-key', - propertiesMatch: true, + entityPropertiesMatch: true, rawDataMatch: false, rawDataDiff: JSON.stringify({ - data: { type: 'missing_in_src' }, + data: { diffType: 'missing_in_original' }, }), }); }); @@ -247,10 +247,10 @@ describe('createDuplicateEntityReport', () => { expect(der).toMatchObject({ _key: 'test-key', - propertiesMatch: true, + entityPropertiesMatch: true, rawDataMatch: false, rawDataDiff: JSON.stringify({ - data: { type: 'missing_in_src' }, + data: { diffType: 'missing_in_original' }, }), }); }); @@ -258,94 +258,94 @@ describe('createDuplicateEntityReport', () => { describe('diffObjects', () => { test('returns an empty diff for identical objects', () => { - const src = { name: 'Alice', age: 30 }; - const dest = { name: 'Alice', age: 30 }; + const original = { name: 'Alice', age: 30 }; + const duplicate = { name: 'Alice', age: 30 }; - expect(diffObjects(src, dest)).toEqual({}); + expect(diffObjects(original, duplicate)).toEqual({}); }); - test('detects missing keys in src', () => { - const src = { name: 'Alice' }; - const dest = { name: 'Alice', age: 30 }; + test('detects missing keys in original', () => { + const original = { name: 'Alice' }; + const duplicate = { name: 'Alice', age: 30 }; - expect(diffObjects(src, dest)).toEqual({ - age: { type: 'missing_in_src' }, + expect(diffObjects(original, duplicate)).toEqual({ + age: { diffType: 'missing_in_original' }, }); }); - test('detects missing keys in dest', () => { - const src = { name: 'Alice', age: 30 }; - const dest = { name: 'Alice' }; + test('detects missing keys in duplicate', () => { + const original = { name: 'Alice', age: 30 }; + const duplicate = { name: 'Alice' }; - expect(diffObjects(src, dest)).toEqual({ - age: { type: 'missing_in_dest' }, + expect(diffObjects(original, duplicate)).toEqual({ + age: { diffType: 'missing_in_duplicate' }, }); }); test('detects type mismatches', () => { - const src = { age: 30 }; - const dest = { age: '30' }; + const original = { age: 30 }; + const duplicate = { age: '30' }; - expect(diffObjects(src, dest)).toEqual({ + expect(diffObjects(original, duplicate)).toEqual({ age: { - type: 'type_mismatch', - valueTypes: { src: 'number', dest: 'string' }, + diffType: 'type_mismatch', + valueTypes: { original: 'number', duplicate: 'string' }, }, }); }); test('detects value mismatches', () => { - const src = { age: 30 }; - const dest = { age: 31 }; + const original = { age: 30 }; + const duplicate = { age: 31 }; - expect(diffObjects(src, dest)).toEqual({ - age: { type: 'value_mismatch' }, + expect(diffObjects(original, duplicate)).toEqual({ + age: { diffType: 'value_mismatch' }, }); }); test('handles nested object differences', () => { - const src = { user: { name: 'Alice', age: 30 } }; - const dest = { user: { name: 'Alice', age: 31 } }; + const original = { user: { name: 'Alice', age: 30 } }; + const duplicate = { user: { name: 'Alice', age: 31 } }; - expect(diffObjects(src, dest)).toEqual({ - 'user.age': { type: 'value_mismatch' }, + expect(diffObjects(original, duplicate)).toEqual({ + 'user.age': { diffType: 'value_mismatch' }, }); }); - test('handles missing nested keys in src', () => { - const src = { user: { name: 'Alice' } }; - const dest = { user: { name: 'Alice', age: 30 } }; + test('handles missing nested keys in original', () => { + const original = { user: { name: 'Alice' } }; + const duplicate = { user: { name: 'Alice', age: 30 } }; - expect(diffObjects(src, dest)).toEqual({ - 'user.age': { type: 'missing_in_src' }, + expect(diffObjects(original, duplicate)).toEqual({ + 'user.age': { diffType: 'missing_in_original' }, }); }); - test('handles missing nested keys in dest', () => { - const src = { user: { name: 'Alice', age: 30 } }; - const dest = { user: { name: 'Alice' } }; + test('handles missing nested keys in duplicate', () => { + const original = { user: { name: 'Alice', age: 30 } }; + const duplicate = { user: { name: 'Alice' } }; - expect(diffObjects(src, dest)).toEqual({ - 'user.age': { type: 'missing_in_dest' }, + expect(diffObjects(original, duplicate)).toEqual({ + 'user.age': { diffType: 'missing_in_duplicate' }, }); }); test('handles array comparison', () => { - const src = { tags: ['a', 'b', 'c'], other: ['a', 'b', 'c'] }; - const dest = { tags: ['a', 'b', 'd'], other: ['a', 'b', 'c'] }; + const original = { tags: ['a', 'b', 'c'], other: ['a', 'b', 'c'] }; + const duplicate = { tags: ['a', 'b', 'd'], other: ['a', 'b', 'c'] }; - expect(diffObjects(src, dest)).toEqual({ + expect(diffObjects(original, duplicate)).toEqual({ tags: { - type: 'value_mismatch', + diffType: 'array_values_mismatch', }, }); }); test('handles empty objects', () => { - const src = {}; - const dest = {}; + const original = {}; + const duplicate = {}; - expect(diffObjects(src, dest)).toEqual({}); + expect(diffObjects(original, duplicate)).toEqual({}); }); test('handles null and undefined objects', () => { diff --git a/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.ts b/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.ts index 37ae752e9..53606c86a 100644 --- a/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.ts +++ b/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.ts @@ -131,16 +131,17 @@ function isDeepStrictEqual(a: any, b: any): boolean { export type DuplicateEntityReport = { _key: string; rawDataMatch: boolean; - propertiesMatch: boolean; + entityPropertiesMatch: boolean; rawDataDiff?: string; - propertiesDiff?: string; + entityPropertiesDiff?: string; }; type DiffType = - | 'missing_in_src' - | 'missing_in_dest' + | 'missing_in_original' + | 'missing_in_duplicate' | 'type_mismatch' - | 'value_mismatch'; + | 'value_mismatch' + | 'array_values_mismatch'; interface ObjectDiff { [key: string]: { @@ -152,26 +153,26 @@ interface ObjectDiff { /** * Compares two objects and returns the differences between them. * - * @param {unknown} src - The source object to compare. - * @param {unknown} dest - The destination object to compare. + * @param {unknown} originalObject - The source object to compare. + * @param {unknown} duplicateObject - The destination object to compare. * @param {string} [path=''] - The base path for keys, used for tracking nested object differences. - * @returns {ObjectDiff} An object representing the differences between `src` and `dest`. + * @returns {ObjectDiff} An object representing the differences between `original` and `duplicate`. * Each key corresponds to a path in the objects, with details about the type of difference. * * @example - * const src = { a: 1, b: { c: 2 } }; - * const dest = { a: 1, b: { c: 3 }, d: 4 }; - * const result = diffObjects(src, dest); + * const originalObj = { a: 1, b: { c: 2 } }; + * const duplicateObj = { a: 1, b: { c: 3 }, d: 4 }; + * const result = diffObjects(originalObj, duplicateObj); * console.log(result); * // Output: * // { * // "b.c": { type: "value_mismatch" }, - * // "d": { type: "missing_in_src" } + * // "d": { type: "missing_in_original" } * // } */ export function diffObjects( - src: unknown, - dest: unknown, + originalObject: unknown, + duplicateObject: unknown, path: string = '', ): ObjectDiff { const diff = {}; @@ -179,16 +180,16 @@ export function diffObjects( // Helper to add differences const addDiff = ( key: string, - type: DiffType, - valueTypes?: { src: string; dest: string }, + diffType: DiffType, + valueTypes?: { original: string; duplicate: string }, ) => { - diff[key] = { type, valueTypes }; + diff[key] = { diffType, valueTypes }; }; // Iterate through the keys of both objects const allKeys = new Set([ - ...Object.keys(src || {}), - ...Object.keys(dest || {}), + ...Object.keys(originalObject || {}), + ...Object.keys(duplicateObject || {}), ]); const isObject = (val: unknown): val is Record => @@ -196,27 +197,27 @@ export function diffObjects( for (const key of allKeys) { const fullPath = path ? `${path}.${key}` : key; - const valSrc = src?.[key]; - const valDest = dest?.[key]; + const valOriginal = originalObject?.[key]; + const valDuplicate = duplicateObject?.[key]; - if (valSrc === undefined) { - addDiff(fullPath, 'missing_in_src'); - } else if (valDest === undefined) { - addDiff(fullPath, 'missing_in_dest'); - } else if (typeof valSrc !== typeof valDest) { + if (valOriginal === undefined) { + addDiff(fullPath, 'missing_in_original'); + } else if (valDuplicate === undefined) { + addDiff(fullPath, 'missing_in_duplicate'); + } else if (typeof valOriginal !== typeof valDuplicate) { addDiff(fullPath, 'type_mismatch', { - src: typeof valSrc, - dest: typeof valDest, + original: typeof valOriginal, + duplicate: typeof valDuplicate, }); - } else if (Array.isArray(valSrc) && Array.isArray(valDest)) { - if (JSON.stringify(valSrc) !== JSON.stringify(valDest)) { - addDiff(fullPath, 'value_mismatch'); + } else if (Array.isArray(valOriginal) && Array.isArray(valDuplicate)) { + if (JSON.stringify(valOriginal) !== JSON.stringify(valDuplicate)) { + addDiff(fullPath, 'array_values_mismatch'); } - } else if (isObject(valSrc) && isObject(valDest)) { + } else if (isObject(valOriginal) && isObject(valDuplicate)) { // Recursive comparison for nested objects - const nestedDiff = diffObjects(valSrc, valDest, fullPath); + const nestedDiff = diffObjects(valOriginal, valDuplicate, fullPath); Object.assign(diff, nestedDiff); - } else if (valSrc !== valDest) { + } else if (valOriginal !== valDuplicate) { addDiff(fullPath, 'value_mismatch'); } } @@ -238,7 +239,7 @@ function compareEntities(a: Entity, b: Entity): DuplicateEntityReport { delete bClone._rawData; const rawDataMatch = isDeepStrictEqual(a._rawData, b._rawData); - const propertiesMatch = isDeepStrictEqual(aClone, bClone); + const entityPropertiesMatch = isDeepStrictEqual(aClone, bClone); let rawDataDiff: ObjectDiff | undefined; if (!rawDataMatch) { @@ -252,10 +253,10 @@ function compareEntities(a: Entity, b: Entity): DuplicateEntityReport { } } - let propertiesDiff: ObjectDiff | undefined; - if (!propertiesMatch) { + let entityPropertiesDiff: ObjectDiff | undefined; + if (!entityPropertiesMatch) { try { - propertiesDiff = diffObjects(aClone, bClone); + entityPropertiesDiff = diffObjects(aClone, bClone); } catch (e) { // ignore } @@ -264,8 +265,10 @@ function compareEntities(a: Entity, b: Entity): DuplicateEntityReport { return { _key: a._key, rawDataMatch, - propertiesMatch, + entityPropertiesMatch, ...(rawDataDiff && { rawDataDiff: JSON.stringify(rawDataDiff) }), - ...(propertiesDiff && { propertiesDiff: JSON.stringify(propertiesDiff) }), + ...(entityPropertiesDiff && { + entityPropertiesDiff: JSON.stringify(entityPropertiesDiff), + }), }; } From 1fde8d60ab2d729bf5d6b94191c3c7afda1f341f Mon Sep 17 00:00:00 2001 From: Ronald Arias Date: Mon, 9 Dec 2024 12:16:10 -0500 Subject: [PATCH 3/3] add error messages to duplicate entity report --- .../src/execution/duplicateKeyTracker.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.ts b/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.ts index 53606c86a..227e27d17 100644 --- a/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.ts +++ b/packages/integration-sdk-runtime/src/execution/duplicateKeyTracker.ts @@ -134,6 +134,7 @@ export type DuplicateEntityReport = { entityPropertiesMatch: boolean; rawDataDiff?: string; entityPropertiesDiff?: string; + diffErrors?: { rawData?: string; entityProperties?: string }; }; type DiffType = @@ -241,6 +242,8 @@ function compareEntities(a: Entity, b: Entity): DuplicateEntityReport { const rawDataMatch = isDeepStrictEqual(a._rawData, b._rawData); const entityPropertiesMatch = isDeepStrictEqual(aClone, bClone); + const diffErrors: { rawData?: string; entityProperties?: string } = {}; + let rawDataDiff: ObjectDiff | undefined; if (!rawDataMatch) { try { @@ -249,7 +252,7 @@ function compareEntities(a: Entity, b: Entity): DuplicateEntityReport { b._rawData?.[0].rawData, ); } catch (e) { - // ignore + diffErrors.rawData = e.message; } } @@ -258,7 +261,7 @@ function compareEntities(a: Entity, b: Entity): DuplicateEntityReport { try { entityPropertiesDiff = diffObjects(aClone, bClone); } catch (e) { - // ignore + diffErrors.entityProperties = e.message; } } @@ -270,5 +273,6 @@ function compareEntities(a: Entity, b: Entity): DuplicateEntityReport { ...(entityPropertiesDiff && { entityPropertiesDiff: JSON.stringify(entityPropertiesDiff), }), + ...(Object.keys(diffErrors).length > 0 && { diffErrors }), }; }