Skip to content
Merged
Prev Previous commit
Next Next commit
[backend] Refactor to assertType utility
  • Loading branch information
fellowseb committed Mar 5, 2026
commit 43e01e6d234c075aa9c79cd10fe14231a0d0c80c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Mutable } from '../types/type-utils';
import type { Mutable } from '../utils/type-utils';

// List of fields extracted by the attachment ingest processor.
// The full list is available in the Elasticsearch docs:
Expand Down
24 changes: 12 additions & 12 deletions opencti-platform/opencti-graphql/src/database/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ import { DRAFT_OPERATION_CREATE, DRAFT_OPERATION_DELETE, DRAFT_OPERATION_DELETE_
import { RELATION_SAMPLE } from '../modules/malwareAnalysis/malwareAnalysis-types';
import { asyncMap } from '../utils/data-processing';
import { doYield } from '../utils/eventloop-utils';
import type { Mutable } from '../utils/type-utils';
import { RELATION_COVERED } from '../modules/securityCoverage/securityCoverage-types';
import type { AuthContext, AuthUser } from '../types/user';
import type {
Expand All @@ -191,7 +192,6 @@ import type {
StoreRelation,
} from '../types/store';
import type { BasicStoreSettings } from '../types/settings';
import type { Mutable } from '../types/type-utils';
import { completeSpecialFilterKeys } from '../utils/filtering/filtering-completeSpecialFilterKeys';
import { IDS_ATTRIBUTES } from '../domain/attribute-utils';
import { schemaRelationsRefDefinition } from '../schema/schema-relationsRef';
Expand Down Expand Up @@ -2746,11 +2746,11 @@ const buildSubQueryForFilterGroup = (

const currentSubQuery = localMustFilters.length > 0
? {
bool: {
should: localMustFilters,
minimum_should_match: mode === 'or' ? 1 : localMustFilters.length,
},
}
bool: {
should: localMustFilters,
minimum_should_match: mode === 'or' ? 1 : localMustFilters.length,
},
}
: null;
return { subQuery: currentSubQuery, postFiltersTags: localPostFilterTags, resultSaltCount: localSaltCount };
};
Expand Down Expand Up @@ -3123,12 +3123,12 @@ const tagFiltersForPostFiltering = (
) => {
const taggedFilters: (Filter & { postFilteringTag: string })[] = filters
? extractFiltersFromGroup(filters, [INSTANCE_REGARDING_OF, INSTANCE_DYNAMIC_REGARDING_OF])
.filter((filter) => isEmptyField(filter.operator) || filter.operator === 'eq')
.map((filter, i) => {
const taggedFilter = filter as Filter & { postFilteringTag: string };
taggedFilter.postFilteringTag = `${i}`;
return taggedFilter;
})
.filter((filter) => isEmptyField(filter.operator) || filter.operator === 'eq')
.map((filter, i) => {
const taggedFilter = filter as Filter & { postFilteringTag: string };
taggedFilter.postFilteringTag = `${i}`;
return taggedFilter;
})
: [];

if (taggedFilters.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,13 @@
import { assertType } from '../../../utils/type-utils';
import type { AttachmentProcessorExtractedProp } from '../../../database/attachment-processor-props';
import { ENTITY_TYPE_INTERNAL_FILE } from '../../../schema/internalObject';
import { schemaAttributesDefinition } from '../../../schema/schema-attributes';
import {
type AttributeDefinition,
createdAt,
creators,
entityType,
id,
internalId,
parentTypes,
refreshedAt,
standardId,
updatedAt,
type MappingDefinition,
type BasicStoreAttribute,
} from '../../../schema/attribute-definition';
import { type AttributeDefinition, createdAt, creators, entityType, id, internalId, parentTypes, refreshedAt, standardId, updatedAt } from '../../../schema/attribute-definition';
import { ENTITY_TYPE_MARKING_DEFINITION } from '../../../schema/stixMetaObject';
import { ABSTRACT_STIX_CORE_OBJECT } from '../../../schema/general';
import { UPLOAD_STATUS_VALUES } from './document-domain';

export const ATTACHMENT_MAPPINGS = [
{ name: 'author', label: 'Author', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'comments', label: 'Comments', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'content', label: 'Content', type: 'string', format: 'text', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'content_length', label: 'Content length', type: 'numeric', precision: 'integer', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'content_type', label: 'Content type', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'creator_tool', label: 'Creator tool', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'date', label: 'Created date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'description', label: 'Description', type: 'string', format: 'text', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'format', label: 'Format', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'keywords', label: 'Keywords', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'language', label: 'Language', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'metadata_date', label: 'Metadata date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'modified', label: 'Modified date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'modifier', label: 'Modifier', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'print_date', label: 'Print date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'title', label: 'Title', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
] satisfies ({
name: AttachmentProcessorExtractedProp;
} & MappingDefinition<BasicStoreAttribute>)[];

// Compile-time shenanigans to make sure we don't forget to update
// ATTACHMENT_MAPPINGS when/if we start extracting new fields
// via the ES/OS attachment ingest pipeline.
type AttachmentMappingsWithCheck = Exclude<
AttachmentProcessorExtractedProp,
typeof ATTACHMENT_MAPPINGS[number]['name']
> extends never
? MappingDefinition<BasicStoreAttribute>[]
: 'Make sure ATTACHMENT_MAPPINGS defines one mapping for each AttachmentProcessorExtractedProp';

const TYPE_CHECKED_ATTACHMENT_MAPPINGS: AttachmentMappingsWithCheck = ATTACHMENT_MAPPINGS;

const attributes: Array<AttributeDefinition> = [
const attributes = [
id,
internalId,
standardId,
Expand Down Expand Up @@ -117,12 +72,40 @@ const attributes: Array<AttributeDefinition> = [
multiple: false,
upsert: false,
isFilterable: false,
mappings: TYPE_CHECKED_ATTACHMENT_MAPPINGS,
mappings: [
{ name: 'author', label: 'Author', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'comments', label: 'Comments', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'content', label: 'Content', type: 'string', format: 'text', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'content_length', label: 'Content length', type: 'numeric', precision: 'integer', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'content_type', label: 'Content type', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'creator_tool', label: 'Creator tool', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'date', label: 'Created date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'description', label: 'Description', type: 'string', format: 'text', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'format', label: 'Format', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'keywords', label: 'Keywords', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'language', label: 'Language', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'metadata_date', label: 'Metadata date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'modified', label: 'Modified date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'modifier', label: 'Modifier', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'print_date', label: 'Print date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
{ name: 'title', label: 'Title', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: true },
],
},
{ name: 'uploaded_at', label: 'Upload date', type: 'date', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: false },
{ name: 'file_id', label: 'File identifier', type: 'string', format: 'short', mandatoryType: 'internal', editDefault: false, multiple: false, upsert: false, isFilterable: false },
{ 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 },
{ name: 'removed', label: 'Removed', type: 'boolean', mandatoryType: 'no', editDefault: false, multiple: false, upsert: false, isFilterable: false },
];
] as const satisfies Array<AttributeDefinition>;

const attachmentAttributes = attributes[18];

type AttachmentAttributeMappingNames = typeof attachmentAttributes.mappings[number]['name'][];

// Make sure there's an attachment mapping for each field extracted
// by the `attachment` ingest processor, exhaustively.
assertType<
AttachmentAttributeMappingNames,
AttachmentProcessorExtractedProp[]
>(attachmentAttributes.mappings.map(({ name }) => name));

schemaAttributesDefinition.registerAttributes(ENTITY_TYPE_INTERNAL_FILE, attributes);
6 changes: 0 additions & 6 deletions opencti-platform/opencti-graphql/src/types/type-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,3 @@ import { isNotEmptyField } from '../database/utils';
export const filterEmpty = <T>(data: T | null | undefined): data is T => {
return isNotEmptyField(data);
};

/**
* Inverse operation of the built-in Readonly<T> utility type:
* makes all records of an object mutable.
*/
export type Mutable<T> = { -readonly [P in keyof T]: T[P]; };
27 changes: 27 additions & 0 deletions opencti-platform/opencti-graphql/src/utils/type-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Inverse operation of the built-in Readonly<T> utility type:
* makes all records of an object mutable.
*/
export type Mutable<T> = { -readonly [P in keyof T]: T[P]; };

type AssertEqual<T, TExpected> = [T] extends [TExpected]
? [TExpected] extends [T]
? T
: never
: never;

/**
* Compile-time type assertion utility checking that a type T
* is exactly of type TExpected.
*
* @example
* ```
* type Fruit = 'banana' | 'strawberry' | 'pineapple';
* const allFruits = ['banana' as const, 'strawberry' as const, 'pineapple' as const];
* // Checks for exhaustiveness
* assertType<typeof allFruits[number][], Fruit[]>(allFruits);
* ```
*/
export const assertType = <T, TExpected>(_x: AssertEqual<T, TExpected>) => {
// noop
};