Skip to content

Commit

Permalink
Add 'search-flow-by-filters-strategy' implementation
Browse files Browse the repository at this point in the history
Nested strategies to find flowIDs from different filters

Merge 5de9ca3
  • Loading branch information
manelcecs committed Nov 26, 2024
1 parent 2782b32 commit 27fae32
Show file tree
Hide file tree
Showing 5 changed files with 553 additions and 0 deletions.
21 changes: 21 additions & 0 deletions src/domain-services/flows/strategy/flowID-search-strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { type Database } from '@unocha/hpc-api-core/src/db';
import { type ShortcutCategoryFilter } from '../../categories/model';
import { type FlowObjectFilterGrouped } from '../../flow-object/model';
import { type FlowCategory, type NestedFlowFilters } from '../graphql/args';
import { type UniqueFlowEntity } from '../model';

export interface FlowIdSearchStrategyResponse {
flows: UniqueFlowEntity[];
}

export interface FlowIdSearchStrategyArgs {
models: Database;
flowObjectFilterGrouped?: FlowObjectFilterGrouped;
flowCategoryConditions?: FlowCategory[];
nestedFlowFilters?: NestedFlowFilters;
shortcutFilters?: ShortcutCategoryFilter[] | null;
}

export interface FlowIDSearchStrategy {
search(args: FlowIdSearchStrategyArgs): Promise<FlowIdSearchStrategyResponse>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { type Database } from '@unocha/hpc-api-core/src/db';
import { type CategoryId } from '@unocha/hpc-api-core/src/db/models/category';
import {
Op,
type Condition,
} 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 { FlowService } from '../../flow-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 flowService: FlowService) {}

async search(
args: FlowIdSearchStrategyArgs
): Promise<FlowIdSearchStrategyResponse> {
const { models, flowCategoryConditions, shortcutFilters } = args;

let categoriesIds: CategoryId[] = [];

let whereClause = null;
if (flowCategoryConditions) {
whereClause = mapFlowCategoryConditionsToWhereClause(
flowCategoryConditions
);
}
if (whereClause) {
const categories = await models.category.find({
where: 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 (shortcutFilters) {
for (const shortcut of shortcutFilters) {
if (shortcut.operation === Op.IN) {
categoriesIdsFromShortcutFilterIN.push(
createBrandedValue(shortcut.id)
);
} else {
categoriesIdsFromShortcutFilterNOTIN.push(
createBrandedValue(shortcut.id)
);
}
}
}

// Search categoriesRef with categoriesID IN and categoriesIdsFromShortcutFilterIN
// and categoriesIdsFromShortcutFilterNOTIN
const where: Condition<InstanceOfModel<Database['categoryRef']>> = {
objectType: 'flow',
};

if (categoriesIdsFromShortcutFilterNOTIN.length > 0) {
where['categoryID'] = {
[Op.NOT_IN]: categoriesIdsFromShortcutFilterNOTIN,
};
}

const categoriesIDsIN = [
...categoriesIds,
...categoriesIdsFromShortcutFilterIN,
];

if (categoriesIDsIN.length > 0) {
where['categoryID'] = { [Op.IN]: categoriesIDsIN };
}

const categoriesRef = await models.categoryRef.find({
where,
distinct: ['objectID', 'versionID'],
});

// Map categoryRef to UniqueFlowEntity (flowId and versionID)
const flowIDsFromCategoryRef: UniqueFlowEntity[] = categoriesRef.map(
(catRef) => ({
id: createBrandedValue(catRef.objectID),
versionID: catRef.versionID,
})
);
return { flows: flowIDsFromCategoryRef };
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
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';

@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 { 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
const flows = await this.flowService.progresiveSearch(
models,
flowIDsFromNestedFlowFilters,
1000,
0,
false, // Stop when we have the limit
[]
);

return { flows };
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Op } from '@unocha/hpc-api-core/src/db/util/conditions';
import { Service } from 'typedi';
import { type UniqueFlowEntity } from '../../model';
import {
type FlowIDSearchStrategy,
type FlowIdSearchStrategyArgs,
type FlowIdSearchStrategyResponse,
} from '../flowID-search-strategy';
import { intersectUniqueFlowEntities } from './utils';

@Service()
export class GetFlowIdsFromObjectConditionsStrategyImpl
implements FlowIDSearchStrategy
{
constructor() {}

async search(
args: FlowIdSearchStrategyArgs
): Promise<FlowIdSearchStrategyResponse> {
const { flowObjectFilterGrouped, models } = args;

if (!flowObjectFilterGrouped) {
return { flows: [] };
}

let intersectedFlows: UniqueFlowEntity[] = [];

for (const [flowObjectType, group] of flowObjectFilterGrouped.entries()) {
for (const [direction, ids] of group.entries()) {
const condition = {
objectType: flowObjectType,
refDirection: direction,
objectID: { [Op.IN]: ids },
};
const flowObjectsFound = await models.flowObject.find({
where: condition,
});

const uniqueFlowObjectsEntities: UniqueFlowEntity[] =
flowObjectsFound.map(
(flowObject) =>
({
id: flowObject.flowID,
versionID: flowObject.versionID,
}) satisfies UniqueFlowEntity
);

intersectedFlows = intersectUniqueFlowEntities(
intersectedFlows,
uniqueFlowObjectsEntities
);
}
}

return { flows: intersectedFlows };
}
}
Loading

0 comments on commit 27fae32

Please sign in to comment.