Skip to content

Commit

Permalink
Temp2
Browse files Browse the repository at this point in the history
  • Loading branch information
manelcecs committed Dec 11, 2023
1 parent 9bd4739 commit d909985
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 87 deletions.
142 changes: 91 additions & 51 deletions src/domain-services/flows/flow-search-service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { type FlowId } from '@unocha/hpc-api-core/src/db/models/flow';
import { type Database } from '@unocha/hpc-api-core/src/db/type';
import { Cond, Op } from '@unocha/hpc-api-core/src/db/util/conditions';
import { Op } from '@unocha/hpc-api-core/src/db/util/conditions';
import { Service } from 'typedi';
import { type SortOrder } from '../../utils/graphql/pagination';
import { CategoryService } from '../categories/category-service';
import { type Category } from '../categories/graphql/types';
import { ExternalReferenceService } from '../external-reference/external-reference-service';
Expand All @@ -25,14 +26,14 @@ import {
type SearchFlowsFilters,
} from './graphql/args';
import {
type FlowPaged,
type Flow,
type FlowParkedParentSource,
type FlowSearchResult,
type FlowSearchResultNonPaginated,
type FlowSearchTotalAmountResult,
type FlowSortField,
} from './graphql/types';
import { type FlowEntity } from './model';
import { type FlowEntity, type FlowOrderBy } from './model';
import { type FlowSearchStrategy } from './strategy/flow-search-strategy';
import { FlowObjectFiltersStrategy } from './strategy/impl/flow-object-conditions-strategy-impl';
import { OnlyFlowFiltersStrategy } from './strategy/impl/only-flow-conditions-strategy-impl';
Expand Down Expand Up @@ -60,12 +61,7 @@ export class FlowSearchService {
const { limit, nextPageCursor, prevPageCursor, sortField, sortOrder } =
filters;

const orderBy:
| { column: FlowSortField; order: 'asc' | 'desc' }
| Array<{ column: FlowSortField; order: 'asc' | 'desc' }> = {
column: sortField ?? 'updatedAt',
order: sortOrder ?? 'desc',
};
const orderBy: FlowOrderBy = this.buildOrderBy(sortField, sortOrder);

const { flowFilters, flowObjectFilters, flowCategoryFilters } = filters;

Expand All @@ -79,7 +75,8 @@ export class FlowSearchService {
const { strategy, conditions } = this.determineStrategy(
flowFilters,
flowObjectFilters,
flowCategoryFilters
flowCategoryFilters,
orderBy
);

// Fetch one more item to check for hasNextPage
Expand Down Expand Up @@ -204,25 +201,86 @@ export class FlowSearchService {
parentIDs,
externalReferences,
reportDetailsWithChannel,
parkedParentSource,
sortField
parkedParentSource
);
})
);

// Sort items
// FIXME: this sorts the page, not the whole result set
items.sort((a: Flow, b: Flow) => {
const nestedA = a[orderBy.entity as keyof Flow];
const nestedB = b[orderBy.entity as keyof Flow];

if (nestedA && nestedB) {
const propertyA = nestedA[orderBy.column as keyof typeof nestedA];
const propertyB = nestedB[orderBy.column as keyof typeof nestedB];

// Implement your custom comparison logic
// For example, compare strings or numbers
if (propertyA < propertyB) {
return orderBy.order === 'asc' ? -1 : 1;
}
if (propertyA > propertyB) {
return orderBy.order === 'asc' ? 1 : -1;
}
}

return 0;
});

const isOrderByForFlows = orderBy.entity === 'flow';
const firstItem = items[0];
const prevPageCursorEntity = isOrderByForFlows
? firstItem
: firstItem[orderBy.entity as keyof typeof firstItem];
const prevPageCursorValue = prevPageCursorEntity
? prevPageCursorEntity[
orderBy.column as keyof typeof prevPageCursorEntity
] ?? ''
: '';

const lastItem = items.at(-1);
const nextPageCursorEntity = isOrderByForFlows
? lastItem
: lastItem![orderBy.entity as keyof typeof lastItem];
const nextPageCursorValue = nextPageCursorEntity
? nextPageCursorEntity[
orderBy.column as keyof typeof nextPageCursorEntity
]?.toString() ?? ''
: '';

// TODO: implement nested cursors for page
return {
flows: items,
hasNextPage: limit <= flows.length,
hasPreviousPage: nextPageCursor !== undefined,
prevPageCursor: flows.length ? items[0].cursor : '',
nextPageCursor: flows.length ? items.at(-1)?.cursor ?? '' : '',
prevPageCursor: prevPageCursorValue,
nextPageCursor: nextPageCursorValue,
pageSize: flows.length,
sortField: sortField ?? 'updatedAt',
sortField: `${orderBy.entity}.${orderBy.column}` as FlowSortField,
sortOrder: sortOrder ?? 'desc',
total: count,
};
}

