From 39ba9a51eedeca9c53f0fcf6286d058c9d11a3c0 Mon Sep 17 00:00:00 2001 From: manelcecs Date: Tue, 12 Nov 2024 11:59:59 +0100 Subject: [PATCH] Add runtime checks for 'input' columns --- src/domain-services/flows/flow-service.ts | 95 +++++++++++++++++++ .../flows/strategy/impl/utils.ts | 13 ++- src/utils/utils.ts | 6 ++ 3 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 src/utils/utils.ts diff --git a/src/domain-services/flows/flow-service.ts b/src/domain-services/flows/flow-service.ts index 2aaa3cd3..4d0a52d5 100644 --- a/src/domain-services/flows/flow-service.ts +++ b/src/domain-services/flows/flow-service.ts @@ -4,6 +4,7 @@ import { Op } from '@unocha/hpc-api-core/src/db/util/conditions'; import { type InstanceOfModel } from '@unocha/hpc-api-core/src/db/util/types'; import { createBrandedValue } from '@unocha/hpc-api-core/src/util/types'; import { Service } from 'typedi'; +import { isValidKey } from '../../utils/utils'; import { FlowObjectService } from '../flow-object/flow-object-service'; import type { FlowObject, @@ -85,10 +86,22 @@ export class FlowService { switch (entity) { case 'emergency': { + if ( + !isValidKey< + InstanceOfModel, + keyof InstanceOfModel + >(orderBy.column as keyof InstanceOfModel) + ) { + throw new Error( + `Invalid column ${orderBy.column} to sort by in ${orderBy.entity}` + ); + } + // Get emergency entities sorted const column = orderBy.column as keyof InstanceOfModel< Database['emergency'] >; + const orderByEmergency = { column, order: orderBy.order }; const emergencies = await database.emergency.find({ @@ -102,6 +115,16 @@ export class FlowService { break; } case 'globalCluster': { + if ( + !isValidKey< + InstanceOfModel, + keyof InstanceOfModel + >(orderBy.column as keyof InstanceOfModel) + ) { + throw new Error( + `Invalid column ${orderBy.column} to sort by in ${orderBy.entity}` + ); + } // Get globalCluster entities sorted const column = orderBy.column as keyof InstanceOfModel< Database['globalCluster'] @@ -119,6 +142,18 @@ export class FlowService { break; } case 'governingEntity': { + if ( + !isValidKey< + InstanceOfModel, + keyof InstanceOfModel + >( + orderBy.column as keyof InstanceOfModel + ) + ) { + throw new Error( + `Invalid column ${orderBy.column} to sort by in ${orderBy.entity}` + ); + } // Get governingEntity entities sorted const column = orderBy.column as keyof InstanceOfModel< Database['governingEntity'] @@ -136,6 +171,16 @@ export class FlowService { break; } case 'location': { + if ( + !isValidKey< + InstanceOfModel, + keyof InstanceOfModel + >(orderBy.column as keyof InstanceOfModel) + ) { + throw new Error( + `Invalid column ${orderBy.column} to sort by in ${orderBy.entity}` + ); + } // Get location entities sorted const column = orderBy.column as keyof InstanceOfModel< Database['location'] @@ -151,6 +196,16 @@ export class FlowService { break; } case 'organization': { + if ( + !isValidKey< + InstanceOfModel, + keyof InstanceOfModel + >(orderBy.column as keyof InstanceOfModel) + ) { + throw new Error( + `Invalid column ${orderBy.column} to sort by in ${orderBy.entity}` + ); + } // Get organization entities sorted const column = orderBy.column as keyof InstanceOfModel< Database['organization'] @@ -168,6 +223,16 @@ export class FlowService { break; } case 'plan': { + if ( + !isValidKey< + InstanceOfModel, + keyof InstanceOfModel + >(orderBy.column as keyof InstanceOfModel) + ) { + throw new Error( + `Invalid column ${orderBy.column} to sort by in ${orderBy.entity}` + ); + } // Get plan entities sorted const column = orderBy.column as keyof InstanceOfModel< Database['plan'] @@ -183,6 +248,16 @@ export class FlowService { break; } case 'project': { + if ( + !isValidKey< + InstanceOfModel, + keyof InstanceOfModel + >(orderBy.column as keyof InstanceOfModel) + ) { + throw new Error( + `Invalid column ${orderBy.column} to sort by in ${orderBy.entity}` + ); + } // Get project entities sorted const column = orderBy.column as keyof InstanceOfModel< Database['project'] @@ -198,6 +273,16 @@ export class FlowService { break; } case 'usageYear': { + if ( + !isValidKey< + InstanceOfModel, + keyof InstanceOfModel + >(orderBy.column as keyof InstanceOfModel) + ) { + throw new Error( + `Invalid column ${orderBy.column} to sort by in ${orderBy.entity}` + ); + } // Get usageYear entities sorted const column = orderBy.column as keyof InstanceOfModel< Database['usageYear'] @@ -213,6 +298,16 @@ export class FlowService { break; } case 'planVersion': { + if ( + !isValidKey< + InstanceOfModel, + keyof InstanceOfModel + >(orderBy.column as keyof InstanceOfModel) + ) { + throw new Error( + `Invalid column ${orderBy.column} to sort by in ${orderBy.entity}` + ); + } // Get planVersion entities sorted // Collect fisrt part of the entity key by the fisrt Case letter const entityKey = `${ diff --git a/src/domain-services/flows/strategy/impl/utils.ts b/src/domain-services/flows/strategy/impl/utils.ts index 2322720f..616d4e76 100644 --- a/src/domain-services/flows/strategy/impl/utils.ts +++ b/src/domain-services/flows/strategy/impl/utils.ts @@ -3,6 +3,7 @@ import { Cond, Op } from '@unocha/hpc-api-core/src/db/util/conditions'; import type { InstanceDataOf } from '@unocha/hpc-api-core/src/db/util/model-definition'; import { type InstanceOfModel } from '@unocha/hpc-api-core/src/db/util/types'; import { createBrandedValue } from '@unocha/hpc-api-core/src/util/types'; +import * as t from 'io-ts'; import { type OrderBy } from '../../../../utils/database-types'; import { type SortOrder } from '../../../../utils/graphql/pagination'; import { type EntityDirection } from '../../../base-types'; @@ -40,7 +41,8 @@ export const defaultSearchFlowFilter: FlowWhere = { type FlowOrderByCommon = { order: SortOrder; - direction?: EntityDirection; + direction: EntityDirection; + subEntity?: string; }; export type FlowOrderBy = FlowOrderByCommon & @@ -91,6 +93,8 @@ export type FlowOrderBy = FlowOrderByCommon & } ); +export type FlowOrderByCodec = t.Type; + export const mapFlowCategoryConditionsToWhereClause = ( flowCategoryConditions: FlowCategory[] ) => { @@ -150,7 +154,7 @@ export const mapFlowCategoryConditionsToWhereClause = ( }; export const mapFlowOrderBy = ( - orderBy?: FlowOrderByWithSubEntity + orderBy?: FlowOrderBy | FlowOrderByWithSubEntity ): OrderBy => { if (!orderBy || orderBy.entity !== 'flow') { return defaultFlowOrderBy(); @@ -371,9 +375,9 @@ export const buildOrderBy = ( const orderBy: FlowOrderByWithSubEntity = { column: sortField ?? 'updatedAt', order: sortOrder ?? ('desc' as SortOrder), - direction: undefined, + direction: 'source' as EntityDirection, entity: 'flow', - }; + } satisfies FlowOrderByWithSubEntity; // Check if sortField is a nested property if (orderBy.column.includes('.')) { @@ -391,6 +395,7 @@ export const buildOrderBy = ( if (struct.length === 2) { orderBy.column = struct[1]; orderBy.entity = struct[0]; + orderBy; } else if (struct.length === 3) { orderBy.column = struct[2]; orderBy.direction = struct[1] as EntityDirection; diff --git a/src/utils/utils.ts b/src/utils/utils.ts new file mode 100644 index 00000000..579ba04a --- /dev/null +++ b/src/utils/utils.ts @@ -0,0 +1,6 @@ +export const isValidKey = ( + key: keyof T +): key is Q => { + const runtimeKeys = Object.keys({} as T) as (keyof T)[]; + return runtimeKeys.includes(key as keyof T); +};