Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
manelcecs committed Nov 10, 2023
1 parent 8e9cfd1 commit 54ac974
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 39 deletions.
5 changes: 3 additions & 2 deletions src/domain-services/flows/flow-link-service.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { FlowId } from '@unocha/hpc-api-core/src/db/models/flow';
import { Database } from '@unocha/hpc-api-core/src/db/type';
import { Op } from '@unocha/hpc-api-core/src/db/util/conditions';
import { InstanceOfModel } from '@unocha/hpc-api-core/src/db/util/types';
import { Service } from 'typedi';

@Service()
export class FlowLinkService {
async getFlowLinksForFlows(
flowIds: FlowId[],
models: Database
): Promise<Map<number, any[]>> {
): Promise<Map<number, InstanceOfModel<Database['flowLink']>[]>> {
const flowLinks = await models.flowLink.find({
where: {
childID: {
Expand All @@ -18,7 +19,7 @@ export class FlowLinkService {
});

// Group flowLinks by flow ID for easy mapping
const flowLinksMap = new Map<number, any[]>();
const flowLinksMap = new Map<number, InstanceOfModel<Database['flowLink']>[]>();

// Populate the map with flowLinks for each flow
flowLinks.forEach((flowLink) => {
Expand Down
112 changes: 95 additions & 17 deletions src/domain-services/flows/flow-search-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ import { FlowId } from '@unocha/hpc-api-core/src/db/models/flow';
import { FlowLinkService } from './flow-link-service';
import { ExternalReferenceService } from '../external-reference/external-reference-service';
import { ReportDetailService } from '../report-details/report-detail-service';
import { FlowService } from './flow-service';
import {
FlowObjectFilters,
SearchFlowsArgs,
SearchFlowsFilters,
} from './graphql/args';

@Service()
export class FlowSearchService {
Expand All @@ -27,18 +33,16 @@ export class FlowSearchService {
private readonly categoryService: CategoryService,
private readonly flowLinkService: FlowLinkService,
private readonly externalReferenceService: ExternalReferenceService,
private readonly reportDetailService: ReportDetailService
private readonly reportDetailService: ReportDetailService,
private readonly flowService: FlowService
) {}

async search(
models: Database,
limit: number = 50,
sortOrder: 'asc' | 'desc' = 'desc',
sortField: FlowSortField = 'id',
afterCursor?: number,
beforeCursor?: number,
filters?: any
filters: SearchFlowsArgs
): Promise<FlowSearchResult> {
const { limit, afterCursor, beforeCursor, sortField, sortOrder } = filters;

if (beforeCursor && afterCursor) {
throw new Error('Cannot use before and after cursor at the same time');
}
Expand All @@ -50,6 +54,28 @@ export class FlowSearchService {

const limitComputed = limit + 1; // Fetch one more item to check for hasNextPage

const { flowFilters, flowObjectFilters } = filters;

let onlyFlowFilters = false;
let onlyFlowObjectFilters = false;
let bothFlowFilters = false;

if (
(!flowFilters && !flowObjectFilters) ||
(flowFilters && !flowObjectFilters)
) {
onlyFlowFilters = true;
} else if (!flowFilters && flowObjectFilters) {
onlyFlowObjectFilters = true;
} else if (flowFilters && flowObjectFilters) {
bothFlowFilters = true;
}

const { flowObjectConditions, flowConditions } = [
this.prepareFlowObjectConditions(flowObjectFilters),
this.prepareFlowConditions(flowFilters),
];

let condition;
if (afterCursor) {
condition = {
Expand All @@ -73,12 +99,13 @@ export class FlowSearchService {
}

const [flows, countRes] = await Promise.all([
models.flow.find({
orderBy: sortCondition,
limit: limitComputed,
where: condition,
}),
models.flow.count({ where: condition }),
this.flowService.getFlows(
models,
condition,
sortCondition,
limitComputed
),
this.flowService.getFlowsCount(models, condition),
]);

const hasNextPage = flows.length > limit;
Expand Down Expand Up @@ -138,7 +165,7 @@ export class FlowSearchService {
const flowLink = flowLinksMap.get(flow.id) || [];
const categories = categoriesMap.get(flow.id) || [];
const organizations = organizationsMap.get(flow.id) || [];
const locations = [...locationsMap.get(flow.id) || []] ;
const locations = [...(locationsMap.get(flow.id) || [])];
const plans = plansMap.get(flow.id) || [];
const usageYears = usageYearsMap.get(flow.id) || [];
const externalReferences = externalReferencesMap.get(flow.id) || [];
Expand All @@ -149,14 +176,19 @@ export class FlowSearchService {
this.getParketParents(flow, flowLink, models, parkedParentSource);
}

// TODO: change and use flow.depth to verify (depth > 0)
const childIDs: number[] = flowLinksMap
.get(flow.id)
?.map((flowLink) => flowLink.childID.valueOf()) as number[];
?.filter(
(flowLink) => flowLink.parentID === flow.id && flowLink.depth > 0
)
.map((flowLink) => flowLink.childID.valueOf()) as number[];

const parentIDs: number[] = flowLinksMap
.get(flow.id)
?.map((flowLink) => flowLink.parentID.valueOf()) as number[];
?.filter(
(flowLink) => flowLink.childID === flow.id && flowLink.depth > 0
)
.map((flowLink) => flowLink.parentID.valueOf()) as number[];

return {
// Mandatory fields
Expand Down Expand Up @@ -268,4 +300,50 @@ export class FlowSearchService {
);
});
}

private prepareFlowObjectConditions(
flowObjectFilters: FlowObjectFilters[]
): Map<string, Map<string, number[]>> {
const flowObjectConditions = new Map<string, Map<string, number[]>>();

for (const flowObjectFilter of flowObjectFilters || []) {
const objectType = flowObjectFilter.objectType;
const direction = flowObjectFilter.direction;
const objectID = flowObjectFilter.objectID;

// Ensure the map for the objectType is initialized
if (!flowObjectConditions.has(objectType)) {
flowObjectConditions.set(objectType, new Map<string, number[]>());
}

const flowObjectCondition = flowObjectConditions.get(objectType);

// Ensure the map for the direction is initialized
if (!flowObjectCondition!.has(direction)) {
flowObjectCondition!.set(direction, []);
}

const flowObjectDirectionCondition = flowObjectCondition!.get(direction);

// Add the objectID to the array
flowObjectDirectionCondition!.push(objectID);
}

return flowObjectConditions;
}

private prepareFlowConditions(flowFilters: SearchFlowsFilters): Map<string, any> {
const flowConditions = new Map<string, any>();

if (flowFilters) {
Object.entries(flowFilters).forEach(([key, value]) => {
if (value !== undefined) {
flowConditions.set(key, value);
}
});
}

return flowConditions;
}

}
24 changes: 24 additions & 0 deletions src/domain-services/flows/flow-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Service } from 'typedi';
import { Database } from '@unocha/hpc-api-core/src/db/type';

@Service()
export class FlowService {
constructor() {}

async getFlows(
models: Database,
conditions: any,
orderBy: any,
limit: number
) {
return await models.flow.find({
orderBy,
limit,
where: conditions,
});
}

async getFlowsCount(models: Database, conditions: any) {
return await models.flow.count({ where: conditions });
}
}
63 changes: 62 additions & 1 deletion src/domain-services/flows/graphql/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,73 @@ import { PaginationArgs } from '../../../utils/graphql/pagination';

@InputType()
export class SearchFlowsFilters {
@Field({ nullable: true })
id: number;

@Field({ nullable: true })
activeStatus: boolean;

@Field({ nullable: true })
status: 'commitment' | 'paid' | 'pledged';

@Field({ nullable: true })
type: 'carryover' | 'parked' | 'pass_through' | 'standard';

@Field({ nullable: true })
amountUSD: number;

@Field({ nullable: true })
reporterReferenceCode: number;

@Field({ nullable: true })
sourceSystemId: number;

@Field({ nullable: true })
legacyId: number;
}

@InputType()
export class FlowObjectFilters {
@Field({ nullable: false })
objectID: number;

@Field({ nullable: false })
direction: 'source' | 'destination';

@Field({ nullable: false })
objectType:
| 'location'
| 'organization'
| 'plan'
| 'usageYear'
| 'category'
| 'project'
| 'globalCluster'
| 'emergency';
}

@InputType()
export class FlowCategory{

@Field({ nullable: false })
id: number;

@Field({ nullable: false })
group: string;

}

@ArgsType()
export class SearchFlowsArgs extends PaginationArgs<FlowSortField> {
@Field(() => SearchFlowsFilters, { nullable: true })
filters: SearchFlowsFilters;
flowFilters: SearchFlowsFilters;

@Field(() => [FlowObjectFilters], { nullable: true })
flowObjectFilters: FlowObjectFilters[];

@Field(() => [FlowCategory], { nullable: true })
categoryFilters: FlowCategory[];

@Field({ nullable: true })
includeChildrenOfParkedFlows: boolean;
}
23 changes: 4 additions & 19 deletions src/domain-services/flows/graphql/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { Service } from 'typedi';
import { Arg, Args, Ctx, Query, Resolver } from 'type-graphql';
import { FlowSearchService } from '../flow-search-service';
import Context from '../../Context';
import { SearchFlowsFilters } from './args';
import { PaginationArgs } from '../../../utils/graphql/pagination';
import { SearchFlowsArgs } from './args';

@Service()
@Resolver(FlowPaged)
Expand All @@ -14,23 +13,9 @@ export default class FlowResolver {
@Query(() => FlowSearchResult)
async searchFlows(
@Ctx() context: Context,
@Args(() => PaginationArgs, { validate: false })
pagination: PaginationArgs<FlowSortField>,
@Arg('activeStatus', { nullable: true }) activeStatus: boolean
@Args(() => SearchFlowsArgs, { validate: false })
args: SearchFlowsArgs
): Promise<FlowSearchResult> {
const { limit, sortOrder, sortField, afterCursor, beforeCursor } =
pagination;
const filters: SearchFlowsFilters = {
activeStatus,
};
return await this.flowSearchService.search(
context.models,
limit,
sortOrder,
sortField,
afterCursor,
beforeCursor,
filters
);
return await this.flowSearchService.search(context.models, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Database } from '@unocha/hpc-api-core/src/db';
import { Ctx } from 'type-graphql';
import { Service } from 'typedi';
import Context from '../../Context';

@Service()
export class OnlyFlowFiltersStrategy {

private models: Database;

constructor(@Ctx() context: Context) {
this.models = context.models;
}

search(flowConditions: Map<string, any>, orderBy: any, limit: number) {

}
}
3 changes: 3 additions & 0 deletions src/domain-services/organizations/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ export class Organization extends BaseType {

@Field({ nullable: true })
name: string;

@Field({ nullable: true })
abbreviation: string;
}
1 change: 1 addition & 0 deletions src/domain-services/organizations/organization-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export class OrganizationService {
name: organization.name,
createdAt: organization.createdAt.toISOString(),
updatedAt: organization.updatedAt.toISOString(),
abbreviation: organization.abbreviation,
};
}
}

0 comments on commit 54ac974

Please sign in to comment.