diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.test.ts index 3702b46fdd790..6e3198f153df1 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.test.ts @@ -267,4 +267,36 @@ describe('validateReferences()', () => { 'Error fetching references for imported objects' ); }); + + // test that when references are missing returns only deduplicated errors + test('returns only deduplicated errors when references are missing', async () => { + const params = setup({ + objects: [ + { + id: '2', + type: 'visualization', + attributes: { title: 'My Visualization 2' }, + references: [ + { name: 'ref_0', type: 'index-pattern', id: '3' }, + { name: 'ref_0', type: 'index-pattern', id: '3' }, + ], + }, + ], + }); + params.savedObjectsClient.bulkGet.mockResolvedValue({ + saved_objects: [createNotFoundError({ type: 'index-pattern', id: '3' })], + }); + + const result = await validateReferences(params); + expect(result).toEqual([ + expect.objectContaining({ + type: 'visualization', + id: '2', + error: { + type: 'missing_references', + references: [{ type: 'index-pattern', id: '3' }], + }, + }), + ]); + }); }); diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.ts index e83fafe3348f7..b482bceb8ae0a 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.ts @@ -102,30 +102,35 @@ export async function validateReferences(params: ValidateReferencesParams) { const nonExistingReferenceKeys = await getNonExistingReferenceAsKeys(params); // Filter out objects with missing references, add to error object - objects.forEach(({ type, id, references, attributes }) => { - if (objectsToSkip.has(`${type}:${id}`)) { + for (const obj of objects) { + const { type, id, references, attributes } = obj; + const objectKey = `${type}:${id}`; + if (objectsToSkip.has(objectKey)) { // skip objects with retries that have specified `ignoreMissingReferences` - return; + continue; } - const missingReferences = []; - const enforcedTypeReferences = (references || []).filter(filterReferencesToValidate); + const missingReferences: Array<{ type: string; id: string }> = []; + const enforcedTypeReferences = references?.filter(filterReferencesToValidate) || []; + + const seenReferences = new Set(); for (const { type: refType, id: refId } of enforcedTypeReferences) { - if (nonExistingReferenceKeys.includes(`${refType}:${refId}`)) { + const refKey = `${refType}:${refId}`; + + if (nonExistingReferenceKeys.includes(refKey) && !seenReferences.has(refKey)) { missingReferences.push({ type: refType, id: refId }); + seenReferences.add(refKey); } } - if (missingReferences.length === 0) { - return; + if (missingReferences.length > 0) { + errorMap[objectKey] = { + id, + type, + meta: { title: attributes.title }, + error: { type: 'missing_references', references: missingReferences }, + }; } - const { title } = attributes; - errorMap[`${type}:${id}`] = { - id, - type, - meta: { title }, - error: { type: 'missing_references', references: missingReferences }, - }; - }); + } return Object.values(errorMap); }