Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[backend/frontend] fromOrTo relations filters with different operators/modes combinations (#6390) #6395

Merged
merged 9 commits into from
Mar 21, 2024
Prev Previous commit
Next Next commit
[backend] elementWithTargetTypes filters with not_eq (#6390)
  • Loading branch information
Archidoit committed Mar 21, 2024
commit 56d9545f797009f8cf33f38d0342fc38997467e5
61 changes: 56 additions & 5 deletions opencti-platform/opencti-graphql/src/database/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ import {
complexConversionFilterKeys,
COMPUTED_RELIABILITY_FILTER,
IDS_FILTER,
INSTANCE_FILTER_TARGET_TYPES,
INSTANCE_RELATION_TYPES_FILTER,
INSTANCE_REGARDING_OF,
INSTANCE_RELATION_FILTER,
RELATION_FROM_FILTER,
Expand Down Expand Up @@ -2289,9 +2289,60 @@ const completeSpecialFilterKeys = async (context, user, inputFilters) => {
];
finalFilters.push({ key: 'connections', nested, mode: filter.mode });
}
if (filterKey === INSTANCE_FILTER_TARGET_TYPES) {
const nested = [{ key: 'types', operator: filter.operator, values: filter.values }];
finalFilters.push({ key: 'connections', nested, mode: filter.mode });
if (filterKey === INSTANCE_RELATION_TYPES_FILTER) {
// define mode for the filter group
let globalMode = 'or';
if (filter.operator === 'eq' || filter.operator === 'not_nil') {
// relatedType = malware <-> fromType = malware OR toType = malware
// relatedType is not empty <-> fromType is not empty OR toType is not empty
globalMode = 'or';
} else if (filter.operator === 'not_eq' || filter.operator === 'nil') {
// relatedType != malware <-> fromType != malware AND toType != malware
// relatedType is empty <-> fromType is empty AND toType is empty
globalMode = 'and';
} else {
throw Error(`${INSTANCE_RELATION_TYPES_FILTER} filter only support 'eq', 'not_eq', 'nil' and 'not_nil' operators, not ${filter.operator}.`);
}
// define the filter group
if (filter.operator === 'eq' || filter.operator === 'not_eq') {
const filterGroupsForValues = filter.values.map((val) => {
const nestedFrom = [
{ key: 'types', operator: filter.operator, values: [val] },
{ key: 'role', operator: 'wildcard', values: ['*_from'] }
];
const nestedTo = [
{ key: 'types', operator: filter.operator, values: [val] },
{ key: 'role', operator: 'wildcard', values: ['*_to'] }
];
return {
mode: globalMode,
filters: [{ key: 'connections', nested: nestedFrom, mode: filter.mode }, { key: 'connections', nested: nestedTo, mode: filter.mode }],
filterGroups: [],
};
});
finalFilterGroups.push({
mode: filter.mode,
filters: [],
filterGroups: filterGroupsForValues,
});
} else if (filter.operator === 'nil' || filter.operator === 'not_nil') {
const nestedFrom = [
{ key: 'types', operator: filter.operator, values: [] },
{ key: 'role', operator: 'wildcard', values: ['*_from'] }
];
const nestedTo = [
{ key: 'types', operator: filter.operator, values: [] },
{ key: 'role', operator: 'wildcard', values: ['*_to'] }
];
const innerFilters = [{ key: 'connections', nested: nestedFrom, mode: filter.mode }, { key: 'connections', nested: nestedTo, mode: filter.mode }];
console.log('filters', innerFilters);
console.log('filters00', innerFilters[0].nested);
finalFilterGroups.push({
mode: globalMode,
filters: innerFilters,
filterGroups: [],
});
}
}
if (filterKey === RELATION_FROM_ROLE_FILTER || filterKey === RELATION_TO_ROLE_FILTER) {
const side = filterKey === RELATION_FROM_ROLE_FILTER ? 'from' : 'to';
Expand All @@ -2303,7 +2354,7 @@ const completeSpecialFilterKeys = async (context, user, inputFilters) => {
if (filterKey === ALIAS_FILTER) {
finalFilters.push({ ...filter, key: [ATTRIBUTE_ALIASES, ATTRIBUTE_ALIASES_OPENCTI] });
}
} else if (arrayKeys.some((fiterKey) => isObjectAttribute(fiterKey)) && !arrayKeys.some((fiterKey) => fiterKey === 'connections')) {
} else if (arrayKeys.some((filterKey) => isObjectAttribute(filterKey)) && !arrayKeys.some((filterKey) => filterKey === 'connections')) {
if (arrayKeys.length > 1) {
throw UnsupportedError('A filter with these multiple keys is not supported', { keys: arrayKeys });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import { schemaAttributesDefinition } from '../../schema/schema-attributes';
import { ABSTRACT_BASIC_RELATIONSHIP } from '../../schema/general';
import {
INSTANCE_FILTER_TARGET_TYPES,
INSTANCE_RELATION_TYPES_FILTER,
INSTANCE_RELATION_FILTER,
RELATION_FROM_FILTER,
RELATION_FROM_TYPES_FILTER,
Expand Down Expand Up @@ -44,7 +44,7 @@ export const connections: AttributeDefinition = {
},
{ name: 'name', label: 'Name', type: 'string', format: 'short', editDefault: false, mandatoryType: 'no', multiple: true, upsert: true, isFilterable: false },
{ name: 'role', label: 'Role', type: 'string', format: 'short', editDefault: false, mandatoryType: 'no', multiple: true, upsert: true, isFilterable: false },
{ name: 'types', label: 'Types', type: 'string', format: 'short', editDefault: false, mandatoryType: 'no', multiple: true, upsert: true, isFilterable: false, associatedFilterKeys: [{ key: RELATION_FROM_TYPES_FILTER, label: 'Source type' }, { key: RELATION_TO_TYPES_FILTER, label: 'Target type' }, { key: INSTANCE_FILTER_TARGET_TYPES, label: 'Related type' }] },
{ name: 'types', label: 'Types', type: 'string', format: 'short', editDefault: false, mandatoryType: 'no', multiple: true, upsert: true, isFilterable: false, associatedFilterKeys: [{ key: RELATION_FROM_TYPES_FILTER, label: 'Source type' }, { key: RELATION_TO_TYPES_FILTER, label: 'Target type' }, { key: INSTANCE_RELATION_TYPES_FILTER, label: 'Related type' }] },
],
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { STIX_CORE_RELATIONSHIPS } from '../../schema/stixCoreRelationship';
import { connections } from './basicRelationship-registrationAttributes';
import { internalId } from '../../schema/attribute-definition';
import {
INSTANCE_FILTER_TARGET_TYPES,
INSTANCE_RELATION_TYPES_FILTER,
INSTANCE_RELATION_FILTER,
RELATION_FROM_FILTER,
RELATION_FROM_TYPES_FILTER,
Expand All @@ -28,7 +28,7 @@ export const stixCoreRelationshipsAttributes: Array<AttributeDefinition> = [
},
{ name: 'name', label: 'Name', type: 'string', format: 'short', editDefault: false, mandatoryType: 'no', multiple: true, upsert: true, isFilterable: false },
{ name: 'role', label: 'Role', type: 'string', format: 'short', editDefault: false, mandatoryType: 'no', multiple: true, upsert: true, isFilterable: false },
{ name: 'types', label: 'Types', type: 'string', format: 'short', editDefault: false, mandatoryType: 'no', multiple: true, upsert: true, isFilterable: true, associatedFilterKeys: [{ key: RELATION_FROM_TYPES_FILTER, label: 'Source type' }, { key: RELATION_TO_TYPES_FILTER, label: 'Target type' }, { key: INSTANCE_FILTER_TARGET_TYPES, label: 'Related type' }] },
{ name: 'types', label: 'Types', type: 'string', format: 'short', editDefault: false, mandatoryType: 'no', multiple: true, upsert: true, isFilterable: true, associatedFilterKeys: [{ key: RELATION_FROM_TYPES_FILTER, label: 'Source type' }, { key: RELATION_TO_TYPES_FILTER, label: 'Target type' }, { key: INSTANCE_RELATION_TYPES_FILTER, label: 'Related type' }] },
],
},
{ name: 'entity_type', label: 'Entity type', type: 'string', format: 'short', editDefault: false, mandatoryType: 'no', multiple: true, upsert: true, isFilterable: false },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const RELATION_FROM_ROLE_FILTER = 'fromRole';
export const RELATION_TO_ROLE_FILTER = 'toRole';
export const RELATION_FROM_TYPES_FILTER = 'fromTypes';
export const RELATION_TO_TYPES_FILTER = 'toTypes';
export const INSTANCE_FILTER_TARGET_TYPES = 'elementWithTargetTypes'; // TODO Rename/migrate to fromOrToType
export const INSTANCE_RELATION_TYPES_FILTER = 'elementWithTargetTypes'; // TODO Rename/migrate to fromOrToType
export const CONNECTED_TO_INSTANCE_FILTER = 'connectedToId'; // TODO Rename/migrate to triggerListenId
export const CONNECTED_TO_INSTANCE_SIDE_EVENTS_FILTER = 'connectedToId_sideEvents';

Expand Down Expand Up @@ -75,7 +75,7 @@ export const complexConversionFilterKeys = [
SOURCE_RELIABILITY_FILTER, // reliability of the author
COMPUTED_RELIABILITY_FILTER, // reliability, or reliabilityof the author if no reliability
INSTANCE_RELATION_FILTER, // nested relation for the from or to of a relationship
INSTANCE_FILTER_TARGET_TYPES, // nested relation for the from or type type of a relationship
INSTANCE_RELATION_TYPES_FILTER, // nested relation for the from or type type of a relationship
Archidoit marked this conversation as resolved.
Show resolved Hide resolved
RELATION_FROM_FILTER, // nested relation for the from of a relationship
RELATION_TO_FILTER, // nested relation for the to of a relationship
RELATION_TO_SIGHTING_FILTER, // nested sigthing relation for the to of a sighting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
ALIAS_FILTER,
CONNECTED_TO_INSTANCE_FILTER,
CONTEXT_OBJECT_LABEL_FILTER,
INSTANCE_FILTER_TARGET_TYPES,
INSTANCE_RELATION_TYPES_FILTER,
INSTANCE_REGARDING_OF,
RELATION_FROM_FILTER,
RELATION_TO_TYPES_FILTER,
Expand Down Expand Up @@ -152,8 +152,8 @@ describe('Filter keys schema generation testing', async () => {
expect(filterDefinition?.label).toEqual('Target type');
expect(filterDefinition?.elementsForFilterValuesSearch.length).toEqual(0);
// 'elementWithTargetTypes' for stix core relationships
filterDefinition = filterKeysSchema.get(ABSTRACT_STIX_CORE_RELATIONSHIP)?.get(INSTANCE_FILTER_TARGET_TYPES);
expect(filterDefinition?.filterKey).toEqual(INSTANCE_FILTER_TARGET_TYPES);
filterDefinition = filterKeysSchema.get(ABSTRACT_STIX_CORE_RELATIONSHIP)?.get(INSTANCE_RELATION_TYPES_FILTER);
expect(filterDefinition?.filterKey).toEqual(INSTANCE_RELATION_TYPES_FILTER);
expect(filterDefinition?.type).toEqual('string');
// 'fromId' for basic relationships: not filterable
filterDefinition = filterKeysSchema.get(ABSTRACT_BASIC_RELATIONSHIP)?.get(RELATION_FROM_FILTER);
Expand Down