From f5b55d4e5d09eb8441856e4acedd2f19b29aac68 Mon Sep 17 00:00:00 2001 From: manelcecs Date: Tue, 14 Nov 2023 13:39:17 +0100 Subject: [PATCH] Allow filter flow by multiples id Fix lint --- .../categories/category-service.ts | 18 ++-- .../flows/flow-search-service.ts | 96 ++++++++++--------- src/domain-services/flows/flow-service.ts | 2 +- src/domain-services/flows/graphql/args.ts | 6 +- src/domain-services/flows/graphql/resolver.ts | 6 +- src/domain-services/flows/graphql/types.ts | 4 +- src/domain-services/location/graphql/types.ts | 2 +- .../location/location-service.ts | 14 +-- .../organizations/organization-service.ts | 10 +- src/domain-services/plans/graphql/types.ts | 2 +- src/domain-services/plans/plan-service.ts | 10 +- .../usage-years/usage-year-service.ts | 12 +-- tests/unit/flow-search-service.spec.ts | 18 ++-- 13 files changed, 104 insertions(+), 96 deletions(-) diff --git a/src/domain-services/categories/category-service.ts b/src/domain-services/categories/category-service.ts index 61099d94..20560423 100644 --- a/src/domain-services/categories/category-service.ts +++ b/src/domain-services/categories/category-service.ts @@ -1,14 +1,14 @@ -import { Database } from '@unocha/hpc-api-core/src/db'; +import { type Database } from '@unocha/hpc-api-core/src/db'; +import { Op } from '@unocha/hpc-api-core/src/db/util/conditions'; +import { type InstanceDataOfModel } from '@unocha/hpc-api-core/src/db/util/raw-model'; import { createBrandedValue } from '@unocha/hpc-api-core/src/util/types'; import { Service } from 'typedi'; -import { Category } from './graphql/types'; -import { InstanceDataOfModel } from '@unocha/hpc-api-core/src/db/util/raw-model'; -import { Op } from '@unocha/hpc-api-core/src/db/util/conditions'; +import { type Category } from './graphql/types'; @Service() export class CategoryService { async getCategoriesForFlows( - flowLinks: Map[]>, + flowLinks: Map>>, models: Database ): Promise> { const flowLinksBrandedIds = []; @@ -23,7 +23,7 @@ export class CategoryService { return categoriesMap; } - const categoriesRef: InstanceDataOfModel[] = + const categoriesRef: Array> = await models.categoryRef.find({ where: { objectID: { @@ -32,7 +32,7 @@ export class CategoryService { }, }); - const categories: InstanceDataOfModel[] = + const categories: Array> = await models.category.find({ where: { id: { @@ -42,7 +42,7 @@ export class CategoryService { }); // Populate the map with categories for each flow - categoriesRef.forEach((catRef) => { + for (const catRef of categoriesRef) { const flowId = catRef.objectID.valueOf(); if (!categoriesMap.has(flowId)) { @@ -58,7 +58,7 @@ export class CategoryService { } categoriesPerFlow.push(this.mapCategoryToFlowCategory(category, catRef)); - }); + } return categoriesMap; } diff --git a/src/domain-services/flows/flow-search-service.ts b/src/domain-services/flows/flow-search-service.ts index bbf48472..6a19a4f1 100644 --- a/src/domain-services/flows/flow-search-service.ts +++ b/src/domain-services/flows/flow-search-service.ts @@ -1,37 +1,37 @@ -import { Database } from '@unocha/hpc-api-core/src/db/type'; +import { type FlowId } from '@unocha/hpc-api-core/src/db/models/flow'; +import { type Database } from '@unocha/hpc-api-core/src/db/type'; 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 { - FlowObjectFilters, - SearchFlowsArgs, - SearchFlowsFilters, -} from './graphql/args'; -import { - FlowPaged, - FlowParkedParentSource, - FlowSearchResult, -} from './graphql/types'; -import { FlowSearchStrategy } from './strategy/flow-search-strategy'; -import { OnlyFlowFiltersStrategy } from './strategy/impl/only-flow-conditions-strategy'; -import { FlowObjectFiltersStrategy } from './strategy/impl/flow-object-conditions-strategy'; -import { FlowId } from '@unocha/hpc-api-core/src/db/models/flow'; import { CategoryService } from '../categories/category-service'; +import { type Category } from '../categories/graphql/types'; import { ExternalReferenceService } from '../external-reference/external-reference-service'; +import { FlowLinkService } from '../flow-link/flow-link-service'; +import { FlowObjectService } from '../flow-object/flow-object-service'; +import { type FlowObject } from '../flow-object/model'; +import { type BaseLocation } from '../location/graphql/types'; import { LocationService } from '../location/location-service'; +import { type Organization } from '../organizations/graphql/types'; import { OrganizationService } from '../organizations/organization-service'; +import { type BasePlan } from '../plans/graphql/types'; import { PlanService } from '../plans/plan-service'; 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 { FlowLinkService } from '../flow-link/flow-link-service'; -import { FlowObject } from '../flow-object/model'; -import { FlowObjectService } from '../flow-object/flow-object-service'; -import { FlowEntity } from './model'; -import { Category } from '../categories/graphql/types'; -import { Organization } from '../organizations/graphql/types'; -import { BaseLocation } from '../location/graphql/types'; -import { BasePlan } from '../plans/graphql/types'; -import { UsageYear } from '../usage-years/grpahql/types'; +import { + type FlowObjectFilters, + type SearchFlowsArgs, + type SearchFlowsFilters, +} from './graphql/args'; +import { + type FlowPaged, + type FlowParkedParentSource, + type FlowSearchResult, +} from './graphql/types'; +import { type FlowEntity } from './model'; +import { type FlowSearchStrategy } from './strategy/flow-search-strategy'; +import { FlowObjectFiltersStrategy } from './strategy/impl/flow-object-conditions-strategy'; +import { OnlyFlowFiltersStrategy } from './strategy/impl/only-flow-conditions-strategy'; @Service() export class FlowSearchService { @@ -144,14 +144,14 @@ export class FlowSearchService { const items = await Promise.all( flows.map(async (flow) => { - 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 plans = plansMap.get(flow.id) || []; - const usageYears = usageYearsMap.get(flow.id) || []; - const externalReferences = externalReferencesMap.get(flow.id) || []; - const reportDetails = reportDetailsMap.get(flow.id) || []; + 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 plans = plansMap.get(flow.id) ?? []; + const usageYears = usageYearsMap.get(flow.id) ?? []; + const externalReferences = externalReferencesMap.get(flow.id) ?? []; + const reportDetails = reportDetailsMap.get(flow.id) ?? []; let parkedParentSource: FlowParkedParentSource[] = []; if (flow.activeStatus && flowLink.length > 0) { @@ -197,7 +197,7 @@ export class FlowSearchService { hasNextPage: limit <= flows.length, hasPreviousPage: afterCursor !== undefined, startCursor: flows.length ? flows[0].id.valueOf() : 0, - endCursor: flows.length ? flows[flows.length - 1].id.valueOf() : 0, + endCursor: flows.length ? flows.at(-1)?.id.valueOf() ?? 0 : 0, pageSize: flows.length, sortField: orderBy.column, sortOrder: orderBy.order, @@ -209,11 +209,15 @@ export class FlowSearchService { let flowConditions = {}; if (flowFilters) { - Object.entries(flowFilters).forEach(([key, value]) => { + for (const [key, value] of Object.entries(flowFilters)) { if (value !== undefined) { - flowConditions = { ...flowConditions, [key]: value }; + if (Array.isArray(value) && value.length !== 0) { + flowConditions = { ...flowConditions, [key]: { [Op.IN]: value } }; + } else { + flowConditions = { ...flowConditions, [key]: value }; + } } - }); + } } return flowConditions; @@ -222,13 +226,16 @@ export class FlowSearchService { prepareFlowObjectConditions( flowObjectFilters: FlowObjectFilters[] ): Map> { - const flowObjectsConditions: Map> = new Map(); + const flowObjectsConditions: Map> = new Map< + string, + Map + >(); - flowObjectFilters.forEach((flowObjectFilter) => { + for (const flowObjectFilter of flowObjectFilters) { const { objectType, direction, objectID } = flowObjectFilter; if (!flowObjectsConditions.has(objectType)) { - flowObjectsConditions.set(objectType, new Map()); + flowObjectsConditions.set(objectType, new Map()); } const refDirectionMap = flowObjectsConditions.get(objectType); @@ -245,7 +252,7 @@ export class FlowSearchService { } objectIDsArray!.push(objectID); - }); + } return flowObjectsConditions; } @@ -257,8 +264,8 @@ export class FlowSearchService { let conditions = {}; if ( (!flowFilters && - (!flowObjectFilters || flowObjectFilters.length === 0)) || - (flowFilters && (!flowObjectFilters || flowObjectFilters.length === 0)) + (!flowObjectFilters ?? flowObjectFilters.length === 0)) ?? + (flowFilters && (!flowObjectFilters ?? flowObjectFilters.length === 0)) ) { const flowConditions = this.prepareFlowConditions(flowFilters); conditions = { ...conditions, ...flowConditions }; @@ -302,6 +309,7 @@ export class FlowSearchService { conditionsMap.set('flow', flowConditions); return conditionsMap; } + private mapFlowObjects( flowObjects: FlowObject[], organizationsFO: any[], @@ -309,7 +317,7 @@ export class FlowSearchService { plansFO: any[], usageYearsFO: any[] ) { - flowObjects.forEach((flowObject) => { + for (const flowObject of flowObjects) { if (flowObject.objectType === 'organization') { organizationsFO.push(flowObject); } else if (flowObject.objectType === 'location') { @@ -319,7 +327,7 @@ export class FlowSearchService { } else if (flowObject.objectType === 'usageYear') { usageYearsFO.push(flowObject); } - }); + } } private async getParketParents( diff --git a/src/domain-services/flows/flow-service.ts b/src/domain-services/flows/flow-service.ts index 536ea3d8..4920eecd 100644 --- a/src/domain-services/flows/flow-service.ts +++ b/src/domain-services/flows/flow-service.ts @@ -1,5 +1,5 @@ +import { type Database } from '@unocha/hpc-api-core/src/db/type'; import { Service } from 'typedi'; -import { Database } from '@unocha/hpc-api-core/src/db/type'; @Service() export class FlowService { diff --git a/src/domain-services/flows/graphql/args.ts b/src/domain-services/flows/graphql/args.ts index 155aa67f..7996e7b0 100644 --- a/src/domain-services/flows/graphql/args.ts +++ b/src/domain-services/flows/graphql/args.ts @@ -1,11 +1,11 @@ import { ArgsType, Field, InputType } from 'type-graphql'; -import { FlowSortField } from './types'; import { PaginationArgs } from '../../../utils/graphql/pagination'; +import { type FlowSortField } from './types'; @InputType() export class SearchFlowsFilters { - @Field({ nullable: true }) - id: number; + @Field(() => [Number], { nullable: true }) + id: number[]; @Field({ nullable: true }) activeStatus: boolean; diff --git a/src/domain-services/flows/graphql/resolver.ts b/src/domain-services/flows/graphql/resolver.ts index 80a96a64..2852b580 100644 --- a/src/domain-services/flows/graphql/resolver.ts +++ b/src/domain-services/flows/graphql/resolver.ts @@ -1,9 +1,9 @@ -import { FlowPaged, FlowSearchResult, FlowSortField } from './types'; +import { Args, Ctx, Query, Resolver } from 'type-graphql'; import { Service } from 'typedi'; -import { Arg, Args, Ctx, Query, Resolver } from 'type-graphql'; -import { FlowSearchService } from '../flow-search-service'; import Context from '../../Context'; +import { FlowSearchService } from '../flow-search-service'; import { SearchFlowsArgs } from './args'; +import { FlowPaged, FlowSearchResult } from './types'; @Service() @Resolver(FlowPaged) diff --git a/src/domain-services/flows/graphql/types.ts b/src/domain-services/flows/graphql/types.ts index 50fb8877..1307e47f 100644 --- a/src/domain-services/flows/graphql/types.ts +++ b/src/domain-services/flows/graphql/types.ts @@ -1,12 +1,12 @@ import { Field, ObjectType } from 'type-graphql'; -import { IItemPaged, PageInfo } from '../../../utils/graphql/pagination'; +import { BaseType } from '../../../utils/graphql/base-types'; +import { PageInfo, type IItemPaged } from '../../../utils/graphql/pagination'; import { Category } from '../../categories/graphql/types'; import { BaseLocation } from '../../location/graphql/types'; import { Organization } from '../../organizations/graphql/types'; import { BasePlan } from '../../plans/graphql/types'; import { ReportDetail } from '../../report-details/graphql/types'; import { UsageYear } from '../../usage-years/grpahql/types'; -import { BaseType } from '../../../utils/graphql/base-types'; @ObjectType() export class FlowExternalReference { diff --git a/src/domain-services/location/graphql/types.ts b/src/domain-services/location/graphql/types.ts index 76f10cf3..27f9ecec 100644 --- a/src/domain-services/location/graphql/types.ts +++ b/src/domain-services/location/graphql/types.ts @@ -1,7 +1,7 @@ -import { BaseType } from '../../../utils/graphql/base-types'; import { Brand } from '@unocha/hpc-api-core/src/util/types'; import { MaxLength } from 'class-validator'; import { Field, ID, Int, ObjectType, registerEnumType } from 'type-graphql'; +import { BaseType } from '../../../utils/graphql/base-types'; export enum LocationStatus { active = 'active', diff --git a/src/domain-services/location/location-service.ts b/src/domain-services/location/location-service.ts index 5221529c..eb83dbe0 100644 --- a/src/domain-services/location/location-service.ts +++ b/src/domain-services/location/location-service.ts @@ -1,10 +1,10 @@ +import { type LocationId } from '@unocha/hpc-api-core/src/db/models/location'; import { type Database } from '@unocha/hpc-api-core/src/db/type'; +import { Op } from '@unocha/hpc-api-core/src/db/util/conditions'; import { type InstanceDataOfModel } from '@unocha/hpc-api-core/src/db/util/raw-model'; import { createBrandedValue } from '@unocha/hpc-api-core/src/util/types'; import { Service } from 'typedi'; -import { BaseLocation } from './graphql/types'; -import { Op } from '@unocha/hpc-api-core/src/db/util/conditions'; -import { LocationId } from '@unocha/hpc-api-core/src/db/models/location'; +import { type BaseLocation } from './graphql/types'; @Service() export class LocationService { @@ -31,14 +31,14 @@ export class LocationService { } async getLocationsForFlows( - locationsFO: InstanceDataOfModel[], + locationsFO: Array>, models: Database ): Promise>> { const locationObjectsIDs: LocationId[] = locationsFO.map((locFO) => createBrandedValue(locFO.objectID) ); - const locations: InstanceDataOfModel[] = + const locations: Array> = await models.location.find({ where: { id: { @@ -49,7 +49,7 @@ export class LocationService { const locationsMap = new Map>(); - locationsFO.forEach((locFO) => { + for (const locFO of locationsFO) { const flowId = locFO.flowID; if (!locationsMap.has(flowId)) { locationsMap.set(flowId, new Set()); @@ -61,7 +61,7 @@ export class LocationService { } const locationMapped = this.mapLocationsToFlowLocations(location, locFO); locationsMap.get(flowId)!.add(locationMapped); - }); + } return locationsMap; } diff --git a/src/domain-services/organizations/organization-service.ts b/src/domain-services/organizations/organization-service.ts index a35e6637..411531c4 100644 --- a/src/domain-services/organizations/organization-service.ts +++ b/src/domain-services/organizations/organization-service.ts @@ -1,7 +1,7 @@ -import { Database } from '@unocha/hpc-api-core/src/db'; -import { Service } from 'typedi'; +import { type Database } from '@unocha/hpc-api-core/src/db'; import { Op } from '@unocha/hpc-api-core/src/db/util/conditions'; -import { Organization } from './graphql/types'; +import { Service } from 'typedi'; +import { type Organization } from './graphql/types'; @Service() export class OrganizationService { @@ -16,7 +16,7 @@ export class OrganizationService { const organizationsMap = new Map(); - organizationsFO.forEach((orgFO) => { + for (const orgFO of organizationsFO) { const flowId = orgFO.flowID; if (!organizationsMap.has(flowId)) { @@ -39,7 +39,7 @@ export class OrganizationService { ); organizationsMap.get(flowId)!.push(organizationMapped); - }); + } return organizationsMap; } diff --git a/src/domain-services/plans/graphql/types.ts b/src/domain-services/plans/graphql/types.ts index 3316b717..9a6fe4fc 100644 --- a/src/domain-services/plans/graphql/types.ts +++ b/src/domain-services/plans/graphql/types.ts @@ -1,8 +1,8 @@ import { Brand } from '@unocha/hpc-api-core/src/util/types'; import { MaxLength } from 'class-validator'; import { Field, ID, Int, ObjectType } from 'type-graphql'; -import PlanTag from '../../plan-tag/graphql/types'; import { BaseType } from '../../../utils/graphql/base-types'; +import PlanTag from '../../plan-tag/graphql/types'; @ObjectType() export class PlanCaseload { diff --git a/src/domain-services/plans/plan-service.ts b/src/domain-services/plans/plan-service.ts index 63a97d2e..f4d98301 100644 --- a/src/domain-services/plans/plan-service.ts +++ b/src/domain-services/plans/plan-service.ts @@ -1,11 +1,11 @@ import { type PlanId } from '@unocha/hpc-api-core/src/db/models/plan'; import { type Database } from '@unocha/hpc-api-core/src/db/type'; import { Op } from '@unocha/hpc-api-core/src/db/util/conditions'; +import { type InstanceDataOfModel } from '@unocha/hpc-api-core/src/db/util/raw-model'; import { NotFoundError } from '@unocha/hpc-api-core/src/util/error'; import { createBrandedValue } from '@unocha/hpc-api-core/src/util/types'; import { Service } from 'typedi'; -import { BasePlan } from './graphql/types'; -import { InstanceDataOfModel } from '@unocha/hpc-api-core/src/db/util/raw-model'; +import { type BasePlan } from './graphql/types'; @Service() export class PlanService { @@ -49,13 +49,13 @@ export class PlanService { } async getPlansForFlows( - plansFO: InstanceDataOfModel[], + plansFO: Array>, models: Database ): Promise> { const planObjectsIDs: PlanId[] = plansFO.map((planFO) => createBrandedValue(planFO.objectID) ); - const plans: InstanceDataOfModel[] = + const plans: Array> = await models.plan.find({ where: { id: { @@ -83,7 +83,7 @@ export class PlanService { const planMapped = this.mapPlansToFlowPlans( plan, planVersion[0], - planFlowOobject?.refDirection || null + planFlowOobject?.refDirection ?? null ); if (flowId) { diff --git a/src/domain-services/usage-years/usage-year-service.ts b/src/domain-services/usage-years/usage-year-service.ts index bb74c9a6..dfb1e6f5 100644 --- a/src/domain-services/usage-years/usage-year-service.ts +++ b/src/domain-services/usage-years/usage-year-service.ts @@ -1,8 +1,8 @@ -import { Database } from '@unocha/hpc-api-core/src/db'; +import { type Database } from '@unocha/hpc-api-core/src/db'; import { Op } from '@unocha/hpc-api-core/src/db/util/conditions'; +import { type InstanceDataOfModel } from '@unocha/hpc-api-core/src/db/util/raw-model'; import { Service } from 'typedi'; -import { UsageYear } from './grpahql/types'; -import { InstanceDataOfModel } from '@unocha/hpc-api-core/src/db/util/raw-model'; +import { type UsageYear } from './grpahql/types'; @Service() export class UsageYearService { @@ -10,7 +10,7 @@ export class UsageYearService { usageYearsFO: any[], models: Database ): Promise> { - const usageYears: InstanceDataOfModel[] = + const usageYears: Array> = await models.usageYear.find({ where: { id: { @@ -21,7 +21,7 @@ export class UsageYearService { const usageYearsMap = new Map(); - usageYearsFO.forEach((usageYearFO) => { + for (const usageYearFO of usageYearsFO) { const flowId = usageYearFO.flowID; if (!usageYearsMap.has(flowId)) { usageYearsMap.set(flowId, []); @@ -40,7 +40,7 @@ export class UsageYearService { usageYearFO.refDirection ); usageYearsMap.get(flowId)!.push(usageYearMapped); - }); + } return usageYearsMap; } diff --git a/tests/unit/flow-search-service.spec.ts b/tests/unit/flow-search-service.spec.ts index 7b2c655b..7797d044 100644 --- a/tests/unit/flow-search-service.spec.ts +++ b/tests/unit/flow-search-service.spec.ts @@ -1,8 +1,8 @@ import Container from 'typedi'; import { FlowSearchService } from '../../src/domain-services/flows/flow-search-service'; import { - FlowObjectFilters, SearchFlowsFilters, + type FlowObjectFilters, } from '../../src/domain-services/flows/graphql/args'; describe('FlowSearchService', () => { @@ -15,7 +15,7 @@ describe('FlowSearchService', () => { describe('PrepareFlowConditions', () => { it('should prepare flow conditions with valid filters', () => { const flowFilters = new SearchFlowsFilters(); - flowFilters.id = 1; + flowFilters.id = [1]; flowFilters.activeStatus = true; flowFilters.status = 'commitment'; flowFilters.type = 'carryover'; @@ -40,7 +40,7 @@ describe('FlowSearchService', () => { it('should prepare flow conditions with some filters set to undefined', () => { const flowFilters = new SearchFlowsFilters(); - flowFilters.id = 1; + flowFilters.id = [1]; flowFilters.activeStatus = true; const result = flowSearchService.prepareFlowConditions(flowFilters); @@ -61,14 +61,14 @@ describe('FlowSearchService', () => { it('should prepare flow conditions with some filters having falsy values', () => { const flowFilters = new SearchFlowsFilters(); - flowFilters.id = 0; + flowFilters.id = []; flowFilters.activeStatus = false; flowFilters.amountUSD = 0; const result = flowSearchService.prepareFlowConditions(flowFilters); expect(result).toEqual({ - id: 0, + id: [], activeStatus: false, amountUSD: 0, }); @@ -77,7 +77,7 @@ describe('FlowSearchService', () => { describe('prepareFlowObjectConditions', () => { it('should prepare flow object conditions correctly', () => { const flowObjectFilters: FlowObjectFilters[] = [ - { objectType: 'organization', direction: 'source', objectID: 12469 }, + { objectType: 'organization', direction: 'source', objectID: 12_469 }, { objectType: 'organization', direction: 'destination', @@ -92,7 +92,7 @@ describe('FlowSearchService', () => { [ 'organization', new Map([ - ['source', [12469]], + ['source', [12_469]], ['destination', [5197]], ]), ], @@ -103,8 +103,8 @@ describe('FlowSearchService', () => { it('should throw an error for duplicate flow object filter', () => { const flowObjectFilters: FlowObjectFilters[] = [ - { objectType: 'organization', direction: 'source', objectID: 12469 }, - { objectType: 'organization', direction: 'source', objectID: 12469 }, // Duplicate filter + { objectType: 'organization', direction: 'source', objectID: 12_469 }, + { objectType: 'organization', direction: 'source', objectID: 12_469 }, // Duplicate filter ]; expect(() =>