Skip to content

Commit

Permalink
Add shortcuts for category filters
Browse files Browse the repository at this point in the history
Temp ref to hpc-api-core
  • Loading branch information
manelcecs committed Jan 3, 2024
1 parent 332accd commit f9f037b
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 161 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"lint": "yarn lint-prettier && yarn lint-eslint"
},
"dependencies": {
"@unocha/hpc-api-core": "github:UN-OCHA/hpc-api-core#3a3030ee83ad77e5fd7c40238d5ecabe1e6c7da9",
"@unocha/hpc-api-core": "github:UN-OCHA/hpc-api-core#e298382f38848370c6daa0ac86b2016eddbef356",
"apollo-server-hapi": "^3.12.0",
"bunyan": "^1.8.15",
"class-validator": "^0.14.0",
Expand Down
125 changes: 117 additions & 8 deletions src/domain-services/flows/flow-search-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,43 @@ export class FlowSearchService {
flowObjectFilters,
flowCategoryFilters,
pending: isPendingFlows,
commitment: isCommitmentFlows,
paid: isPaidFlows,
pledged: isPledgedFlows,
carryover: isCarryoverFlows,
parked: isParkedFlows,
pass_through: isPassThroughFlows,
standard: isStandardFlows,
} = filters;

// Validate the shortcut filters
// There must be only one shortcut filter
// if only one is defined
// return an object like
// { {where:{
// group: 'inactiveReason',
// name: 'Pending review',
// }, operation: 'IN'} }
// if more than one is defined
// throw an error
const shortcutFilter = this.validateShortcutFilters(
isPendingFlows,
isCommitmentFlows,
isPaidFlows,
isPledgedFlows,
isCarryoverFlows,
isParkedFlows,
isPassThroughFlows,
isStandardFlows
);

// Once we've gathered all the filters, we need to determine the strategy
// to use in order to obtain the flowIDs
const strategy: FlowSearchStrategy = this.determineStrategy(
flowFilters,
flowObjectFilters,
flowCategoryFilters,
isPendingFlows,
shortcutFilter,
orderBy
);

Expand All @@ -109,7 +137,8 @@ export class FlowSearchService {
flowFilters,
flowObjectFilters,
flowCategoryFilters,
searchPendingFlows: isPendingFlows,
// shortcuts for categories
shortcutFilter,
});

// Remove the extra item used to check hasNextPage
Expand Down Expand Up @@ -280,11 +309,63 @@ export class FlowSearchService {
};
}

/**
* This method validates that only one shortcut filter is defined
* and returns the shortcut filter defined with the operation
* IN if is true or NOT IN if is false
*
* @param isPendingFlows
* @param isCommitmentFlows
* @param isPaidFlows
* @param isPledgedFlows
* @param isCarryoverFlows
* @param isParkedFlows
* @param isPassThroughFlows
* @param isStandardFlows
* @returns { category: String, operation: Op.IN | Op.NOT_IN}
*/
validateShortcutFilters(
isPendingFlows: boolean,
isCommitmentFlows: boolean,
isPaidFlows: boolean,
isPledgedFlows: boolean,
isCarryoverFlows: boolean,
isParkedFlows: boolean,
isPassThroughFlows: boolean,
isStandardFlows: boolean
) {
const filters = [
{ flag: isPendingFlows, category: 'Pending', group: 'inactiveReason' },
{ flag: isCommitmentFlows, category: 'Commitment', group: 'flowStatus' },
{ flag: isPaidFlows, category: 'Paid', group: 'flowStatus' },
{ flag: isPledgedFlows, category: 'Pledged', group: 'flowStatus' },
{ flag: isCarryoverFlows, category: 'Carryover', group: 'flowType' },
{ flag: isParkedFlows, category: 'Parked', group: 'flowType' },
{ flag: isPassThroughFlows, category: 'Pass Through', group: 'flowType' },
{ flag: isStandardFlows, category: 'Standard', group: 'flowType' },
];

const shortcutFilters = filters
.filter((filter) => filter.flag)
.map((filter) => ({
where: { group: filter.group, name: filter.category },
operation: filter.flag ? Op.IN : Op.NOT_IN,
}));

if (shortcutFilters.length > 1) {
throw new Error(
'Only one shortcut filter can be defined at the same time'
);
}

return shortcutFilters.length === 1 ? shortcutFilters[0] : null;
}

