Skip to content

Commit

Permalink
[backend/frontend] add double mappings filters for notifications (#4939)
Browse files Browse the repository at this point in the history
  • Loading branch information
Archidoit committed Feb 20, 2024
1 parent 6a9b466 commit db5241a
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1337,7 +1337,7 @@ class ToolBar extends Component {
const currentMap = schema.filterKeysSchema.get(entityType);
currentMap?.forEach((value, key) => filterKeysMap.set(key, value));
});
const availableFilterKeys = Array.from(filterKeysMap.keys() ?? []).concat(['entity_type']);
const availableFilterKeys = Array.from(filterKeysMap.keys()).concat(['entity_type']);
// endregion
return (
<Drawer
Expand Down
27 changes: 16 additions & 11 deletions opencti-platform/opencti-graphql/src/domain/filterKeysSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { schemaAttributesDefinition } from '../schema/schema-attributes';
import { schemaTypesDefinition } from '../schema/schema-types';
import type {
AttributeDefinition,
ComplexAttributeWithMappings,
EnumAttribute,
IdAttribute,
NestedObjectAttribute,
NumericAttribute,
RefAttribute,
StringAttribute,
Expand Down Expand Up @@ -138,23 +138,28 @@ const completeFilterDefinitionMapWithElement = (
}
};

const completeFilterDefinitionMapWithNestedAttribute = (
const completeFilterDefinitionMapWithObjectAttributeWithMappings = (
attributesMapWithFilterDefinition: Map<string, FilterDefinition>, // map in construction
nestedAttributeDefinition: NestedObjectAttribute, // nested attribute to study
objectAttributeDefinition: ComplexAttributeWithMappings, // object attribute with mappings
types: string[], // entity types to apply
) => {
const { mappings } = nestedAttributeDefinition;
const { mappings } = objectAttributeDefinition;
mappings.forEach((mappingAttributeDefinition) => {
if (mappingAttributeDefinition.isFilterable) {
if (mappingAttributeDefinition.type === 'object' && ['nested', 'standard'].includes(mappingAttributeDefinition.format)) { // case 1: nested attribute
throw Error('A nested attribute can\'t contain a nested attribute'); // not supported for the moment
} else if (mappingAttributeDefinition.associatedFilterKeys) { // case 2: not nested attribute and associatedFilterKeys is set
if (mappingAttributeDefinition.type === 'object' && ['nested', 'standard'].includes(mappingAttributeDefinition.format)) { // case 1: object attribute with mappings
const composedMappingName = `${objectAttributeDefinition.name}.${mappingAttributeDefinition.name}`;
completeFilterDefinitionMapWithObjectAttributeWithMappings(
attributesMapWithFilterDefinition,
{ ...mappingAttributeDefinition, name: composedMappingName } as ComplexAttributeWithMappings,
types
);
} else if (mappingAttributeDefinition.associatedFilterKeys) { // case 2: attribute with no mappings and associatedFilterKeys is set
// the keys to add are the ones in associatedFilterKeys
mappingAttributeDefinition.associatedFilterKeys.forEach(({ key, label }) => {
completeFilterDefinitionMapWithElement(attributesMapWithFilterDefinition, types, key, { ...mappingAttributeDefinition, name: key, label }, 'attribute');
});
} else { // case 3: not nested attribute and the key to add is composed with the attribute name and the mapping attribute name
const composedMappingName = `${nestedAttributeDefinition.name}.${mappingAttributeDefinition.name}`;
} else { // case 3: attribute with no mappings and the key to add is composed with the attribute name and the mapping attribute name
const composedMappingName = `${objectAttributeDefinition.name}.${mappingAttributeDefinition.name}`;
completeFilterDefinitionMapWithElement(attributesMapWithFilterDefinition, types, composedMappingName, { ...mappingAttributeDefinition, name: composedMappingName }, 'attribute');
}
}
Expand All @@ -172,8 +177,8 @@ const completeFilterDefinitionMapForType = (
attributesMap.forEach((attributeDefinition, attributeName) => {
if (attributeDefinition.isFilterable) { // if it is filterable
if (attributeDefinition.type === 'object' && ['nested', 'standard'].includes(attributeDefinition.format)) { // case 1.1: attribute with mappings
completeFilterDefinitionMapWithNestedAttribute(filterDefinitionMap, attributeDefinition as NestedObjectAttribute, types);
} else { // case 1.2: not nested attribute
completeFilterDefinitionMapWithObjectAttributeWithMappings(filterDefinitionMap, attributeDefinition as ComplexAttributeWithMappings, types);
} else { // case 1.2: attribute with no mappings
completeFilterDefinitionMapWithElement(filterDefinitionMap, types, attributeName, attributeDefinition, 'attribute');
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
type StoreEntityNotification,
type StoreEntityTrigger
} from './notification-types';
import { ABSTRACT_INTERNAL_OBJECT } from '../../schema/general';
import { ABSTRACT_INTERNAL_OBJECT, ABSTRACT_STIX_CORE_OBJECT } from '../../schema/general';
import type { ModuleDefinition } from '../../schema/module';
import { registerDefinition } from '../../schema/module';
import { authorizedAuthorities, authorizedMembers } from '../../schema/attribute-definition';
Expand Down Expand Up @@ -81,9 +81,9 @@ const NOTIFICATION_DEFINITION: ModuleDefinition<StoreEntityNotification, StixNot
editDefault: false,
multiple: true,
upsert: false,
isFilterable: false,
isFilterable: true,
mappings: [
{ name: 'title', label: 'Notification title', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'title', label: 'Notification title', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: false },
{
name: 'events',
label: 'Notification events',
Expand All @@ -95,9 +95,9 @@ const NOTIFICATION_DEFINITION: ModuleDefinition<StoreEntityNotification, StixNot
upsert: false,
isFilterable: true,
mappings: [
{ name: 'message', label: 'Notification event message', type: 'string', format: 'text', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'instance_id', label: 'Notification related instance', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'operation', label: 'Notification operation', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'message', label: 'Notification message', type: 'string', format: 'text', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'instance_id', label: 'Notification related instance', type: 'string', format: 'id', entityTypes: [ABSTRACT_STIX_CORE_OBJECT], mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: false },
{ name: 'operation', label: 'Notification operation', type: 'string', format: 'enum', values: ['create', 'update', 'delete'], mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
]
},
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export type NestedObjectAttribute = { type: 'object', format: 'nested' } & Basic
export type RefAttribute = { type: 'ref', databaseName: string, stixName: string, isRefExistingForTypes: Checker, datable?: boolean, toTypes: string[] } & BasicDefinition;
export type StringAttribute = IdAttribute | TextAttribute | EnumAttribute | VocabAttribute | JsonAttribute;
export type ComplexAttribute = FlatObjectAttribute | ObjectAttribute | NestedObjectAttribute;
export type ComplexAttributeWithMappings = ObjectAttribute | NestedObjectAttribute;

export type AttributeDefinition = NumericAttribute | DateAttribute | BooleanAttribute | StringAttribute | ComplexAttribute | RefAttribute;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ describe('Filter keys schema generation testing', async () => {
// 'LZJD hash' for observables (not filterable)
filterDefinition = filterKeysSchema.get(ABSTRACT_STIX_CYBER_OBSERVABLE)?.get('hashes.LZJD');
expect(filterDefinition).toBeUndefined(); // LZJD hash is not filterable
// 'operation' for notifications (mapping attribute in a mapping attribute)
filterDefinition = filterKeysSchema.get(ABSTRACT_STIX_CYBER_OBSERVABLE)?.get('notification_content.events.operation');
expect(filterDefinition?.filterKey).toEqual('notification_content.events.operation');
expect(filterDefinition?.type).toEqual('enum');
expect(filterDefinition?.elementsForFilterValuesSearch.length).toEqual(3); // create, update, delete
});
it('should construct correct filter definition for nested object attributes', () => {
// 'fromId' for relationships
Expand Down

0 comments on commit db5241a

Please sign in to comment.