Skip to content

Commit

Permalink
[backend] avoid useless operations during batched replace (#6297)
Browse files Browse the repository at this point in the history
Co-authored-by: Laurent Bonnet <laurent.bonnet@filigran.io>
  • Loading branch information
marieflorescontact and labo-flg committed Mar 19, 2024
1 parent d857cc0 commit 9c63947
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 15 deletions.
33 changes: 24 additions & 9 deletions opencti-platform/opencti-graphql/src/manager/taskManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,20 +239,35 @@ const executeRemove = async (context, user, actionContext, element) => {
await patchAttribute(context, user, element.id, element.entity_type, patch, { operations });
}
};
const executeReplace = async (context, user, actionContext, element) => {
export const executeReplace = async (context, user, actionContext, element) => {
const { field, type: contextType, values } = actionContext;
if (contextType === ACTION_TYPE_RELATION) {
const toCreate = [];
// 01 - Delete all relations of the element
const rels = await listAllRelations(context, user, field, { fromId: element.id });
for (let indexRel = 0; indexRel < rels.length; indexRel += 1) {
const rel = rels[indexRel];
await deleteElementById(context, user, rel.id, rel.entity_type);
}
// 02 - Create new ones
for (let indexCreate = 0; indexCreate < values.length; indexCreate += 1) {
const target = values[indexCreate];
await createRelation(context, user, { fromId: element.id, toId: target, relationship_type: field });
if (rels.length > 0) {
// Items to Delete
for (let index = 0; index < rels.length; index += 1) {
if (!values.includes(rels[index].toId)) {
const rel = rels[index];
await deleteElementById(context, user, rel.id, rel.entity_type);
}
}
// 02 - Create new ones
for (let indexCreate = 0; indexCreate < values.length; indexCreate += 1) {
const target = values[indexCreate];
if (!rels.some(({ toId }) => toId === target)) {
toCreate.push(target);
}
}
} else if (rels.length === 0) {
// 02 - Create relations if no relation to replace
for (let indexCreate = 0; indexCreate < values.length; indexCreate += 1) {
const target = values[indexCreate];
toCreate.push(target);
}
}
await Promise.all(toCreate.map(async (tc) => await createRelation(context, user, { fromId: element.id, toId: tc, relationship_type: field })));
}
if (contextType === ACTION_TYPE_ATTRIBUTE) {
const patch = generatePatch(field, values, element.entity_type);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { afterAll, describe, expect, it } from 'vitest';
import { executeReplace } from '../../../src/manager/taskManager';
import type { AuthContext } from '../../../src/types/user';
import { ADMIN_USER } from '../../utils/testQuery';
import { MARKING_TLP_CLEAR, MARKING_TLP_AMBER } from '../../../src/schema/identifier';
import { addReport, findById as findReportById } from '../../../src/domain/report';
import { findById } from '../../../src/domain/markingDefinition';
import { stixDomainObjectDelete } from '../../../src/domain/stixDomainObject';

describe('TaskManager executeReplace tests ', () => {
const adminContext: AuthContext = { user: ADMIN_USER, tracing: undefined, source: 'taskManager-integration-test', otp_mandatory: false };
const reportsId: string[] = [];
afterAll(async () => {
expect(reportsId.length).toBe(3);
for (let index = 0; index < reportsId.length; index += 1) {
await stixDomainObjectDelete(adminContext, adminContext.user, reportsId[index]);
const report = await findReportById(adminContext, adminContext.user, reportsId[index]);
expect(report).toBeUndefined();
}
});
it('REPLACE report marking with different marking', async () => {
const reportInput = {
name: 'test report marking with different marking',
objectMarking: [MARKING_TLP_CLEAR],
};
const report = await addReport(adminContext, adminContext.user, reportInput);

expect(report.id).toBeDefined();
reportsId.push(report.id);
const reportId = report.id;
const marking = await findById(adminContext, adminContext.user, MARKING_TLP_AMBER);

const actionContext = {
field: 'object-marking',
type: 'RELATION',
values: [marking.id]
};

await executeReplace(adminContext, adminContext.user, actionContext, report);
const reportAfterReplace = await findReportById(adminContext, adminContext.user, reportId);

const markings = reportAfterReplace['object-marking'];
expect(markings?.length).toEqual(1);
if (markings) {
const markingEntity = await findById(adminContext, adminContext.user, markings[0]);
expect(markingEntity.standard_id).toEqual(MARKING_TLP_AMBER);
}
});
it('REPLACE report marking with same marking', async () => {
const reportInput = {
name: 'test report marking with same marking',
objectMarking: [MARKING_TLP_CLEAR],
};
const report = await addReport(adminContext, adminContext.user, reportInput);
expect(report.id).toBeDefined();
reportsId.push(report.id);
const reportId = report.id;

const marking = await findById(adminContext, adminContext.user, MARKING_TLP_CLEAR);

const actionContext = {
field: 'object-marking',
type: 'RELATION',
values: [marking.id]
};

await executeReplace(adminContext, adminContext.user, actionContext, report);
const reportAfterReplace = await findReportById(adminContext, adminContext.user, reportId);

const markings = reportAfterReplace['object-marking'];
expect(markings?.length).toEqual(1);
if (markings) {
const markingEntity = await findById(adminContext, adminContext.user, markings[0]);
expect(markingEntity.standard_id).toEqual(MARKING_TLP_CLEAR);
}
});
it('REPLACE report no marking with marking', async () => {
const reportInput = {
name: 'test report no marking with marking',
objectMarking: [],
};
const report = await addReport(adminContext, adminContext.user, reportInput);
expect(report.id).toBeDefined();
reportsId.push(report.id);
const reportId = report.id;

const marking = await findById(adminContext, adminContext.user, MARKING_TLP_CLEAR);

const actionContext = {
field: 'object-marking',
type: 'RELATION',
values: [marking.id]
};

await executeReplace(adminContext, adminContext.user, actionContext, report);
const reportAfterReplace = await findReportById(adminContext, adminContext.user, reportId);

const markings = reportAfterReplace['object-marking'];
expect(markings?.length).toEqual(1);
if (markings) {
const markingEntity = await findById(adminContext, adminContext.user, markings[0]);
expect(markingEntity.standard_id).toEqual(MARKING_TLP_CLEAR);
}
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ describe('Raw streams tests', () => {
expect(createEventsByTypes.relationship.length).toBe(126);
expect(createEventsByTypes.indicator.length).toBe(33);
expect(createEventsByTypes['attack-pattern'].length).toBe(7);
expect(createEventsByTypes.report.length).toBe(19);
expect(createEventsByTypes.report.length).toBe(22);
expect(createEventsByTypes.tool.length).toBe(2);
expect(createEventsByTypes.vocabulary.length).toBe(335); // 328 created at init + 2 created in tests + 5 vocabulary organizations types
expect(createEventsByTypes.vulnerability.length).toBe(7);
expect(createEvents.length).toBe(720);
expect(createEvents.length).toBe(723);
for (let createIndex = 0; createIndex < createEvents.length; createIndex += 1) {
const { data: insideData, origin, type } = createEvents[createIndex];
expect(origin).toBeDefined();
Expand Down Expand Up @@ -58,14 +58,14 @@ describe('Raw streams tests', () => {
expect(updateEventsByTypes['malware-analysis'].length).toBe(3);
expect(updateEventsByTypes['note'].length).toBe(3);
expect(updateEventsByTypes['opinion'].length).toBe(6);
expect(updateEventsByTypes['report'].length).toBe(4);
expect(updateEventsByTypes['report'].length).toBe(7);
expect(updateEventsByTypes['ipv4-addr'].length).toBe(3);
expect(updateEventsByTypes['tool'].length).toBe(7);
expect(updateEventsByTypes['sighting'].length).toBe(4);
expect(updateEventsByTypes['threat-actor'].length).toBe(17);
expect(updateEventsByTypes['vocabulary'].length).toBe(3);
expect(updateEventsByTypes['vulnerability'].length).toBe(3);
expect(updateEvents.length).toBe(135);
expect(updateEvents.length).toBe(138);
for (let updateIndex = 0; updateIndex < updateEvents.length; updateIndex += 1) {
const event = updateEvents[updateIndex];
const { data: insideData, origin, type } = event;
Expand All @@ -78,7 +78,7 @@ describe('Raw streams tests', () => {
}
// 03 - CHECK DELETE EVENTS
const deleteEvents = events.filter((e) => e.type === EVENT_TYPE_DELETE);
expect(deleteEvents.length).toBe(95);
expect(deleteEvents.length).toBe(98);
// const deleteEventsByTypes = R.groupBy((e) => e.data.data.type, deleteEvents);
for (let delIndex = 0; delIndex < deleteEvents.length; delIndex += 1) {
const { data: insideData, origin, type } = deleteEvents[delIndex];
Expand Down
2 changes: 1 addition & 1 deletion opencti-platform/opencti-graphql/tests/utils/testQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const SYNC_LIVE_START_REMOTE_URI = conf.get('app:sync_live_start_remote_u
export const SYNC_DIRECT_START_REMOTE_URI = conf.get('app:sync_direct_start_remote_uri');
export const SYNC_RESTORE_START_REMOTE_URI = conf.get('app:sync_restore_start_remote_uri');
export const SYNC_TEST_REMOTE_URI = `http://api-tests:${PORT}`;
export const RAW_EVENTS_SIZE = 957;
export const RAW_EVENTS_SIZE = 966;
export const SYNC_LIVE_EVENTS_SIZE = 598;

export const PYTHON_PATH = './src/python/testing';
Expand Down

0 comments on commit 9c63947

Please sign in to comment.