Skip to content

Commit 2f4f798

Browse files
[backend] ”Saved filters” module implemented (#6044)
Co-authored-by: Valentin Bouzin <[email protected]>
1 parent 3fb4024 commit 2f4f798

File tree

13 files changed

+461
-4
lines changed

13 files changed

+461
-4
lines changed

opencti-platform/opencti-front/src/schema/relay.schema.graphql

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8206,6 +8206,7 @@ type Query {
82068206
fintelTemplate(id: ID!): FintelTemplate
82078207
disseminationList(id: ID!): DisseminationList
82088208
disseminationLists(first: Int, after: ID, orderBy: DisseminationListOrdering, orderMode: OrderingMode, filters: FilterGroup, search: String): DisseminationListConnection
8209+
savedFilters(first: Int, after: ID, orderBy: SavedFilterOrdering, orderMode: OrderingMode, filters: FilterGroup, search: String): SavedFilterConnection
82098210
}
82108211

82118212
type Subscription {
@@ -9051,6 +9052,8 @@ type Mutation {
90519052
disseminationListDelete(id: ID!): ID
90529053
disseminationListFieldPatch(id: ID!, input: [EditInput!]!): DisseminationList
90539054
disseminationListSend(id: ID!, input: DisseminationListSendInput!): Boolean
9055+
savedFilterAdd(input: SavedFilterAddInput!): SavedFilter
9056+
savedFilterDelete(id: ID!): ID
90549057
}
90559058

90569059
type Channel implements BasicObject & StixObject & StixCoreObject & StixDomainObject {
@@ -12668,4 +12671,35 @@ input DisseminationListSendInput {
1266812671
email_body: String!
1266912672
email_attachment_ids: [ID!]!
1267012673
html_to_body_file_id: ID
12674+
}
12675+
12676+
type SavedFilter implements InternalObject & BasicObject {
12677+
id: ID!
12678+
standard_id: String!
12679+
entity_type: String!
12680+
parent_types: [String]!
12681+
name: String!
12682+
filters: String!
12683+
scope: String
12684+
}
12685+
12686+
type SavedFilterEdge {
12687+
cursor: String!
12688+
node: SavedFilter!
12689+
}
12690+
12691+
type SavedFilterConnection {
12692+
pageInfo: PageInfo!
12693+
edges: [SavedFilterEdge!]
12694+
}
12695+
12696+
enum SavedFilterOrdering {
12697+
name
12698+
_score
12699+
}
12700+
12701+
input SavedFilterAddInput {
12702+
name: String!
12703+
filters: String!
12704+
scope: String
1267112705
}

opencti-platform/opencti-graphql/graphql-codegen.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ generates:
5555
DraftWorkspace: ../modules/draftWorkspace/draftWorkspace-types#BasicStoreEntityDraftWorkspace
5656
ExclusionList: ../modules/exclusionList/exclusionList-types#BasicStoreEntityExclusionList
5757
FintelTemplate: ../modules/fintelTemplate/fintelTemplate-types#BasicStoreEntityFintelTemplate
58+
SavedFilter: ../modules/savedFilter/savedFilter-types#BasicStoreEntitySavedFilter
5859
./graphql.schema.json:
5960
plugins:
6061
- "introspection"

opencti-platform/opencti-graphql/src/generated/graphql.ts

Lines changed: 100 additions & 4 deletions
Large diffs are not rendered by default.

opencti-platform/opencti-graphql/src/modules/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import './exclusionList/exclusionList';
6666
import './draftWorkspace/draftWorkspace';
6767
import './fintelTemplate/fintelTemplate';
6868
import './disseminationList/disseminationList';
69+
import './savedFilter/savedFilter';
6970

7071
// incomplete modules
7172
import './report/report';
@@ -127,4 +128,5 @@ import './exclusionList/exclusionList-graphql';
127128
import './draftWorkspace/draftWorkspace-graphql';
128129
import './fintelTemplate/fintelTemplate-graphql';
129130
import './disseminationList/disseminationList-graphql';
131+
import './savedFilter/savedFilter-graphql';
130132
// endregion
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { StixSavedFilter, StoreEntitySavedFilter } from './savedFilter-types';
2+
import { buildStixObject } from '../../database/stix-converter';
3+
4+
const convertSavedFiltersToStix = (instance: StoreEntitySavedFilter): StixSavedFilter => {
5+
const stixObject = buildStixObject(instance);
6+
return {
7+
...stixObject,
8+
name: instance.name,
9+
filters: instance.filters,
10+
scope: instance.scope,
11+
};
12+
};
13+
14+
export default convertSavedFiltersToStix;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { addFilter } from '../../utils/filtering/filtering-utils';
2+
import { type BasicStoreEntitySavedFilter, ENTITY_TYPE_SAVED_FILTER, type StoreEntitySavedFilter } from './savedFilter-types';
3+
import type { AuthContext, AuthUser } from '../../types/user';
4+
import { listEntitiesPaginated } from '../../database/middleware-loader';
5+
import type { QuerySavedFiltersArgs, SavedFilterAddInput } from '../../generated/graphql';
6+
import { createInternalObject, deleteInternalObject } from '../../domain/internalObject';
7+
8+
export const findAll = (context: AuthContext, user: AuthUser, args: QuerySavedFiltersArgs) => {
9+
const queryFilters = addFilter(args.filters, 'creator_id', user.id);
10+
const queryArgs = { ...args, filters: queryFilters };
11+
return listEntitiesPaginated<BasicStoreEntitySavedFilter>(context, user, [ENTITY_TYPE_SAVED_FILTER], queryArgs);
12+
};
13+
export const addSavedFilter = (context: AuthContext, user: AuthUser, input: SavedFilterAddInput) => {
14+
return createInternalObject<StoreEntitySavedFilter>(context, user, input, ENTITY_TYPE_SAVED_FILTER);
15+
};
16+
export const deleteSavedFilter = (context: AuthContext, user: AuthUser, savedFilterId: string) => {
17+
return deleteInternalObject(context, user, savedFilterId, ENTITY_TYPE_SAVED_FILTER);
18+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { registerGraphqlSchema } from '../../graphql/schema';
2+
import savedFilterTypeDefs from './savedFilter.graphql';
3+
import savedFilterResolver from './savedFilter-resolver';
4+
5+
registerGraphqlSchema({
6+
schema: savedFilterTypeDefs,
7+
resolver: savedFilterResolver
8+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { Resolvers } from '../../generated/graphql';
2+
import { addSavedFilter, deleteSavedFilter, findAll } from './savedFilter-domain';
3+
4+
const savedFilterResolver: Resolvers = {
5+
Query: {
6+
savedFilters: (_, args, context) => findAll(context, context.user, args),
7+
},
8+
Mutation: {
9+
savedFilterAdd: (_, { input }, context) => {
10+
return addSavedFilter(context, context.user, input);
11+
},
12+
savedFilterDelete: (_, { id }, context) => {
13+
return deleteSavedFilter(context, context.user, id);
14+
},
15+
},
16+
};
17+
18+
export default savedFilterResolver;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { BasicStoreEntity, StoreEntity } from '../../types/store';
2+
import type { StixObject } from '../../types/stix-common';
3+
4+
export const ENTITY_TYPE_SAVED_FILTER = 'SavedFilter';
5+
6+
export interface BasicStoreEntitySavedFilter extends BasicStoreEntity {
7+
name: string
8+
filters: string
9+
scope: string
10+
}
11+
12+
export interface StoreEntitySavedFilter extends StoreEntity {
13+
name: string
14+
filters: string
15+
scope: string
16+
}
17+
18+
export interface StixSavedFilter extends StixObject {
19+
name: string
20+
filters: string
21+
scope: string
22+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
type SavedFilter implements InternalObject & BasicObject {
2+
id: ID!
3+
standard_id: String!
4+
entity_type: String!
5+
parent_types: [String]!
6+
# SavedFilters
7+
name: String!
8+
filters: String!
9+
scope: String
10+
}
11+
12+
type SavedFilterEdge {
13+
cursor: String!
14+
node: SavedFilter!
15+
}
16+
17+
type SavedFilterConnection {
18+
pageInfo: PageInfo!
19+
edges: [SavedFilterEdge!]
20+
}
21+
22+
enum SavedFilterOrdering {
23+
name
24+
_score
25+
}
26+
27+
input SavedFilterAddInput {
28+
name: String!
29+
filters: String!
30+
scope: String
31+
}
32+
33+
34+
type Query {
35+
savedFilters(
36+
first: Int
37+
after: ID
38+
orderBy: SavedFilterOrdering
39+
orderMode: OrderingMode
40+
filters: FilterGroup
41+
search: String
42+
): SavedFilterConnection @auth
43+
}
44+
45+
type Mutation {
46+
savedFilterAdd(input: SavedFilterAddInput!): SavedFilter @auth
47+
savedFilterDelete(id: ID!): ID @auth
48+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { v4 as uuidv4 } from 'uuid';
2+
import convertSavedFiltersToStix from './savedFilter-converter';
3+
import { ENTITY_TYPE_SAVED_FILTER, type StoreEntitySavedFilter, type StixSavedFilter } from './savedFilter-types';
4+
import { ABSTRACT_INTERNAL_OBJECT } from '../../schema/general';
5+
import { type ModuleDefinition, registerDefinition } from '../../schema/module';
6+
import { isFeatureEnabled } from '../../config/conf';
7+
import { creators, createdAt } from '../../schema/attribute-definition';
8+
9+
const SAVED_FILTER_DEFINITION: ModuleDefinition<StoreEntitySavedFilter, StixSavedFilter> = {
10+
type: {
11+
id: 'saved-filter',
12+
name: ENTITY_TYPE_SAVED_FILTER,
13+
category: ABSTRACT_INTERNAL_OBJECT,
14+
aliased: false,
15+
},
16+
identifier: {
17+
definition: {
18+
[ENTITY_TYPE_SAVED_FILTER]: () => uuidv4()
19+
},
20+
},
21+
attributes: [
22+
creators,
23+
createdAt,
24+
{
25+
name: 'name',
26+
label: 'Name',
27+
type: 'string',
28+
format: 'short',
29+
mandatoryType: 'external',
30+
editDefault: false,
31+
multiple: false,
32+
upsert: false,
33+
isFilterable: true
34+
},
35+
{
36+
name: 'filters',
37+
label: 'Filters',
38+
type: 'string',
39+
format: 'text',
40+
mandatoryType: 'external',
41+
editDefault: false,
42+
multiple: false,
43+
upsert: false,
44+
isFilterable: false
45+
},
46+
{
47+
name: 'scope',
48+
label: 'Scope',
49+
type: 'string',
50+
format: 'short',
51+
mandatoryType: 'external',
52+
editDefault: false,
53+
multiple: false,
54+
upsert: false,
55+
isFilterable: false
56+
}
57+
],
58+
relations: [],
59+
representative: (instance: StixSavedFilter) => {
60+
return instance.name;
61+
},
62+
converter: convertSavedFiltersToStix,
63+
};
64+
65+
const isSavedFiltersEnabled = isFeatureEnabled('SAVED_FILTERS');
66+
67+
if (isSavedFiltersEnabled) {
68+
registerDefinition(SAVED_FILTER_DEFINITION);
69+
}

opencti-platform/opencti-graphql/src/schema/internalObject.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ENTITY_TYPE_DELETE_OPERATION } from '../modules/deleteOperation/deleteO
66
import { ENTITY_TYPE_DRAFT_WORKSPACE } from '../modules/draftWorkspace/draftWorkspace-types';
77
import { ENTITY_TYPE_EXCLUSION_LIST } from '../modules/exclusionList/exclusionList-types';
88
import { ENTITY_TYPE_FINTEL_TEMPLATE } from '../modules/fintelTemplate/fintelTemplate-types';
9+
import { ENTITY_TYPE_SAVED_FILTER } from '../modules/savedFilter/savedFilter-types';
910

1011
export const ENTITY_TYPE_SETTINGS = 'Settings';
1112
export const ENTITY_TYPE_MIGRATION_STATUS = 'MigrationStatus';
@@ -45,6 +46,7 @@ const DATED_INTERNAL_OBJECTS = [
4546
ENTITY_TYPE_DELETE_OPERATION,
4647
ENTITY_TYPE_DRAFT_WORKSPACE,
4748
ENTITY_TYPE_EXCLUSION_LIST,
49+
ENTITY_TYPE_SAVED_FILTER,
4850
];
4951
const INTERNAL_OBJECTS = [
5052
ENTITY_TYPE_SETTINGS,
@@ -74,6 +76,7 @@ const INTERNAL_OBJECTS = [
7476
ENTITY_TYPE_DRAFT_WORKSPACE,
7577
ENTITY_TYPE_EXCLUSION_LIST,
7678
ENTITY_TYPE_FINTEL_TEMPLATE,
79+
ENTITY_TYPE_SAVED_FILTER,
7780
];
7881
const HISTORY_OBJECTS = [ENTITY_TYPE_WORK];
7982

0 commit comments

Comments
 (0)