buildOrderBy(sortField?: FlowSortField, sortOrder?: SortOrder) {
const orderBy: FlowOrderBy = {
column: sortField ?? 'updatedAt',
order: sortOrder ?? ('desc' as SortOrder),
entity: 'flow',
};

// Check if sortField is a nested property
if (orderBy.column.includes('.')) {
const [nestedEntity, propertyToSort] = orderBy.column.split('.');
// Update orderBy object with nested information
orderBy.column = propertyToSort;
orderBy.entity = nestedEntity;
}

return orderBy;
}
prepareFlowConditions(flowFilters: SearchFlowsFilters): any {
let flowConditions = {};

Expand Down Expand Up @@ -278,7 +336,8 @@ export class FlowSearchService {
determineStrategy(
flowFilters: SearchFlowsFilters,
flowObjectFilters: FlowObjectFilters[],
flowCategoryFilters: FlowCategoryFilters
flowCategoryFilters: FlowCategoryFilters,
orderBy?: FlowOrderBy
): { strategy: FlowSearchStrategy; conditions: any } {
const isFlowFilterDefined = flowFilters !== undefined;
const isFlowObjectFilterDefined = flowObjectFilters !== undefined;
Expand All @@ -287,6 +346,8 @@ export class FlowSearchService {

const isFlowCategoryFilterDefined = flowCategoryFilters !== undefined;

const isOrderByForFlows = orderBy?.entity === 'flow';

if (
(!isFlowFilterDefined &&
(!isFlowObjectFilterDefined || !isFlowObjectFiltersNotEmpty) &&
Expand All @@ -296,6 +357,15 @@ export class FlowSearchService {
!isFlowCategoryFilterDefined)
) {
const flowConditions = this.prepareFlowConditions(flowFilters);
if (!isOrderByForFlows) {
return {
strategy: this.flowObjectFiltersStrategy,
conditions: {
conditionsMap: this.buildConditionsMap(flowConditions, {}),
flowCategoryFilters,
},
};
}
return {
strategy: this.onlyFlowFiltersStrategy,
conditions: flowConditions,
Expand Down Expand Up @@ -412,9 +482,7 @@ export class FlowSearchService {
private buildCursorCondition(
beforeCursor: string,
afterCursor: string,
orderBy:
| { column: FlowSortField; order: 'asc' | 'desc' }
| Array<{ column: FlowSortField; order: 'asc' | 'desc' }>
orderBy: FlowOrderBy
) {
if (beforeCursor && afterCursor) {
throw new Error('Cannot use before and after cursor at the same time');
Expand All @@ -424,20 +492,6 @@ export class FlowSearchService {
return {};
}

if (Array.isArray(orderBy)) {
// Build iterations of cursor conditions
const cursorConditions = orderBy.map((orderBy) => {
return this.buildCursorConditionForSingleOrderBy(
beforeCursor,
afterCursor,
orderBy
);
});

// Combine cursor conditions
return { [Cond.AND]: cursorConditions };
}

return this.buildCursorConditionForSingleOrderBy(
beforeCursor,
afterCursor,
Expand All @@ -448,7 +502,7 @@ export class FlowSearchService {
private buildCursorConditionForSingleOrderBy(
beforeCursor: string,
afterCursor: string,
orderBy: { column: FlowSortField; order: 'asc' | 'desc' }
orderBy: FlowOrderBy
) {
let cursorCondition;

Expand Down Expand Up @@ -482,20 +536,8 @@ export class FlowSearchService {
parentIDs: number[],
externalReferences: any[],
reportDetails: any[],
parkedParentSource: FlowParkedParentSource[],
sortColumn?: FlowSortField
): FlowPaged {
let cursor = sortColumn ? flow[sortColumn] : flow.updatedAt;

if (cursor instanceof Date) {
cursor = cursor.toISOString();
} else if (typeof cursor === 'number') {
cursor = cursor.toString();
} else if (typeof cursor === 'boolean' || cursor === null) {
// cases such as 'boolean'
cursor = flow.id.toString();
}

parkedParentSource: FlowParkedParentSource[]
): Flow {
return {
// Mandatory fields
id: flow.id.valueOf(),
Expand All @@ -518,8 +560,6 @@ export class FlowSearchService {
externalReferences,
reportDetails,
parkedParentSource,
// Paged item field
cursor,
};
}

Expand Down Expand Up @@ -558,7 +598,7 @@ export class FlowSearchService {
): Promise<FlowSearchResultNonPaginated> {
const flowSearchResponse = await this.search(models, args);

const flows: FlowPaged[] = flowSearchResponse.flows;
const flows: Flow[] = flowSearchResponse.flows;

let hasNextPage = flowSearchResponse.hasNextPage;

Expand Down
4 changes: 2 additions & 2 deletions src/domain-services/flows/graphql/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import Context from '../../Context';
import { FlowSearchService } from '../flow-search-service';
import { SearchFlowsArgs, SearchFlowsArgsNonPaginated } from './args';
import {
FlowPaged,
Flow,
FlowSearchResult,
FlowSearchResultNonPaginated,
FlowSearchTotalAmountResult,
} from './types';

@Service()
@Resolver(FlowPaged)
@Resolver(Flow)
export default class FlowResolver {
constructor(private flowSearchService: FlowSearchService) {}

Expand Down
56 changes: 25 additions & 31 deletions src/domain-services/flows/graphql/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Field, ObjectType } from 'type-graphql';
import { BaseType } from '../../../utils/graphql/base-types';
import { PageInfo, type IItemPaged } from '../../../utils/graphql/pagination';
import { PageInfo } from '../../../utils/graphql/pagination';
import { Category } from '../../categories/graphql/types';
import { BaseLocation } from '../../location/graphql/types';
import { Organization } from '../../organizations/graphql/types';
Expand Down Expand Up @@ -98,22 +98,16 @@ export class Flow extends BaseFlow {
parkedParentSource: FlowParkedParentSource[];
}

@ObjectType()
export class FlowPaged extends Flow implements IItemPaged {
@Field(() => String, { nullable: false })
cursor: string;
}

@ObjectType()
export class FlowSearchResult extends PageInfo<FlowSortField> {
@Field(() => [FlowPaged], { nullable: false })
flows: FlowPaged[];
@Field(() => [Flow], { nullable: false })
flows: Flow[];
}

@ObjectType()
export class FlowSearchResultNonPaginated {
@Field(() => [FlowPaged], { nullable: false })
flows: FlowPaged[];
@Field(() => [Flow], { nullable: false })
flows: Flow[];

@Field(() => Number, { nullable: false })
flowsCount: number;
Expand All @@ -129,23 +123,23 @@ export class FlowSearchTotalAmountResult {
}

export type FlowSortField =
| 'id'
| 'versionID'
| 'amountUSD'
| 'updatedAt'
| 'activeStatus'
| 'restricted'
| 'newMoney'
| 'flowDate'
| 'decisionDate'
| 'firstReportedDate'
| 'budgetYear'
| 'origAmount'
| 'origCurrency'
| 'exchangeRate'
| 'description'
| 'notes'
| 'versionStartDate'
| 'versionEndDate'
| 'createdAt'
| 'deletedAt';
| 'flow.id'
| 'flow.versionID'
| 'flow.amountUSD'
| 'flow.updatedAt'
| 'flow.activeStatus'
| 'flow.restricted'
| 'flow.newMoney'
| 'flow.flowDate'
| 'flow.decisionDate'
| 'flow.firstReportedDate'
| 'flow.budgetYear'
| 'flow.origAmount'
| 'flow.origCurrency'
| 'flow.exchangeRate'
| 'flow.description'
| 'flow.notes'
| 'flow.versionStartDate'
| 'flow.versionEndDate'
| 'flow.createdAt'
| 'flow.deletedAt';
8 changes: 7 additions & 1 deletion src/domain-services/flows/model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { type Database } from '@unocha/hpc-api-core/src/db';
import { type InstanceDataOfModel } from '@unocha/hpc-api-core/src/db/util/raw-model';

import { type SortOrder } from '../../utils/graphql/pagination';
export type FlowEntity = InstanceDataOfModel<Database['flow']>;

export type FlowOrderBy = {
column: string;
order: SortOrder;
entity: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class GetFlowIdsFromCategoryConditionsStrategyImpl

async search(
models: Database,
flowObjectsConditions: Map<string, Map<string, number[]>>,
_flowObjectsConditions: Map<string, Map<string, number[]>>,
flowCategoryConditions: FlowCategoryFilters
): Promise<FlowIdSearchStrategyResponse> {
const whereClause = mapFlowCategoryConditionsToWhereClause(
Expand Down
2 changes: 1 addition & 1 deletion src/utils/graphql/pagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class PageInfo<TSortFields extends string> {
}

export function prepareConditionFromCursor(
sortCondition: { column: string; order: 'asc' | 'desc' },
sortCondition: { column: string; order: SortOrder },
afterCursor?: number,
beforeCursor?: number
): any {
Expand Down

0 comments on commit d909985

Please sign in to comment.