determineStrategy(
flowFilters: SearchFlowsFilters,
flowObjectFilters: FlowObjectFilters[],
flowCategoryFilters: FlowCategory[],
isPendingFlows: boolean,
shortcutFilter: any | null,
orderBy?: FlowOrderBy
) {
// If there are no filters (flowFilters, flowObjectFilters, flowCategoryFilters or pending)
Expand All @@ -297,18 +378,20 @@ export class FlowSearchService {
const isFlowFiltersDefined = flowFilters !== undefined;
const isFlowObjectFiltersDefined = flowObjectFilters !== undefined;
const isFlowCategoryFiltersDefined = flowCategoryFilters !== undefined;
const isFilterByPendingFlowsDefined = isPendingFlows !== undefined;
// Shortcuts fot categories
const isFilterByShortcutsDefined = shortcutFilter !== null;

const isNoFilterDefined =
!isFlowFiltersDefined &&
!isFlowObjectFiltersDefined &&
!isFlowCategoryFiltersDefined &&
!isFilterByPendingFlowsDefined;
!isFilterByShortcutsDefined;

const isFlowFiltersOnly =
isFlowFiltersDefined &&
!isFlowObjectFiltersDefined &&
!isFlowCategoryFiltersDefined &&
!isFilterByPendingFlowsDefined;
!isFilterByShortcutsDefined;

if (isOrderByEntityFlow && (isNoFilterDefined || isFlowFiltersOnly)) {
// Use onlyFlowFiltersStrategy
Expand Down Expand Up @@ -683,6 +766,13 @@ export class FlowSearchService {
flowObjectFilters,
flowCategoryFilters,
pending: isPendingFlows,
commitment: isCommitmentFlows,
paid: isPaidFlows,
pledged: isPledgedFlows,
carryover: isCarryoverFlows,
parked: isParkedFlows,
pass_through: isPassThroughFlows,
standard: isStandardFlows,
} = args;

if (!flowFilters) {
Expand All @@ -692,13 +782,31 @@ export class FlowSearchService {
flowFilters.activeStatus = true;
}

// Validate the shortcut filters
// There must be only one shortcut filter
// if only one is defined
// return an object like
// { category: 'Parked', operation: 'IN' }
// if more than one is defined
// throw an error
const shortcutFilter = this.validateShortcutFilters(
isPendingFlows,
isCommitmentFlows,
isPaidFlows,
isPledgedFlows,
isCarryoverFlows,
isParkedFlows,
isPassThroughFlows,
isStandardFlows
);

// Once we've gathered all the filters, we need to determine the strategy
// to use in order to obtain the flowIDs
const strategy: FlowSearchStrategy = this.determineStrategy(
flowFilters,
flowObjectFilters,
flowCategoryFilters,
isPendingFlows
shortcutFilter
);

const { flows, count } = await strategy.search({
Expand All @@ -707,7 +815,8 @@ export class FlowSearchService {
flowFilters,
flowObjectFilters,
flowCategoryFilters,
searchPendingFlows: isPendingFlows,
// shortcuts for categories
shortcutFilter,
});

const flowsAmountUSD: Array<string | number> = flows.map(
Expand Down
76 changes: 44 additions & 32 deletions src/domain-services/flows/graphql/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,6 @@ import { ArgsType, Field, InputType } from 'type-graphql';
import { PaginationArgs } from '../../../utils/graphql/pagination';
import { type FlowSortField } from './types';

@InputType()
export class FlowStatusFilters {
@Field(() => Boolean, { nullable: true })
pending: boolean | null;

@Field(() => Boolean, { nullable: true })
commitment: boolean | null;

@Field(() => Boolean, { nullable: true })
paid: boolean | null;

@Field(() => Boolean, { nullable: true })
pledged: boolean | null;
}

@InputType()
export class FlowTypeFilters {
@Field(() => Boolean, { nullable: true })
carryover: boolean | null;

@Field(() => Boolean, { nullable: true })
parked: boolean | null;

@Field(() => Boolean, { nullable: true })
pass_through: boolean | null;

@Field(() => Boolean, { nullable: true })
standard: boolean | null;
}

@InputType()
export class SearchFlowsFilters {
@Field(() => [Number], { nullable: true })
Expand Down Expand Up @@ -116,8 +86,29 @@ export class SearchFlowsArgs extends PaginationArgs<FlowSortField> {
@Field(() => [FlowCategory], { nullable: true })
flowCategoryFilters: FlowCategory[];

@Field({ nullable: true })
@Field(() => Boolean, { nullable: true })
pending: boolean;

@Field(() => Boolean, { nullable: true })
commitment: boolean;

@Field(() => Boolean, { nullable: true })
paid: boolean;

@Field(() => Boolean, { nullable: true })
pledged: boolean;

@Field(() => Boolean, { nullable: true })
carryover: boolean;

@Field(() => Boolean, { nullable: true })
parked: boolean;

@Field(() => Boolean, { nullable: true })
pass_through: boolean;

@Field(() => Boolean, { nullable: true })
standard: boolean;
}

@ArgsType()
Expand All @@ -134,6 +125,27 @@ export class SearchFlowsArgsNonPaginated {
@Field(() => [FlowCategory], { nullable: true })
flowCategoryFilters: FlowCategory[];

@Field({ nullable: true })
@Field(() => Boolean, { nullable: true })
pending: boolean;

@Field(() => Boolean, { nullable: true })
commitment: boolean;

@Field(() => Boolean, { nullable: true })
paid: boolean;

@Field(() => Boolean, { nullable: true })
pledged: boolean;

@Field(() => Boolean, { nullable: true })
carryover: boolean;

@Field(() => Boolean, { nullable: true })
parked: boolean;

@Field(() => Boolean, { nullable: true })
pass_through: boolean;

@Field(() => Boolean, { nullable: true })
standard: boolean;
}
2 changes: 1 addition & 1 deletion src/domain-services/flows/strategy/flow-search-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface FlowSearchArgs {
limit?: number;
orderBy?: any;
cursorCondition?: any;
searchPendingFlows?: boolean;
shortcutFilter: any;
}

export interface FlowSearchStrategy {
Expand Down
14 changes: 8 additions & 6 deletions src/domain-services/flows/strategy/flowID-search-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ export interface FlowIdSearchStrategyResponse {
flowIDs: FlowId[];
}

export interface FlowIdSearchStrategyArgs {
models: Database;
flowObjectsConditions?: Map<string, Map<string, number[]>>;
flowCategoryConditions?: FlowCategory[];
shortcutFilter?: any | null;
}

export interface FlowIDSearchStrategy {
search(
models: Database,
flowObjectsConditions: Map<string, Map<string, number[]>>,
flowCategoryConditions: FlowCategory[],
filterByPendingFlows?: boolean
): Promise<FlowIdSearchStrategyResponse>;
search(args: FlowIdSearchStrategyArgs): Promise<FlowIdSearchStrategyResponse>;

generateWhereClause(
flowIds: FlowId[],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Service } from 'typedi';
import { CategoryService } from '../../../categories/category-service';
import { type FlowCategory } from '../../graphql/args';

Check failure on line 8 in src/domain-services/flows/strategy/impl/get-flowIds-flow-category-conditions-strategy-impl.ts

View workflow job for this annotation

GitHub Actions / Code Checks

'FlowCategory' is defined but never used. Allowed unused vars must match /^_/u
import {

Check failure on line 9 in src/domain-services/flows/strategy/impl/get-flowIds-flow-category-conditions-strategy-impl.ts

View workflow job for this annotation

GitHub Actions / Code Checks

All imports in the declaration are only used as types. Use `import type`
FlowIdSearchStrategyArgs,
type FlowIDSearchStrategy,
type FlowIdSearchStrategyResponse,
} from '../flowID-search-strategy';
Expand All @@ -19,14 +20,13 @@ export class GetFlowIdsFromCategoryConditionsStrategyImpl
constructor(private readonly categoryService: CategoryService) {}

async search(
models: Database,
_flowObjectsConditions: Map<string, Map<string, number[]>>,
flowCategoryConditions: FlowCategory[],
filterByPendingFlows: boolean | undefined
args: FlowIdSearchStrategyArgs
): Promise<FlowIdSearchStrategyResponse> {
const { models, flowCategoryConditions, shortcutFilter } = args;

const whereClause = mapFlowCategoryConditionsToWhereClause(
filterByPendingFlows,
flowCategoryConditions
shortcutFilter,
flowCategoryConditions!
);

const categories = await this.categoryService.findCategories(
Expand Down
Loading

0 comments on commit f9f037b

Please sign in to comment.