Skip to content

Commit

Permalink
Improved database querying for individuals profiles (#2360)
Browse files Browse the repository at this point in the history
* refactor(workflows-service): now fetching workflows via endusers instead of using repository

* refactor(workflows-service): improved performance on profiles query

* refactor(workflow-service): renamed iterable name
  • Loading branch information
Omri-Levy authored Jul 9, 2024
1 parent 73756ea commit 304062e
Show file tree
Hide file tree
Showing 7 changed files with 585 additions and 600 deletions.
2 changes: 1 addition & 1 deletion services/workflows-service/prisma/data-migrations
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- CreateIndex
CREATE INDEX "Counterparty_endUserId_idx" ON "Counterparty"("endUserId");
1 change: 1 addition & 0 deletions services/workflows-service/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,7 @@ model Counterparty {
@@unique([projectId, correlationId])
@@index([correlationId])
@@index([endUserId])
}

enum BusinessReportStatus {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,19 @@ export class CaseManagementController {
@CurrentProject() projectId: TProjectId,
@Query() searchQueryParams: z.infer<typeof ListIndividualsProfilesSchema>,
) {
const tagToKyc = {
[StateTag.COLLECTION_FLOW]: 'PENDING',
[StateTag.APPROVED]: 'APPROVED',
[StateTag.REJECTED]: 'REJECTED',
[StateTag.REVISION]: 'REVISIONS',
[StateTag.PENDING_PROCESS]: 'PROCESSED',
[StateTag.DATA_ENRICHMENT]: 'PROCESSED',
[StateTag.MANUAL_REVIEW]: 'PROCESSED',
} as const satisfies Record<
Exclude<TStateTag, 'failure' | 'flagged' | 'resolved' | 'dismissed'>,
'APPROVED' | 'REJECTED' | 'REVISIONS' | 'PROCESSED' | 'PENDING'
>;

const endUsers = await this.endUserService.list(
{
select: {
Expand All @@ -103,6 +116,24 @@ export class CaseManagementController {
companyName: true,
},
},
Counterparty: {
select: {
alerts: true,
},
},
workflowRuntimeData: {
select: {
tags: true,
},
where: {
OR: Object.keys(tagToKyc).map(key => ({
tags: {
array_contains: key,
},
})),
},
take: 1,
},
amlHits: true,
activeMonitorings: true,
updatedAt: true,
Expand All @@ -126,62 +157,54 @@ export class CaseManagementController {
position: EndUsersOnBusinesses['position'];
business: Pick<Business, 'companyName'>;
}>;
workflowRuntimeData: Array<{
tags: string[];
}>;
businesses: Array<Pick<Business, 'companyName'>>;
Counterparty: {
alerts: Array<{
id: string;
}>;
};
}
>;

const tagToKyc = {
[StateTag.COLLECTION_FLOW]: 'PENDING',
[StateTag.APPROVED]: 'APPROVED',
[StateTag.REJECTED]: 'REJECTED',
[StateTag.REVISION]: 'REVISIONS',
[StateTag.PENDING_PROCESS]: 'PROCESSED',
[StateTag.DATA_ENRICHMENT]: 'PROCESSED',
[StateTag.MANUAL_REVIEW]: 'PROCESSED',
} as const satisfies Record<
Exclude<TStateTag, 'failure' | 'flagged' | 'resolved' | 'dismissed'>,
'APPROVED' | 'REJECTED' | 'REVISIONS' | 'PROCESSED' | 'PENDING'
>;

return await Promise.all(
typedEndUsers.map(async endUser => {
const workflowRuntimeData = await this.workflowService.getByEntityId(endUser.id, projectId);
const tag = (workflowRuntimeData?.tags as string[])?.find(
tag => !!tagToKyc[tag as keyof typeof tagToKyc],
);
const alerts = await this.alertsService.getAlertsByEntityId(endUser.id, projectId);
const checkIsMonitored = () =>
Array.isArray(endUser.activeMonitorings) && !!endUser.activeMonitorings?.length;
const getMatches = () => {
const amlHits = (endUser.amlHits as z.infer<typeof EndUserAmlHitsSchema>)?.length ?? 0;
const isPlural = amlHits > 1 || amlHits === 0;
return typedEndUsers.map(endUser => {
const tag = endUser.workflowRuntimeData?.[0]?.tags?.find(
tag => !!tagToKyc[tag as keyof typeof tagToKyc],
);
const alerts = endUser.Counterparty?.alerts;
const checkIsMonitored = () =>
Array.isArray(endUser.activeMonitorings) && !!endUser.activeMonitorings?.length;
const getMatches = () => {
const amlHits = (endUser.amlHits as z.infer<typeof EndUserAmlHitsSchema>)?.length ?? 0;
const isPlural = amlHits > 1 || amlHits === 0;

return `${amlHits} ${isPlural ? 'matches' : 'match'}`;
};
const isMonitored = checkIsMonitored();
const matches = getMatches();
return `${amlHits} ${isPlural ? 'matches' : 'match'}`;
};
const isMonitored = checkIsMonitored();
const matches = getMatches();

const businesses = endUser.businesses?.length
? endUser.businesses.map(business => business.companyName).join(', ')
: endUser.endUsersOnBusinesses
?.map(endUserOnBusiness => endUserOnBusiness.business.companyName)
.join(', ');
const businesses = endUser.businesses?.length
? endUser.businesses.map(business => business.companyName).join(', ')
: endUser.endUsersOnBusinesses
?.map(endUserOnBusiness => endUserOnBusiness.business.companyName)
.join(', ');

return {
correlationId: endUser.correlationId,
createdAt: endUser.createdAt,
name: `${endUser.firstName} ${endUser.lastName}`,
businesses,
roles: endUser.endUsersOnBusinesses?.flatMap(
endUserOnBusiness => endUserOnBusiness.position,
),
kyc: tagToKyc[tag as keyof typeof tagToKyc],
isMonitored,
matches,
alerts: alerts?.length ?? 0,
updatedAt: endUser.updatedAt,
};
}),
);
return {
correlationId: endUser.correlationId,
createdAt: endUser.createdAt,
name: `${endUser.firstName} ${endUser.lastName}`,
businesses,
roles: endUser.endUsersOnBusinesses?.flatMap(
endUserOnBusiness => endUserOnBusiness.position,
),
kyc: tagToKyc[tag as keyof typeof tagToKyc],
isMonitored,
matches,
alerts: alerts?.length ?? 0,
updatedAt: endUser.updatedAt,
};
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -358,30 +358,4 @@ export class WorkflowRuntimeDataRepository {

return (await this.prisma.$queryRaw(sql)) as WorkflowRuntimeData[];
}

async findFirstByEntityId<T extends Prisma.WorkflowRuntimeDataFindFirstArgs>(
entityId: string,
projectIds: TProjectIds,
args?: Prisma.SelectSubset<T, Prisma.WorkflowRuntimeDataFindFirstArgs>,
) {
return await this.prisma.workflowRuntimeData.findFirst(
this.scopeService.scopeFindFirst(
{
...args,
where: {
...args?.where,
OR: [
{
endUserId: entityId,
},
{
businessId: entityId,
},
],
},
},
projectIds,
),
);
}
}
12 changes: 0 additions & 12 deletions services/workflows-service/src/workflow/workflow.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2367,16 +2367,4 @@ export class WorkflowService {
data: args,
});
}

async getByEntityId(
entityId: string,
projectId: TProjectId,
args?: Parameters<WorkflowRuntimeDataRepository['findById']>[1],
) {
return await this.workflowRuntimeDataRepository.findFirstByEntityId(
entityId,
[projectId],
args,
);
}
}
Loading

0 comments on commit 304062e

Please sign in to comment.