Skip to content

Commit 85c139e

Browse files
committed
[backend] Refactor to assertType utility
1 parent 0af3f59 commit 85c139e

File tree

5 files changed

+73
-69
lines changed

5 files changed

+73
-69
lines changed

opencti-platform/opencti-graphql/src/database/attachment-processor-props.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Mutable } from '../types/type-utils';
1+
import type { Mutable } from '../utils/type-utils';
22

33
// List of fields extracted by the attachment ingest processor.
44
// The full list is available in the Elasticsearch docs:

opencti-platform/opencti-graphql/src/database/engine.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ import { DRAFT_OPERATION_CREATE, DRAFT_OPERATION_DELETE, DRAFT_OPERATION_DELETE_
175175
import { RELATION_SAMPLE } from '../modules/malwareAnalysis/malwareAnalysis-types';
176176
import { asyncMap } from '../utils/data-processing';
177177
import { doYield } from '../utils/eventloop-utils';
178+
import type { Mutable } from '../utils/type-utils';
178179
import { RELATION_COVERED } from '../modules/securityCoverage/securityCoverage-types';
179180
import type { AuthContext, AuthUser } from '../types/user';
180181
import type {
@@ -191,7 +192,6 @@ import type {
191192
StoreRelation,
192193
} from '../types/store';
193194
import type { BasicStoreSettings } from '../types/settings';
194-
import type { Mutable } from '../types/type-utils';
195195
import { completeSpecialFilterKeys } from '../utils/filtering/filtering-completeSpecialFilterKeys';
196196
import { IDS_ATTRIBUTES } from '../domain/attribute-utils';
197197
import { schemaRelationsRefDefinition } from '../schema/schema-relationsRef';
@@ -2746,11 +2746,11 @@ const buildSubQueryForFilterGroup = (
27462746

27472747
const currentSubQuery = localMustFilters.length > 0
27482748
? {
2749-
bool: {
2750-
should: localMustFilters,
2751-
minimum_should_match: mode === 'or' ? 1 : localMustFilters.length,
2752-
},
2753-
}
2749+
bool: {
2750+
should: localMustFilters,
2751+
minimum_should_match: mode === 'or' ? 1 : localMustFilters.length,
2752+
},
2753+
}
27542754
: null;
27552755
return { subQuery: currentSubQuery, postFiltersTags: localPostFilterTags, resultSaltCount: localSaltCount };
27562756
};
@@ -3123,12 +3123,12 @@ const tagFiltersForPostFiltering = (
31233123
) => {
31243124
const taggedFilters: (Filter & { postFilteringTag: string })[] = filters
31253125
? extractFiltersFromGroup(filters, [INSTANCE_REGARDING_OF, INSTANCE_DYNAMIC_REGARDING_OF])
3126-
.filter((filter) => isEmptyField(filter.operator) || filter.operator === 'eq')
3127-
.map((filter, i) => {
3128-
const taggedFilter = filter as Filter & { postFilteringTag: string };
3129-
taggedFilter.postFilteringTag = `${i}`;
3130-
return taggedFilter;
3131-
})
3126+
.filter((filter) => isEmptyField(filter.operator) || filter.operator === 'eq')
3127+
.map((filter, i) => {
3128+
const taggedFilter = filter as Filter & { postFilteringTag: string };
3129+
taggedFilter.postFilteringTag = `${i}`;
3130+
return taggedFilter;
3131+
})
31323132
: [];
31333133

31343134
if (taggedFilters.length > 0) {

opencti-platform/opencti-graphql/src/modules/internal/document/document.ts

Lines changed: 33 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,13 @@
1+
import { assertType } from '../../../utils/type-utils';
12
import type { AttachmentProcessorExtractedProp } from '../../../database/attachment-processor-props';
23
import { ENTITY_TYPE_INTERNAL_FILE } from '../../../schema/internalObject';
34
import { schemaAttributesDefinition } from '../../../schema/schema-attributes';
4-
import {
5-
type AttributeDefinition,
6-
createdAt,
7-
creators,
8-
entityType,
9-
id,
10-
internalId,
11-
parentTypes,
12-
refreshedAt,
13-
standardId,
14-
updatedAt,
15-
type MappingDefinition,
16-
type BasicStoreAttribute,
17-
} from '../../../schema/attribute-definition';
5+
import { type AttributeDefinition, createdAt, creators, entityType, id, internalId, parentTypes, refreshedAt, standardId, updatedAt } from '../../../schema/attribute-definition';
186
import { ENTITY_TYPE_MARKING_DEFINITION } from '../../../schema/stixMetaObject';
197
import { ABSTRACT_STIX_CORE_OBJECT } from '../../../schema/general';
208
import { UPLOAD_STATUS_VALUES } from './document-domain';
219

22-
export const ATTACHMENT_MAPPINGS = [
23-
{ name: 'author', label: 'Author', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
24-
{ name: 'comments', label: 'Comments', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
25-
{ name: 'content', label: 'Content', type: 'string', format: 'text', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
26-
{ name: 'content_length', label: 'Content length', type: 'numeric', precision: 'integer', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
27-
{ name: 'content_type', label: 'Content type', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
28-
{ name: 'creator_tool', label: 'Creator tool', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
29-
{ name: 'date', label: 'Created date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
30-
{ name: 'description', label: 'Description', type: 'string', format: 'text', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
31-
{ name: 'format', label: 'Format', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
32-
{ name: 'keywords', label: 'Keywords', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
33-
{ name: 'language', label: 'Language', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
34-
{ name: 'metadata_date', label: 'Metadata date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
35-
{ name: 'modified', label: 'Modified date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
36-
{ name: 'modifier', label: 'Modifier', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
37-
{ name: 'print_date', label: 'Print date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
38-
{ name: 'title', label: 'Title', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
39-
] satisfies ({
40-
name: AttachmentProcessorExtractedProp;
41-
} & MappingDefinition<BasicStoreAttribute>)[];
42-
43-
// Compile-time shenanigans to make sure we don't forget to update
44-
// ATTACHMENT_MAPPINGS when/if we start extracting new fields
45-
// via the ES/OS attachment ingest pipeline.
46-
type AttachmentMappingsWithCheck = Exclude<
47-
AttachmentProcessorExtractedProp,
48-
typeof ATTACHMENT_MAPPINGS[number]['name']
49-
> extends never
50-
? MappingDefinition<BasicStoreAttribute>[]
51-
: 'Make sure ATTACHMENT_MAPPINGS defines one mapping for each AttachmentProcessorExtractedProp';
52-
53-
const TYPE_CHECKED_ATTACHMENT_MAPPINGS: AttachmentMappingsWithCheck = ATTACHMENT_MAPPINGS;
54-
55-
const attributes: Array<AttributeDefinition> = [
10+
const attributes = [
5611
id,
5712
internalId,
5813
standardId,
@@ -117,12 +72,40 @@ const attributes: Array<AttributeDefinition> = [
11772
multiple: false,
11873
upsert: false,
11974
isFilterable: false,
120-
mappings: TYPE_CHECKED_ATTACHMENT_MAPPINGS,
75+
mappings: [
76+
{ name: 'author', label: 'Author', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
77+
{ name: 'comments', label: 'Comments', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
78+
{ name: 'content', label: 'Content', type: 'string', format: 'text', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
79+
{ name: 'content_length', label: 'Content length', type: 'numeric', precision: 'integer', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
80+
{ name: 'content_type', label: 'Content type', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
81+
{ name: 'creator_tool', label: 'Creator tool', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
82+
{ name: 'date', label: 'Created date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
83+
{ name: 'description', label: 'Description', type: 'string', format: 'text', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
84+
{ name: 'format', label: 'Format', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
85+
{ name: 'keywords', label: 'Keywords', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
86+
{ name: 'language', label: 'Language', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
87+
{ name: 'metadata_date', label: 'Metadata date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
88+
{ name: 'modified', label: 'Modified date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
89+
{ name: 'modifier', label: 'Modifier', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
90+
{ name: 'print_date', label: 'Print date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
91+
{ name: 'title', label: 'Title', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
92+
],
12193
},
12294
{ name: 'uploaded_at', label: 'Upload date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: false },
12395
{ name: 'file_id', label: 'File identifier', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: false },
12496
{ name: 'entity_id', label: 'Related entity', type: 'string', format: 'id', entityTypes: [ABSTRACT_STIX_CORE_OBJECT], mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: false },
12597
{ name: 'removed', label: 'Removed', type: 'boolean', mandatoryType: 'no', editDefault: false, multiple: false, upsert: false, isFilterable: false },
126-
];
98+
] as const satisfies Array<AttributeDefinition>;
99+
100+
const attachmentAttributes = attributes[18];
101+
102+
type AttachmentAttributeMappingNames = typeof attachmentAttributes.mappings[number]['name'][];
103+
104+
// Make sure there's an attachment mapping for each field extracted
105+
// by the `attachment` ingest processor, exhaustively.
106+
assertType<
107+
AttachmentAttributeMappingNames,
108+
AttachmentProcessorExtractedProp[]
109+
>(attachmentAttributes.mappings.map(({ name }) => name));
127110

128111
schemaAttributesDefinition.registerAttributes(ENTITY_TYPE_INTERNAL_FILE, attributes);

opencti-platform/opencti-graphql/src/types/type-utils.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,3 @@ import { isNotEmptyField } from '../database/utils';
33
export const filterEmpty = <T>(data: T | null | undefined): data is T => {
44
return isNotEmptyField(data);
55
};
6-
7-
/**
8-
* Inverse operation of the built-in Readonly<T> utility type:
9-
* makes all records of an object mutable.
10-
*/
11-
export type Mutable<T> = { -readonly [P in keyof T]: T[P]; };
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Inverse operation of the built-in Readonly<T> utility type:
3+
* makes all records of an object mutable.
4+
*/
5+
export type Mutable<T> = { -readonly [P in keyof T]: T[P]; };
6+
7+
type AssertEqual<T, TExpected> = [T] extends [TExpected]
8+
? [TExpected] extends [T]
9+
? T
10+
: never
11+
: never;
12+
13+
/**
14+
* Compile-time type assertion utility checking that a type T
15+
* is exactly of type TExpected.
16+
*
17+
* @example
18+
* ```
19+
* type Fruit = 'banana' | 'strawberry' | 'pineapple';
20+
* const allFruits = ['banana' as const, 'strawberry' as const, 'pineapple' as const];
21+
* // Checks for exhaustiveness
22+
* assertType<typeof allFruits[number][], Fruit[]>(allFruits);
23+
* ```
24+
*/
25+
export const assertType = <T, TExpected>(_x: AssertEqual<T, TExpected>) => {
26+
// noop
27+
};

0 commit comments

Comments
 (0)