-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add 'search-flow-by-filters-strategy' implementation
Nested strategies to find flowIDs from different filters
- Loading branch information
Showing
5 changed files
with
653 additions
and
0 deletions.
There are no files selected for viewing
22 changes: 22 additions & 0 deletions
22
src/domain-services/flows/strategy/flowID-search-strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { type Database } from '@unocha/hpc-api-core/src/db'; | ||
import type Knex from 'knex'; | ||
import { type FlowCategory, type NestedFlowFilters } from '../graphql/args'; | ||
import { type FlowShortcutFilter } from '../graphql/types'; | ||
import { type UniqueFlowEntity } from '../model'; | ||
|
||
export interface FlowIdSearchStrategyResponse { | ||
flows: UniqueFlowEntity[]; | ||
} | ||
|
||
export interface FlowIdSearchStrategyArgs { | ||
databaseConnection: Knex; | ||
models: Database; | ||
flowObjectsConditions?: any; | ||
flowCategoryConditions?: FlowCategory[]; | ||
nestedFlowFilters?: NestedFlowFilters; | ||
shortcutFilter?: FlowShortcutFilter; | ||
} | ||
|
||
export interface FlowIDSearchStrategy { | ||
search(args: FlowIdSearchStrategyArgs): Promise<FlowIdSearchStrategyResponse>; | ||
} |
122 changes: 122 additions & 0 deletions
122
...domain-services/flows/strategy/impl/get-flowIds-flow-category-conditions-strategy-impl.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { type CategoryId } from '@unocha/hpc-api-core/src/db/models/category'; | ||
import { Op } from '@unocha/hpc-api-core/src/db/util/conditions'; | ||
import { createBrandedValue } from '@unocha/hpc-api-core/src/util/types'; | ||
import { Service } from 'typedi'; | ||
import { CategoryService } from '../../../categories/category-service'; | ||
import { type UniqueFlowEntity } from '../../model'; | ||
import { | ||
type FlowIDSearchStrategy, | ||
type FlowIdSearchStrategyArgs, | ||
type FlowIdSearchStrategyResponse, | ||
} from '../flowID-search-strategy'; | ||
import { mapFlowCategoryConditionsToWhereClause } from './utils'; | ||
|
||
@Service() | ||
export class GetFlowIdsFromCategoryConditionsStrategyImpl | ||
implements FlowIDSearchStrategy | ||
{ | ||
constructor(private readonly categoryService: CategoryService) {} | ||
|
||
async search( | ||
args: FlowIdSearchStrategyArgs | ||
): Promise<FlowIdSearchStrategyResponse> { | ||
const { | ||
models, | ||
flowCategoryConditions, | ||
shortcutFilter, | ||
databaseConnection, | ||
} = args; | ||
|
||
let categoriesIds: CategoryId[] = []; | ||
|
||
let whereClause = null; | ||
if (flowCategoryConditions) { | ||
whereClause = mapFlowCategoryConditionsToWhereClause( | ||
flowCategoryConditions | ||
); | ||
} | ||
if (whereClause) { | ||
const categories = await this.categoryService.findCategories( | ||
models, | ||
whereClause | ||
); | ||
|
||
categoriesIds = categories.map((category) => category.id); | ||
} | ||
|
||
// Add category IDs from shortcut filter | ||
// to the list of category IDs IN or NOT_IN | ||
const categoriesIdsFromShortcutFilterIN: CategoryId[] = []; | ||
const categoriesIdsFromShortcutFilterNOTIN: CategoryId[] = []; | ||
|
||
if (shortcutFilter) { | ||
for (const shortcut of shortcutFilter) { | ||
if (shortcut.operation === Op.IN) { | ||
categoriesIdsFromShortcutFilterIN.push( | ||
createBrandedValue(shortcut.id) | ||
); | ||
} else { | ||
categoriesIdsFromShortcutFilterNOTIN.push( | ||
createBrandedValue(shortcut.id) | ||
); | ||
} | ||
} | ||
} | ||
|
||
let joinQuery = databaseConnection! | ||
.queryBuilder() | ||
.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) { | ||
joinQuery = joinQuery.andWhere(function () { | ||
this.where('categoryRef.categoryID', 'IN', categoriesIds) | ||
.andWhere('categoryRef.objectType', 'flow') | ||
.andWhere('flow.deletedAt', null); | ||
}); | ||
} | ||
|
||
if (categoriesIdsFromShortcutFilterIN.length > 0) { | ||
joinQuery = joinQuery.andWhere(function () { | ||
this.where( | ||
'categoryRef.categoryID', | ||
'IN', | ||
categoriesIdsFromShortcutFilterIN | ||
) | ||
.andWhere('categoryRef.objectType', 'flow') | ||
.andWhere('flow.deletedAt', null); | ||
}); | ||
} | ||
|
||
if (categoriesIdsFromShortcutFilterNOTIN.length > 0) { | ||
joinQuery = joinQuery.andWhere(function () { | ||
this.where( | ||
'categoryRef.categoryID', | ||
'NOT IN', | ||
categoriesIdsFromShortcutFilterNOTIN | ||
) | ||
.andWhere('categoryRef.objectType', 'flow') | ||
.andWhere('flow.deletedAt', null); | ||
}); | ||
} | ||
|
||
const flows = await joinQuery; | ||
|
||
const mapFlows: UniqueFlowEntity[] = flows.map( | ||
(flow) => | ||
({ | ||
id: flow.id, | ||
versionID: flow.versionID, | ||
}) as UniqueFlowEntity | ||
); | ||
|
||
return { flows: mapFlows }; | ||
} | ||
} |
113 changes: 113 additions & 0 deletions
113
...n-services/flows/strategy/impl/get-flowIds-flow-from-nested-flow-filters-strategy-impl.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
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 { | ||
buildSearchFlowsConditions, | ||
defaultFlowOrderBy, | ||
intersectUniqueFlowEntities, | ||
} from './utils'; | ||
|
||
@Service() | ||
export class GetFlowIdsFromNestedFlowFiltersStrategyImpl | ||
implements FlowIDSearchStrategy | ||
{ | ||
constructor( | ||
private readonly reportDetailService: ReportDetailService, | ||
private readonly legacyService: LegacyService, | ||
private readonly externalRefenceService: ExternalReferenceService, | ||
private readonly flowService: FlowService | ||
) {} | ||
|
||
async search( | ||
args: FlowIdSearchStrategyArgs | ||
): Promise<FlowIdSearchStrategyResponse> { | ||
const { databaseConnection, models, nestedFlowFilters } = args; | ||
|
||
let flowsReporterReferenceCode: UniqueFlowEntity[] = []; | ||
let flowsSourceSystemId: UniqueFlowEntity[] = []; | ||
let flowsSystemId: UniqueFlowEntity[] = []; | ||
const flowsLegacyId: UniqueFlowEntity[] = []; | ||
|
||
// Get the flowIDs using 'reporterReferenceCode' | ||
if (nestedFlowFilters?.reporterRefCode) { | ||
flowsReporterReferenceCode = | ||
await this.reportDetailService.getUniqueFlowIDsFromReportDetailsByReporterReferenceCode( | ||
models, | ||
nestedFlowFilters.reporterRefCode | ||
); | ||
} | ||
|
||
// Get the flowIDs using 'sourceSystemID' from 'reportDetail' | ||
if (nestedFlowFilters?.sourceSystemID) { | ||
flowsSourceSystemId = | ||
await this.reportDetailService.getUniqueFlowIDsFromReportDetailsBySourceSystemID( | ||
models, | ||
nestedFlowFilters.sourceSystemID | ||
); | ||
} | ||
|
||
// Get the flowIDs using 'systemID' from 'externalRefecence' | ||
if (nestedFlowFilters?.systemID) { | ||
flowsSystemId = | ||
await this.externalRefenceService.getUniqueFlowIDsBySystemID( | ||
models, | ||
nestedFlowFilters.systemID | ||
); | ||
} | ||
|
||
// Get the flowIDs using 'legacyID' | ||
if (nestedFlowFilters?.legacyID) { | ||
const flowID = await this.legacyService.getFlowIdFromLegacyId( | ||
models, | ||
nestedFlowFilters.legacyID | ||
); | ||
|
||
if (flowID) { | ||
flowsLegacyId.push({ | ||
id: flowID, | ||
versionID: 1, | ||
}); | ||
} | ||
} | ||
|
||
// Intersect the flowIDs from the nestedFlowFilters | ||
const flowIDsFromNestedFlowFilters: UniqueFlowEntity[] = | ||
intersectUniqueFlowEntities( | ||
flowsReporterReferenceCode, | ||
flowsSourceSystemId, | ||
flowsSystemId, | ||
flowsLegacyId | ||
); | ||
|
||
if (flowIDsFromNestedFlowFilters.length === 0) { | ||
return { flows: [] }; | ||
} | ||
// 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 uniqueFlowEntitiesNotDeleted = []; | ||
|
||
// Slice the flowIDs in chunks of 1000 to avoid the SQL query limit | ||
for (let i = 0; i < flowIDsFromNestedFlowFilters.length; i += 1000) { | ||
const getFlowArgs = { | ||
databaseConnection, | ||
orderBy: defaultFlowOrderBy(), | ||
whereClauses: buildSearchFlowsConditions( | ||
flowIDsFromNestedFlowFilters.slice(i, i + 1000) | ||
), | ||
}; | ||
const uniqueFlowsNotDeleted = | ||
await this.flowService.getFlowsAsUniqueFlowEntity(getFlowArgs); | ||
uniqueFlowEntitiesNotDeleted.push(...uniqueFlowsNotDeleted); | ||
} | ||
return { flows: uniqueFlowEntitiesNotDeleted }; | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
src/domain-services/flows/strategy/impl/get-flowIds-flow-object-conditions-strategy-impl.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
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, | ||
private readonly flowService: FlowService | ||
) {} | ||
|
||
async search( | ||
args: FlowIdSearchStrategyArgs | ||
): Promise<FlowIdSearchStrategyResponse> { | ||
const { flowObjectsConditions, databaseConnection } = args; | ||
|
||
// 1. Obtain flowIDs from flowObjects | ||
const flowsFromFilteredFlowObjects: UniqueFlowEntity[] = []; | ||
const flowObjects = | ||
await this.flowObjectService.getFlowObjectsByFlowObjectConditions( | ||
databaseConnection, | ||
flowObjectsConditions | ||
); | ||
|
||
// 1.1. Check if flowObjects is undefined | ||
if (!flowObjects) { | ||
return { flows: [] }; | ||
} | ||
|
||
// 2. Map flowObjects to UniqueFlowEntity and store in flowsFromFilteredFlowObjects | ||
for (const flowObject of flowObjects) { | ||
flowsFromFilteredFlowObjects.push({ | ||
id: flowObject.flowID, | ||
versionID: flowObject.versionID, | ||
}); | ||
} | ||
|
||
// 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 uniqueFlowsNotDeleted: UniqueFlowEntity[] = []; | ||
|
||
// Slice the flowIDs in chunks of 1000 to avoid the SQL query limit | ||
for (let i = 0; i < flowsFromFilteredFlowObjects.length; i += 1000) { | ||
const getFlowArgs = { | ||
databaseConnection, | ||
orderBy: defaultFlowOrderBy(), | ||
whereClauses: buildSearchFlowsConditions( | ||
flowsFromFilteredFlowObjects.slice(i, i + 1000) | ||
), | ||
}; | ||
const uniqueFlowsNotDeletedSlice = | ||
await this.flowService.getFlowsAsUniqueFlowEntity(getFlowArgs); | ||
uniqueFlowsNotDeleted.push(...uniqueFlowsNotDeletedSlice); | ||
} | ||
|
||
return { flows: uniqueFlowsNotDeleted }; | ||
} | ||
} |
Oops, something went wrong.