From 89caaa4851e094e9679eec24c92fbd17970a012f Mon Sep 17 00:00:00 2001 From: manelcecs Date: Tue, 16 Apr 2024 10:41:27 +0200 Subject: [PATCH] Remove 'searchTotalAmountUSD' resolver Remove methods and types related to 'searchTotalAmountUSD' resolver Refactor after removing 'searchTotalAmountUSD' needs from the search methods and strategies Added extra steps when filtering to discart 'deleted' flows --- .../flows/flow-search-service.ts | 163 ++---------------- src/domain-services/flows/flow-service.ts | 12 +- src/domain-services/flows/graphql/resolver.ts | 22 +-- src/domain-services/flows/graphql/types.ts | 9 - src/domain-services/flows/model.ts | 3 +- .../flows/strategy/flow-search-strategy.ts | 4 +- ...-flow-category-conditions-strategy-impl.ts | 49 ++++-- ...-from-nested-flow-filters-strategy-impl.ts | 24 ++- ...ds-flow-object-conditions-strategy-impl.ts | 20 ++- .../search-flow-by-filters-strategy-impl.ts | 154 +++-------------- .../flows/strategy/impl/utils.ts | 34 +++- 11 files changed, 158 insertions(+), 336 deletions(-) diff --git a/src/domain-services/flows/flow-search-service.ts b/src/domain-services/flows/flow-search-service.ts index 64a1a29a..2cc466de 100644 --- a/src/domain-services/flows/flow-search-service.ts +++ b/src/domain-services/flows/flow-search-service.ts @@ -22,29 +22,23 @@ import { ReportDetailService } from '../report-details/report-detail-service'; import { type UsageYear } from '../usage-years/grpahql/types'; import { UsageYearService } from '../usage-years/usage-year-service'; import { FlowService } from './flow-service'; -import { +import type { + FlowCategory, + FlowObjectFilters, + NestedFlowFilters, + SearchFlowsArgs, SearchFlowsFilters, - type FlowCategory, - type FlowObjectFilters, - type NestedFlowFilters, - type SearchFlowsArgs, - type SearchFlowsArgsNonPaginated, } from './graphql/args'; -import { - type Flow, - type FlowParkedParentSource, - type FlowSearchResult, - type FlowSearchResultNonPaginated, - type FlowSearchTotalAmountResult, - type FlowShortcutFilter, - type FlowSortField, - type FlowStatusFilter, +import type { + Flow, + FlowParkedParentSource, + FlowSearchResult, + FlowSearchResultNonPaginated, + FlowShortcutFilter, + FlowSortField, + FlowStatusFilter, } from './graphql/types'; -import { - type FlowEntity, - type FlowNestedDirection, - type FlowOrderBy, -} from './model'; +import type { FlowEntity, FlowNestedDirection, FlowOrderBy } from './model'; import { type FlowSearchStrategy } from './strategy/flow-search-strategy'; import { OnlyFlowFiltersStrategy } from './strategy/impl/only-flow-conditions-strategy-impl'; import { SearchFlowByFiltersStrategy } from './strategy/impl/search-flow-by-filters-strategy-impl'; @@ -599,135 +593,6 @@ export class FlowSearchService { ); } - async searchTotalAmount( - models: Database, - databaseConnection: Knex, - args: SearchFlowsArgsNonPaginated - ): Promise { - let { flowFilters } = args; - const { - flowObjectFilters, - flowCategoryFilters, - pending: isPendingFlows, - commitment: isCommitmentFlows, - paid: isPaidFlows, - pledge: isPledgedFlows, - carryover: isCarryoverFlows, - parked: isParkedFlows, - pass_through: isPassThroughFlows, - standard: isStandardFlows, - nestedFlowFilters, - status, - } = args; - - if (!flowFilters) { - flowFilters = new SearchFlowsFilters(); - flowFilters.activeStatus = true; - } else if (!flowFilters.activeStatus) { - flowFilters.activeStatus = true; - } - - if (flowObjectFilters) { - const usageYearFilter = flowObjectFilters.find( - (filter) => filter.objectType === 'usageYear' - ); - - if (!usageYearFilter) { - // Find the flowObjectFilters since 2021 until currentYear - let startYear = 2021; - const currentYear = new Date().getFullYear(); - - const usageYearsArrayFilter: string[] = []; - while (startYear <= currentYear) { - usageYearsArrayFilter.push(startYear.toString()); - startYear++; - } - const usageYears = await models.usageYear.find({ - where: { - year: { - [Op.IN]: usageYearsArrayFilter, - }, - }, - }); - - for (const usageYear of usageYears) { - // Map the usageYear filters to the flowObjectFilters - const sourceUsageYearFilter: FlowObjectFilters = { - objectType: 'usageYear', - direction: 'source', - objectID: usageYear.id.valueOf(), - inclusive: true, - }; - - const destinationUsageYearFilter: FlowObjectFilters = { - objectType: 'usageYear', - direction: 'destination', - objectID: usageYear.id.valueOf(), - inclusive: true, - }; - - flowObjectFilters.push( - sourceUsageYearFilter, - destinationUsageYearFilter - ); - } - } - } - - // This filter MUST apply always - flowFilters.restricted = false; - - // Validate the shortcut filters - const shortcutFilter: ShortcutCategoryFilter[] | null = - this.mapShortcutFilters( - isPendingFlows, - isCommitmentFlows, - isPaidFlows, - isPledgedFlows, - isCarryoverFlows, - isParkedFlows, - isPassThroughFlows, - isStandardFlows - ); - - // Once we've gathered all the filters, we need to determine the strategy - // to use in order to obtain the flowIDs - const strategy: FlowSearchStrategy = this.determineStrategy( - flowFilters, - flowObjectFilters, - flowCategoryFilters, - nestedFlowFilters, - shortcutFilter, - status - ); - - const { flows, count } = await strategy.search({ - models, - databaseConnection, - flowFilters, - flowObjectFilters, - flowCategoryFilters, - nestedFlowFilters, - // Shortcuts for categories - shortcutFilter, - statusFilter: status, - }); - - const flowsAmountUSD: Array = flows.map( - (flow) => flow.amountUSD - ); - - const totalAmount = flowsAmountUSD.reduce((a, b) => +a + +b, 0); - - return { - totalAmountUSD: totalAmount.toLocaleString(undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }), - flowsCount: count, - }; - } - async searchBatches( models: Database, databaseConnection: Knex, diff --git a/src/domain-services/flows/flow-service.ts b/src/domain-services/flows/flow-service.ts index de18762b..fc0e7807 100644 --- a/src/domain-services/flows/flow-service.ts +++ b/src/domain-services/flows/flow-service.ts @@ -39,7 +39,14 @@ export class FlowService { async getFlowsAsUniqueFlowEntity( args: GetFlowsArgs ): Promise { - const { databaseConnection, orderBy, conditions, offset, limit } = args; + const { + databaseConnection, + orderBy, + conditions, + offset, + limit, + whereClauses, + } = args; let query = databaseConnection! .queryBuilder() @@ -51,6 +58,9 @@ export class FlowService { if (conditions) { query = applySearchFilters(query, conditions); } + if (whereClauses) { + query = query.andWhere(prepareCondition(whereClauses)); + } // Because this list can be really large (+330K entries), we need to split it in chunks // Using limit and offset as cursors diff --git a/src/domain-services/flows/graphql/resolver.ts b/src/domain-services/flows/graphql/resolver.ts index 78c4d9d5..c1fcdcf2 100644 --- a/src/domain-services/flows/graphql/resolver.ts +++ b/src/domain-services/flows/graphql/resolver.ts @@ -2,13 +2,8 @@ import { Args, Ctx, Query, Resolver } from 'type-graphql'; import { Service } from 'typedi'; import Context from '../../Context'; import { FlowSearchService } from '../flow-search-service'; -import { SearchFlowsArgs, SearchFlowsArgsNonPaginated } from './args'; -import { - Flow, - FlowSearchResult, - FlowSearchResultNonPaginated, - FlowSearchTotalAmountResult, -} from './types'; +import { SearchFlowsArgs } from './args'; +import { Flow, FlowSearchResult, FlowSearchResultNonPaginated } from './types'; @Service() @Resolver(Flow) @@ -28,19 +23,6 @@ export default class FlowResolver { ); } - @Query(() => FlowSearchTotalAmountResult) - async searchFlowsTotalAmountUSD( - @Ctx() context: Context, - @Args(() => SearchFlowsArgsNonPaginated, { validate: false }) - args: SearchFlowsArgsNonPaginated - ): Promise { - return await this.flowSearchService.searchTotalAmount( - context.models, - context.connection, - args - ); - } - @Query(() => FlowSearchResultNonPaginated) async searchFlowsBatches( @Ctx() context: Context, diff --git a/src/domain-services/flows/graphql/types.ts b/src/domain-services/flows/graphql/types.ts index 768d11c9..ab921638 100644 --- a/src/domain-services/flows/graphql/types.ts +++ b/src/domain-services/flows/graphql/types.ts @@ -175,15 +175,6 @@ export class FlowSearchResultNonPaginated { flowsCount: number; } -@ObjectType() -export class FlowSearchTotalAmountResult { - @Field(() => String, { nullable: false }) - totalAmountUSD: string; - - @Field(() => Number, { nullable: false }) - flowsCount: number; -} - export type FlowSortField = | 'flow.id' | 'flow.versionID' diff --git a/src/domain-services/flows/model.ts b/src/domain-services/flows/model.ts index b3b3e1e7..c838c077 100644 --- a/src/domain-services/flows/model.ts +++ b/src/domain-services/flows/model.ts @@ -24,7 +24,8 @@ export type FlowNestedDirection = 'source' | 'destination'; export type GetFlowsArgs = { models?: Database; databaseConnection?: Knex; - conditions: any; + conditions?: any; + whereClauses?: any; offset?: number; orderBy?: any; limit?: number; diff --git a/src/domain-services/flows/strategy/flow-search-strategy.ts b/src/domain-services/flows/strategy/flow-search-strategy.ts index 3cbb8e61..98402728 100644 --- a/src/domain-services/flows/strategy/flow-search-strategy.ts +++ b/src/domain-services/flows/strategy/flow-search-strategy.ts @@ -26,8 +26,8 @@ export interface FlowSearchArgs { nestedFlowFilters: NestedFlowFilters; shortcutFilter: FlowShortcutFilter; statusFilter: FlowStatusFilter | null; - limit?: number; - offset?: number; + limit: number; + offset: number; orderBy?: any; } diff --git a/src/domain-services/flows/strategy/impl/get-flowIds-flow-category-conditions-strategy-impl.ts b/src/domain-services/flows/strategy/impl/get-flowIds-flow-category-conditions-strategy-impl.ts index 95974de1..0f08538c 100644 --- a/src/domain-services/flows/strategy/impl/get-flowIds-flow-category-conditions-strategy-impl.ts +++ b/src/domain-services/flows/strategy/impl/get-flowIds-flow-category-conditions-strategy-impl.ts @@ -78,35 +78,56 @@ export class GetFlowIdsFromCategoryConditionsStrategyImpl } } - let query = databaseConnection! + let joinQuery = databaseConnection! .queryBuilder() - .distinct('objectID', 'versionID') - .from('categoryRef'); + .distinct('flow.id', 'flow.versionID') + .from('flow') + .join('categoryRef', function () { + this.on('flow.id', '=', 'categoryRef.objectID').andOn( + 'flow.versionID', + '=', + 'categoryRef.versionID' + ); + }); if (categoriesIds.length > 0) { - query = query - .orWhere('categoryID', 'IN', categoriesIds) - .andWhere('objectType', 'flow'); + joinQuery = joinQuery.orWhere(function () { + this.where('categoryRef.categoryID', 'IN', categoriesIds) + .andWhere('categoryRef.objectType', 'flow') + .andWhere('flow.deletedAt', null); + }); } if (categoriesIdsFromShortcutFilterIN.length > 0) { - query = query - .orWhere('categoryID', 'IN', categoriesIdsFromShortcutFilterIN) - .andWhere('objectType', 'flow'); + joinQuery = joinQuery.orWhere(function () { + this.where( + 'categoryRef.categoryID', + 'IN', + categoriesIdsFromShortcutFilterIN + ) + .andWhere('categoryRef.objectType', 'flow') + .andWhere('flow.deletedAt', null); + }); } if (categoriesIdsFromShortcutFilterNOTIN.length > 0) { - query = query - .orWhere('categoryID', 'NOT IN', categoriesIdsFromShortcutFilterNOTIN) - .andWhere('objectType', 'flow'); + joinQuery = joinQuery.orWhere(function () { + this.where( + 'categoryRef.categoryID', + 'NOT IN', + categoriesIdsFromShortcutFilterNOTIN + ) + .andWhere('categoryRef.objectType', 'flow') + .andWhere('flow.deletedAt', null); + }); } - const flows = await query; + const flows = await joinQuery; const mapFlows: UniqueFlowEntity[] = flows.map( (flow) => ({ - id: flow.objectID, + id: flow.id, versionID: flow.versionID, }) as UniqueFlowEntity ); diff --git a/src/domain-services/flows/strategy/impl/get-flowIds-flow-from-nested-flow-filters-strategy-impl.ts b/src/domain-services/flows/strategy/impl/get-flowIds-flow-from-nested-flow-filters-strategy-impl.ts index c3b2f493..d79070bb 100644 --- a/src/domain-services/flows/strategy/impl/get-flowIds-flow-from-nested-flow-filters-strategy-impl.ts +++ b/src/domain-services/flows/strategy/impl/get-flowIds-flow-from-nested-flow-filters-strategy-impl.ts @@ -2,13 +2,18 @@ import { Service } from 'typedi'; import { ExternalReferenceService } from '../../../external-reference/external-reference-service'; import { LegacyService } from '../../../legacy/legacy-service'; import { ReportDetailService } from '../../../report-details/report-detail-service'; +import { FlowService } from '../../flow-service'; import { type UniqueFlowEntity } from '../../model'; import { type FlowIDSearchStrategy, type FlowIdSearchStrategyArgs, type FlowIdSearchStrategyResponse, } from '../flowID-search-strategy'; -import { intersectUniqueFlowEntities } from './utils'; +import { + buildSearchFlowsConditions, + defaultFlowOrderBy, + intersectUniqueFlowEntities, +} from './utils'; @Service() export class GetFlowIdsFromNestedFlowFiltersStrategyImpl @@ -17,13 +22,14 @@ export class GetFlowIdsFromNestedFlowFiltersStrategyImpl constructor( private readonly reportDetailService: ReportDetailService, private readonly legacyService: LegacyService, - private readonly externalRefenceService: ExternalReferenceService + private readonly externalRefenceService: ExternalReferenceService, + private readonly flowService: FlowService ) {} async search( args: FlowIdSearchStrategyArgs ): Promise { - const { models, nestedFlowFilters } = args; + const { databaseConnection, models, nestedFlowFilters } = args; let flowsReporterReferenceCode: UniqueFlowEntity[] = []; let flowsSourceSystemId: UniqueFlowEntity[] = []; @@ -81,6 +87,16 @@ export class GetFlowIdsFromNestedFlowFiltersStrategyImpl flowsLegacyId ); - return { flows: flowIDsFromNestedFlowFilters }; + // Once gathered and disjoined the flowIDs from the nestedFlowFilters + // Look after this uniqueFlows in the flow table + // To verify the flow is not deleted + const getFlowArgs = { + databaseConnection, + orderBy: defaultFlowOrderBy(), + whereClauses: buildSearchFlowsConditions(flowIDsFromNestedFlowFilters), + }; + const uniqueFlowsNotDeleted = + await this.flowService.getFlowsAsUniqueFlowEntity(getFlowArgs); + return { flows: uniqueFlowsNotDeleted }; } } diff --git a/src/domain-services/flows/strategy/impl/get-flowIds-flow-object-conditions-strategy-impl.ts b/src/domain-services/flows/strategy/impl/get-flowIds-flow-object-conditions-strategy-impl.ts index 6b00e9ea..c1adc62c 100644 --- a/src/domain-services/flows/strategy/impl/get-flowIds-flow-object-conditions-strategy-impl.ts +++ b/src/domain-services/flows/strategy/impl/get-flowIds-flow-object-conditions-strategy-impl.ts @@ -1,17 +1,22 @@ import { Service } from 'typedi'; import { FlowObjectService } from '../../../flow-object/flow-object-service'; +import { FlowService } from '../../flow-service'; import { type UniqueFlowEntity } from '../../model'; import { type FlowIDSearchStrategy, type FlowIdSearchStrategyArgs, type FlowIdSearchStrategyResponse, } from '../flowID-search-strategy'; +import { buildSearchFlowsConditions, defaultFlowOrderBy } from './utils'; @Service() export class GetFlowIdsFromObjectConditionsStrategyImpl implements FlowIDSearchStrategy { - constructor(private readonly flowObjectService: FlowObjectService) {} + constructor( + private readonly flowObjectService: FlowObjectService, + private readonly flowService: FlowService + ) {} async search( args: FlowIdSearchStrategyArgs @@ -39,6 +44,17 @@ export class GetFlowIdsFromObjectConditionsStrategyImpl }); } - return { flows: flowsFromFilteredFlowObjects }; + // 3. Once we have the flowIDs + // Search in the Flow table for the flowIDs and versionIDs + // To verify that the flows are not deleted + + const searchFlowArgs = { + databaseConnection, + orderBy: defaultFlowOrderBy(), + whereClauses: buildSearchFlowsConditions(flowsFromFilteredFlowObjects), + }; + const uniqueFlowsNotDeleted = + await this.flowService.getFlowsAsUniqueFlowEntity(searchFlowArgs); + return { flows: uniqueFlowsNotDeleted }; } } diff --git a/src/domain-services/flows/strategy/impl/search-flow-by-filters-strategy-impl.ts b/src/domain-services/flows/strategy/impl/search-flow-by-filters-strategy-impl.ts index a445b204..2a1c7050 100644 --- a/src/domain-services/flows/strategy/impl/search-flow-by-filters-strategy-impl.ts +++ b/src/domain-services/flows/strategy/impl/search-flow-by-filters-strategy-impl.ts @@ -1,9 +1,7 @@ import { type Database } from '@unocha/hpc-api-core/src/db'; -import { Cond } from '@unocha/hpc-api-core/src/db/util/conditions'; import { Service } from 'typedi'; import { type OrderBy } from '../../../../utils/database-types'; import { FlowService } from '../../flow-service'; -import { type SearchFlowsFilters } from '../../graphql/args'; import type { FlowEntity, UniqueFlowEntity } from '../../model'; import type { FlowSearchArgs, @@ -15,6 +13,8 @@ import { GetFlowIdsFromCategoryConditionsStrategyImpl } from './get-flowIds-flow import { GetFlowIdsFromNestedFlowFiltersStrategyImpl } from './get-flowIds-flow-from-nested-flow-filters-strategy-impl'; import { GetFlowIdsFromObjectConditionsStrategyImpl } from './get-flowIds-flow-object-conditions-strategy-impl'; import { + buildSearchFlowsConditions, + defaultFlowOrderBy, intersectUniqueFlowEntities, mapFlowOrderBy, prepareFlowConditions, @@ -31,8 +31,6 @@ export class SearchFlowByFiltersStrategy implements FlowSearchStrategy { private readonly getFlowIdsFromNestedFlowFilters: GetFlowIdsFromNestedFlowFiltersStrategyImpl ) {} - private readonly MAX_DATABASE_CHUNK_SIZE = 5000; - async search(args: FlowSearchArgs): Promise { const { models, @@ -45,10 +43,9 @@ export class SearchFlowByFiltersStrategy implements FlowSearchStrategy { offset, shortcutFilter, statusFilter, + orderBy, } = args; - let { orderBy } = args; - // First, we need to check if we need to sort by a certain entity // and if so, we need to map the orderBy to be from that entity // obtain the entities relation to the flow @@ -95,9 +92,6 @@ export class SearchFlowByFiltersStrategy implements FlowSearchStrategy { } } - // Default orderBy for flows - orderBy = mapFlowOrderBy(orderBy); - // We need to fetch the flowIDs by the nestedFlowFilters // if there are any const isFilterByNestedFilters = nestedFlowFilters !== undefined; @@ -198,11 +192,12 @@ export class SearchFlowByFiltersStrategy implements FlowSearchStrategy { statusFilter ); - const flows: FlowEntity[] = await this.flowService.getFlows({ - models, - conditions: flowConditions, - orderBy, - }); + const flows: UniqueFlowEntity[] = + await this.flowService.getFlowsAsUniqueFlowEntity({ + databaseConnection, + conditions: flowConditions, + orderBy, + }); // If after this filter we have no flows, we can return an empty array if (flows.length === 0) { @@ -214,11 +209,7 @@ export class SearchFlowByFiltersStrategy implements FlowSearchStrategy { // When using the spread operator - a workaround is to use push fot each element // also, we need to map the FlowEntity to UniqueFlowEntity for (const flow of flows) { - const uniqueFlowEntity: UniqueFlowEntity = { - id: flow.id, - versionID: flow.versionID, - }; - flowsFromFlowFilters.push(uniqueFlowEntity); + flowsFromFlowFilters.push(flow); } } @@ -243,40 +234,19 @@ export class SearchFlowByFiltersStrategy implements FlowSearchStrategy { sortedFlows = deduplicatedFlows; } - // Store the count const count = sortedFlows.length; - let flows: FlowEntity[] = []; - // This should only happen when doing the totalAmountSearch - if ( - limit === undefined && - offset === undefined && - sortedFlows.length > this.MAX_DATABASE_CHUNK_SIZE - ) { - // We need to paginate over the searchConditions - // Then collect the flows - flows = await this.progresiveChunkSearch( - models, - sortedFlows, - 0, - this.MAX_DATABASE_CHUNK_SIZE, - orderBy, - [] - ); - } else { - // Store the flows promise - const flowsResponse = await this.progresiveSearch( - models, - sortedFlows, - limit ?? 0, - offset ?? 0, - orderBy, - [] - ); + const flowEntityOrderBy = defaultFlowOrderBy(); + // Store the flows promise + const flows = await this.progresiveSearch( + models, + sortedFlows, + limit, + offset ?? 0, + flowEntityOrderBy, + [] + ); - // Store the flows - flows = flowsResponse; - } return { flows, count }; } @@ -308,7 +278,7 @@ export class SearchFlowByFiltersStrategy implements FlowSearchStrategy { ): Promise { const reducedFlows = sortedFlows.slice(offset, offset + limit); - const whereConditions = this.buildConditions(reducedFlows); + const whereConditions = buildSearchFlowsConditions(reducedFlows); const flows = await this.flowService.getFlows({ models, @@ -333,86 +303,4 @@ export class SearchFlowByFiltersStrategy implements FlowSearchStrategy { flowResponse ); } - - /** - * This method have a similar behavior to the progresiveSearch method - * but the exit condition differs - * - * The exit condition is when - * the flowResponse length is equal to the sortedFlows length - * or the reducedFlows length is less than the chunkSize after doing the search - * @param models - * @param sortedFlows - * @param start - * @param chunkSize - * @param orderBy - * @param flowResponse - * @returns list of flows - */ - async progresiveChunkSearch( - models: Database, - sortedFlows: UniqueFlowEntity[], - start: number, - chunkSize: number, - orderBy: OrderBy, - flowResponse: FlowEntity[] - ): Promise { - const reducedFlows = sortedFlows.slice(start, start + chunkSize); - - const whereConditions = this.buildConditions(reducedFlows); - - const flows = await this.flowService.getFlows({ - models, - conditions: whereConditions, - orderBy, - }); - - flowResponse.push(...flows); - - if ( - flowResponse.length === sortedFlows.length || - reducedFlows.length < chunkSize - ) { - return flowResponse; - } - - // Recursive call - start += chunkSize; - return await this.progresiveChunkSearch( - models, - sortedFlows, - start, - chunkSize, - orderBy, - flowResponse - ); - } - - buildConditions( - uniqueFlowEntities: UniqueFlowEntity[], - flowFilters?: SearchFlowsFilters - ): any { - const whereClauses = uniqueFlowEntities.map((flow) => ({ - [Cond.AND]: [ - { id: flow.id }, - { versionID: flow.versionID }, - { deletedAt: null }, - ], - })); - - if (flowFilters) { - const flowConditions = prepareFlowConditions(flowFilters); - return { - [Cond.AND]: [ - { deletedAt: null }, - flowConditions, - { [Cond.OR]: whereClauses }, - ], - }; - } - - return { - [Cond.OR]: whereClauses, - }; - } } diff --git a/src/domain-services/flows/strategy/impl/utils.ts b/src/domain-services/flows/strategy/impl/utils.ts index f1d7a2eb..25d0fc9e 100644 --- a/src/domain-services/flows/strategy/impl/utils.ts +++ b/src/domain-services/flows/strategy/impl/utils.ts @@ -114,7 +114,7 @@ export const sortingColumnMapping: Map = new Map< export function mapFlowOrderBy(orderBy: any): OrderBy { if (!orderBy) { - return { column: 'updatedAt', order: 'DESC' }; + return defaultFlowOrderBy(); } if (orderBy.entity === 'flow') { @@ -134,6 +134,10 @@ export function mapFlowOrderBy(orderBy: any): OrderBy { return { column: columnToSort, order: orderBy.order }; } +export function defaultFlowOrderBy(): OrderBy { + return { column: 'updatedAt', order: 'DESC' }; +} + export function prepareFlowConditions(flowFilters: SearchFlowsFilters): any { let flowConditions = {}; @@ -290,3 +294,31 @@ export function prepareFlowStatusConditions( } return flowConditions; } + +export function buildSearchFlowsConditions( + uniqueFlowEntities: UniqueFlowEntity[], + flowFilters?: SearchFlowsFilters +): any { + const whereClauses = uniqueFlowEntities.map((flow) => ({ + [Cond.AND]: [ + { id: flow.id }, + { versionID: flow.versionID }, + { deletedAt: null }, + ], + })); + + if (flowFilters) { + const flowConditions = prepareFlowConditions(flowFilters); + return { + [Cond.AND]: [ + { deletedAt: null }, + flowConditions, + { [Cond.OR]: whereClauses }, + ], + }; + } + + return { + [Cond.OR]: whereClauses, + }; +}