Skip to content

Commit

Permalink
Add 'only-flow-conditions-strategy' implementation
Browse files Browse the repository at this point in the history
Add also utils to operate over flows
  • Loading branch information
manelcecs committed Nov 26, 2024
1 parent 9e3e438 commit 2782b32
Show file tree
Hide file tree
Showing 5 changed files with 556 additions and 30 deletions.
13 changes: 9 additions & 4 deletions src/domain-services/flow-object/flow-object-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ 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 { type OrderByCond } from '@unocha/hpc-api-core/src/db/util/raw-model';
import type {
FieldsOfModel,
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 { type OrderBy } from '../../utils/database-types';
import { type UniqueFlowEntity } from '../flows/model';
import { buildSearchFlowsObjectConditions } from '../flows/strategy/impl/utils';
import { type FlowObjectFilterGrouped } from './model';
Expand All @@ -16,6 +19,8 @@ import { buildWhereConditionsForFlowObjectFilters } from './utils';
// Local types definition to increase readability
type FlowObjectModel = Database['flowObject'];
type FlowObjectInstance = InstanceOfModel<FlowObjectModel>;
export type FlowObjectsFieldsDefinition = FieldsOfModel<FlowObjectModel>;
export type FlowObjectOrderByCond = OrderByCond<FlowObjectsFieldsDefinition>;
export type FlowObjectWhere = Condition<FlowObjectInstance>;
@Service()
export class FlowObjectService {
Expand Down Expand Up @@ -75,7 +80,7 @@ export class FlowObjectService {
async getFlowsObjectsByFlows(
models: Database,
whereClauses: FlowObjectWhere,
orderBy?: OrderBy<FlowObjectModel['_internals']['fields']>
orderBy?: FlowObjectOrderByCond
): Promise<FlowObjectInstance[]> {
const distinctColumns: Array<keyof FlowObjectInstance> = [
'flowID',
Expand Down Expand Up @@ -104,7 +109,7 @@ export class FlowObjectService {
stopOnBatchSize: boolean,
responseList: FlowObjectInstance[],
flowObjectsWhere: FlowObjectWhere,
orderBy?: OrderBy<FlowObjectModel['_internals']['fields']>
orderBy?: FlowObjectOrderByCond
): Promise<FlowObjectInstance[]> {
const reducedFlows = referenceList.slice(offset, offset + batchSize);

Expand Down
89 changes: 65 additions & 24 deletions src/domain-services/flows/flow-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ import type {
IGetFlowsArgs,
UniqueFlowEntity,
} from './model';
import {
buildSearchFlowsConditions,
mapOrderByToEntityOrderBy,
} from './strategy/impl/utils';
import { buildSearchFlowsConditions } from './strategy/impl/utils';

@Service()
export class FlowService {
Expand Down Expand Up @@ -56,12 +53,11 @@ export class FlowService {
): Promise<UniqueFlowEntity[]> {
const entity = orderBy.subEntity ?? orderBy.entity;
// Get the entity list
const mappedOrderBy = mapOrderByToEntityOrderBy(orderBy);
// 'externalReference' is a special case
// because it does have a direct relation with flow
// and no direction
if (entity === 'externalReference') {
const column = mappedOrderBy.column as keyof InstanceOfModel<
const column = orderBy.column as keyof InstanceOfModel<
Database['externalReference']
>;
const externalReferences = await database.externalReference.find({
Expand Down Expand Up @@ -90,9 +86,14 @@ export class FlowService {
switch (entity) {
case 'emergency': {
// 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({
distinct: [mappedOrderBy.column, 'id'],
orderBy: mappedOrderBy,
distinct: [column, 'id'],
orderBy: orderByEmergency,
});

entityIDsSorted = emergencies.map((emergency) =>
Expand All @@ -102,9 +103,14 @@ export class FlowService {
}
case 'globalCluster': {
// Get globalCluster entities sorted
const column = orderBy.column as keyof InstanceOfModel<
Database['globalCluster']
>;
const orderByGlobalCluster = { column, order: orderBy.order };

const globalClusters = await database.globalCluster.find({
distinct: [mappedOrderBy.column, 'id'],
orderBy: mappedOrderBy,
distinct: [column, 'id'],
orderBy: orderByGlobalCluster,
});

entityIDsSorted = globalClusters.map((globalCluster) =>
Expand All @@ -114,9 +120,14 @@ export class FlowService {
}
case 'governingEntity': {
// Get governingEntity entities sorted
const column = orderBy.column as keyof InstanceOfModel<
Database['governingEntity']
>;
const orderByGoverningEntity = { column, order: orderBy.order };

const governingEntities = await database.governingEntity.find({
distinct: [mappedOrderBy.column, 'id'],
orderBy: mappedOrderBy,
distinct: [column, 'id'],
orderBy: orderByGoverningEntity,
});

entityIDsSorted = governingEntities.map((governingEntity) =>
Expand All @@ -126,19 +137,29 @@ export class FlowService {
}
case 'location': {
// Get location entities sorted
const column = orderBy.column as keyof InstanceOfModel<
Database['location']
>;
const orderByLocation = { column, order: orderBy.order };

const locations = await database.location.find({
distinct: [mappedOrderBy.column, 'id'],
orderBy: mappedOrderBy,
distinct: [column, 'id'],
orderBy: orderByLocation,
});

entityIDsSorted = locations.map((location) => location.id.valueOf());
break;
}
case 'organization': {
// Get organization entities sorted
const column = orderBy.column as keyof InstanceOfModel<
Database['organization']
>;
const orderByOrganization = { column, order: orderBy.order };

const organizations = await database.organization.find({
distinct: [mappedOrderBy.column, 'id'],
orderBy: mappedOrderBy,
distinct: [column, 'id'],
orderBy: orderByOrganization,
});

entityIDsSorted = organizations.map((organization) =>
Expand All @@ -148,29 +169,44 @@ export class FlowService {
}
case 'plan': {
// Get plan entities sorted
const column = orderBy.column as keyof InstanceOfModel<
Database['plan']
>;
const orderByPlan = { column, order: orderBy.order };

const plans = await database.plan.find({
distinct: [mappedOrderBy.column, 'id'],
orderBy: mappedOrderBy,
distinct: [column, 'id'],
orderBy: orderByPlan,
});

entityIDsSorted = plans.map((plan) => plan.id.valueOf());
break;
}
case 'project': {
// Get project entities sorted
const column = orderBy.column as keyof InstanceOfModel<
Database['project']
>;
const orderByProject = { column, order: orderBy.order };

const projects = await database.project.find({
distinct: [mappedOrderBy.column, 'id'],
orderBy: mappedOrderBy,
distinct: [column, 'id'],
orderBy: orderByProject,
});

entityIDsSorted = projects.map((project) => project.id.valueOf());
break;
}
case 'usageYear': {
// Get usageYear entities sorted
const column = orderBy.column as keyof InstanceOfModel<
Database['usageYear']
>;
const orderByUsageYear = { column, order: orderBy.order };

const usageYears = await database.usageYear.find({
distinct: [mappedOrderBy.column, 'id'],
orderBy: mappedOrderBy,
distinct: [column, 'id'],
orderBy: orderByUsageYear,
});

entityIDsSorted = usageYears.map((usageYear) => usageYear.id.valueOf());
Expand All @@ -183,9 +219,14 @@ export class FlowService {
entity.split(/[A-Z]/)[0]
}Id` as keyof InstanceOfModel<Database['planVersion']>;

const column = orderBy.column as keyof InstanceOfModel<
Database['planVersion']
>;
const orderByPlanVersion = { column, order: orderBy.order };

const planVersions = await database.planVersion.find({
distinct: [mappedOrderBy.column, entityKey],
orderBy: mappedOrderBy,
distinct: [column, entityKey],
orderBy: orderByPlanVersion,
});

entityIDsSorted = planVersions.map((planVersion) =>
Expand Down
7 changes: 5 additions & 2 deletions src/domain-services/flows/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import { type Database } from '@unocha/hpc-api-core/src/db';
import { type FlowId } from '@unocha/hpc-api-core/src/db/models/flow';
import { type Condition } from '@unocha/hpc-api-core/src/db/util/conditions';
import { type OrderByCond } from '@unocha/hpc-api-core/src/db/util/raw-model';
import type { FieldsOfModel, InstanceOfModel } from '@unocha/hpc-api-core/src/db/util/types';
import type {
FieldsOfModel,
InstanceOfModel,
} from '@unocha/hpc-api-core/src/db/util/types';
import { type SortOrder } from '../../utils/graphql/pagination';
import { type EntityDirection } from '../base-types';

export type FlowModel = Database['flow'];
export type FlowInstance = InstanceOfModel<FlowModel>;
export type FlowWhere = Condition<FlowInstance>;
export type FlowFieldsDefinition = FieldsOfModel<FlowModel>
export type FlowFieldsDefinition = FieldsOfModel<FlowModel>;
export type FlowOrderByCond = OrderByCond<FlowFieldsDefinition>; // Can this be simplified somehow?
export type UniqueFlowEntity = {
id: FlowId;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Cond, Op } from '@unocha/hpc-api-core/src/db/util/conditions';
import { Service } from 'typedi';
import { FlowService } from '../../flow-service';
import { type FlowWhere } from '../../model';
import {
type FlowSearchArgs,
type FlowSearchStrategy,
type FlowSearchStrategyResponse,
} from '../flow-search-strategy';
import {
mapFlowOrderBy,
prepareFlowConditions,
prepareFlowStatusConditions,
} from './utils';

@Service()
export class OnlyFlowFiltersStrategy implements FlowSearchStrategy {
constructor(private readonly flowService: FlowService) {}

async search(args: FlowSearchArgs): Promise<FlowSearchStrategyResponse> {
const { models, flowFilters, orderBy, limit, offset, statusFilter } = args;
// Map flowConditions to where clause
let flowConditions: FlowWhere = prepareFlowConditions(flowFilters);

// Add status filter conditions if provided
flowConditions = prepareFlowStatusConditions(flowConditions, statusFilter);

// Build conditions object
// We need to add the condition to filter the deletedAt field
const whereClause: FlowWhere = {
[Cond.AND]: [
{
deletedAt: {
[Op.IS_NULL]: true,
},
},
flowConditions ?? {},
],
};

const orderByFlow = mapFlowOrderBy(orderBy);

const [flows, countRes] = await Promise.all([
this.flowService.getFlows({
models,
conditions: whereClause,
offset,
orderBy: orderByFlow,
limit,
}),
await models.flow.count({
where: whereClause,
}),
]);

// Map count result query to count object
const countObject = countRes;

// on certain conditions, this conversion from 'bigint' to 'number' can cause a loss of precision
// But in order to reach that point, the number of flows would have to be in the billions
// that is not a realistic scenario for this application
// Nonetheless, we can validate that using Number.MAX_SAFE_INTEGER as a threshold
return { flows, count: Number(countObject) };
}
}
Loading

0 comments on commit 2782b32

Please sign in to comment.