From d94aa8d08af364c61a78090680896f9db4e4faaa Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Wed, 28 Feb 2024 02:17:41 +0100 Subject: [PATCH] [backend] Update upsert behavior for createdBy attribute - To prevent too much flickering on multi sources the created-by will be replaced only for strict upper confidence --- .../opencti-graphql/src/database/middleware.js | 15 +++++++++------ .../opencti-graphql/src/utils/confidence-level.ts | 2 ++ .../tests/01-unit/utils/confidence-level-test.ts | 11 +++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/opencti-platform/opencti-graphql/src/database/middleware.js b/opencti-platform/opencti-graphql/src/database/middleware.js index 3a3706e90457..c91a40010b41 100644 --- a/opencti-platform/opencti-graphql/src/database/middleware.js +++ b/opencti-platform/opencti-graphql/src/database/middleware.js @@ -2424,7 +2424,7 @@ const upsertElement = async (context, user, element, type, basePatch, opts = {}) inputs.push(fileImpact); } // region confidence control / upsert - const { confidenceLevelToApply, isConfidenceMatch } = controlUpsertInputWithUserConfidence(user, updatePatch, element); + const { confidenceLevelToApply, isConfidenceMatch, isConfidenceUpper } = controlUpsertInputWithUserConfidence(user, updatePatch, element); updatePatch.confidence = confidenceLevelToApply; // note that if the existing data has no confidence (null) it will still be updated below, even if isConfidenceMatch = false // endregion @@ -2487,13 +2487,16 @@ const upsertElement = async (context, user, element, type, basePatch, opts = {}) } else { // not multiple // If expected data is different from current data... const currentData = element[relDef.databaseName]; + const isCurrentEmptyData = isEmptyField(currentData); const isInputDifferentFromCurrent = !R.equals(currentData, patchInputData); // ... and data can be updated: - // forced synchro - // OR the field was null -> better than nothing ! - // OR the confidence matches -> new value is "better" than existing value - const updatable = isUpsertSynchro || (isInputWithData && isEmptyField(currentData)) || isConfidenceMatch; - if (isInputDifferentFromCurrent && updatable) { + // forced synchro + // OR the field is currently null (auto consolidation) + // OR the confidence matches + // To prevent too much flickering on multi sources the created-by will be replaced only for strict upper confidence + const isProtectedCreatedBy = relDef.databaseName === RELATION_CREATED_BY && !isCurrentEmptyData && !isConfidenceUpper; + const updatable = isUpsertSynchro || (isInputWithData && isCurrentEmptyData) || isConfidenceMatch; + if (isInputDifferentFromCurrent && updatable && !isProtectedCreatedBy) { inputs.push({ key: inputField, value: [patchInputData] }); } } diff --git a/opencti-platform/opencti-graphql/src/utils/confidence-level.ts b/opencti-platform/opencti-graphql/src/utils/confidence-level.ts index b501fb1662f6..3dd5f062019c 100644 --- a/opencti-platform/opencti-graphql/src/utils/confidence-level.ts +++ b/opencti-platform/opencti-graphql/src/utils/confidence-level.ts @@ -108,10 +108,12 @@ export const controlUpsertInputWithUserConfidence = = existing; // always true if no existingConfidence + const isConfidenceUpper = confidenceLevelToApply > existing; return { confidenceLevelToApply, isConfidenceMatch, + isConfidenceUpper }; }; diff --git a/opencti-platform/opencti-graphql/tests/01-unit/utils/confidence-level-test.ts b/opencti-platform/opencti-graphql/tests/01-unit/utils/confidence-level-test.ts index 63a814943f51..d13179ac5673 100644 --- a/opencti-platform/opencti-graphql/tests/01-unit/utils/confidence-level-test.ts +++ b/opencti-platform/opencti-graphql/tests/01-unit/utils/confidence-level-test.ts @@ -157,56 +157,67 @@ describe('Confidence level utilities', () => { .toEqual({ isConfidenceMatch: true, confidenceLevelToApply: 30, + isConfidenceUpper: true, }); expect(controlUpsertInputWithUserConfidence(makeUser(50), makeElement(10), makeElement(30))) .toEqual({ isConfidenceMatch: false, confidenceLevelToApply: 10, + isConfidenceUpper: false, }); expect(controlUpsertInputWithUserConfidence(makeUser(30), makeElement(50), makeElement(10))) .toEqual({ isConfidenceMatch: true, confidenceLevelToApply: 30, + isConfidenceUpper: true, }); expect(controlUpsertInputWithUserConfidence(makeUser(30), makeElement(10), makeElement(50))) .toEqual({ isConfidenceMatch: false, confidenceLevelToApply: 10, + isConfidenceUpper: false, }); expect(controlUpsertInputWithUserConfidence(makeUser(10), makeElement(50), makeElement(30))) .toEqual({ isConfidenceMatch: false, confidenceLevelToApply: 10, + isConfidenceUpper: false, }); expect(controlUpsertInputWithUserConfidence(makeUser(10), makeElement(30), makeElement(50))) .toEqual({ isConfidenceMatch: false, confidenceLevelToApply: 10, + isConfidenceUpper: false, }); expect(controlUpsertInputWithUserConfidence(makeUser(50), makeElement(null), makeElement(30))) .toEqual({ isConfidenceMatch: true, confidenceLevelToApply: 50, + isConfidenceUpper: true, }); expect(controlUpsertInputWithUserConfidence(makeUser(30), makeElement(null), makeElement(50))) .toEqual({ isConfidenceMatch: false, confidenceLevelToApply: 30, + isConfidenceUpper: false, }); expect(controlUpsertInputWithUserConfidence(makeUser(50), makeElement(30), makeElement(null))) .toEqual({ isConfidenceMatch: true, confidenceLevelToApply: 30, + isConfidenceUpper: true, }); expect(controlUpsertInputWithUserConfidence(makeUser(30), makeElement(50), makeElement(null))) .toEqual({ isConfidenceMatch: true, confidenceLevelToApply: 30, + isConfidenceUpper: true, }); expect(controlUpsertInputWithUserConfidence(makeUser(30), makeElement(null), makeElement(null))) .toEqual({ isConfidenceMatch: true, confidenceLevelToApply: 30, + isConfidenceUpper: true, }); expect(() => controlUpsertInputWithUserConfidence(makeUser(null), makeElement(30), makeElement(50))) .toThrowError('User has no effective max confidence level and cannot upsert this element');