From 2cb7c772dea2c03348b481320d1144a546191212 Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Tue, 27 Feb 2024 00:05:20 +0200 Subject: [PATCH 01/20] feat: ongoing monitoring --- services/workflows-service/package.json | 2 +- .../workflows-service/prisma/data-migrations | 2 +- .../migration.sql | 1 + .../workflows-service/prisma/schema.prisma | 1 + services/workflows-service/src/app.module.ts | 2 + .../src/customer/schemas/zod-schemas.ts | 4 ++ .../src/end-user/end-user.repository.ts | 14 +++- .../src/end-user/end-user.service.ts | 1 + .../src/filter/filter.repository.ts | 11 +-- services/workflows-service/src/global.d.ts | 5 +- .../src/webhooks/dtos/aml-webhook-input.ts | 11 +++ .../dtos/individual-aml-webhook-input.ts | 53 ++++++++++++++ .../src/webhooks/webhooks.controller.ts | 55 +++++++++++++++ .../src/webhooks/webhooks.module.ts | 70 +++++++++++++++++++ .../src/webhooks/webhooks.service.ts | 50 +++++++++++++ .../workflow-definition.service.ts | 6 +- .../workflow/workflow.controller.external.ts | 1 + .../src/workflow/workflow.service.ts | 19 ----- 18 files changed, 276 insertions(+), 32 deletions(-) create mode 100644 services/workflows-service/prisma/migrations/20240226191757_add_config_column_to_customer/migration.sql create mode 100644 services/workflows-service/src/webhooks/dtos/aml-webhook-input.ts create mode 100644 services/workflows-service/src/webhooks/dtos/individual-aml-webhook-input.ts create mode 100644 services/workflows-service/src/webhooks/webhooks.controller.ts create mode 100644 services/workflows-service/src/webhooks/webhooks.module.ts create mode 100644 services/workflows-service/src/webhooks/webhooks.service.ts diff --git a/services/workflows-service/package.json b/services/workflows-service/package.json index 6b574dd4a1..ab8f3ebf3f 100644 --- a/services/workflows-service/package.json +++ b/services/workflows-service/package.json @@ -25,7 +25,7 @@ "db:migrate-up": "prisma migrate deploy", "db:reset": "prisma migrate reset --skip-seed -f", "db:reset:dev": "npm run db:reset && npm run seed", - "db:reset:dev:with-data": "npm run db:reset && npm run seed && npm run db:data-migration:migrate", + "db:reset:dev:with-data": "npm run db:reset:dev && npm run db:data-migration:migrate", "db:init": "npm run db:migrate-dev -- --name 'initial version' && npm run db:migrate-up seed", "prisma:generate": "prisma generate", "docker:db": "docker compose -f docker-compose.db.yml up -d --wait", diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index 5c2b920641..479921bb5d 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit 5c2b920641fb49cdb87df3a130495938b6fb57d7 +Subproject commit 479921bb5daa0b55686a85ea11ad5a5001e7e0c5 diff --git a/services/workflows-service/prisma/migrations/20240226191757_add_config_column_to_customer/migration.sql b/services/workflows-service/prisma/migrations/20240226191757_add_config_column_to_customer/migration.sql new file mode 100644 index 0000000000..406ae3962b --- /dev/null +++ b/services/workflows-service/prisma/migrations/20240226191757_add_config_column_to_customer/migration.sql @@ -0,0 +1 @@ +ALTER TABLE "Customer" ADD COLUMN "config" JSONB; diff --git a/services/workflows-service/prisma/schema.prisma b/services/workflows-service/prisma/schema.prisma index 4361c94d8c..b32e5ca8ee 100644 --- a/services/workflows-service/prisma/schema.prisma +++ b/services/workflows-service/prisma/schema.prisma @@ -283,6 +283,7 @@ model Customer { logoImageUri String faviconImageUri String @default("") // TODO: remove default value after data migration customerStatus CustomerStatuses @default(onboarding) + config Json? authenticationConfiguration Json? country String? language String? diff --git a/services/workflows-service/src/app.module.ts b/services/workflows-service/src/app.module.ts index cb54c2bf5e..6d3243cb54 100644 --- a/services/workflows-service/src/app.module.ts +++ b/services/workflows-service/src/app.module.ts @@ -37,6 +37,7 @@ import { initHttpMoudle } from '@/common/http-service/http-config.service'; import { DataMigrationModule } from '@/data-migration/data-migration.module'; import { CaseManagementModule } from '@/case-management/case-management.module'; import { WorkflowModule } from '@/workflow/workflow.module'; +import { WebhooksModule } from '@/webhooks/webhooks.module'; @Module({ controllers: [MetricsController], @@ -50,6 +51,7 @@ import { WorkflowModule } from '@/workflow/workflow.module'; EventEmitterModule.forRoot(), UserModule, WorkflowModule, + WebhooksModule, UiDefinitionModule, StorageModule, DataMigrationModule, diff --git a/services/workflows-service/src/customer/schemas/zod-schemas.ts b/services/workflows-service/src/customer/schemas/zod-schemas.ts index 997c3f02f9..a6edbdcb11 100644 --- a/services/workflows-service/src/customer/schemas/zod-schemas.ts +++ b/services/workflows-service/src/customer/schemas/zod-schemas.ts @@ -4,3 +4,7 @@ import { z } from 'zod'; export const CustomerSubscriptionSchema = z.object({ subscriptions: z.array(SubscriptionSchema) }); export type TCustomerSubscription = z.infer; + +const CustomerConfigSchema = z.object({ ongoingWorkflowDefinitionId: z.string() }); + +export type TCustomerConfig = z.infer; diff --git a/services/workflows-service/src/end-user/end-user.repository.ts b/services/workflows-service/src/end-user/end-user.repository.ts index 61d3a72f22..b75a92b935 100644 --- a/services/workflows-service/src/end-user/end-user.repository.ts +++ b/services/workflows-service/src/end-user/end-user.repository.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { Prisma } from '@prisma/client'; -import { PrismaService } from '../prisma/prisma.service'; +import { PrismaService } from '@/prisma/prisma.service'; import { EndUserModel } from './end-user.model'; import type { TProjectIds } from '@/types'; import { ProjectScopeService } from '@/project/project-scope.service'; @@ -48,6 +48,18 @@ export class EndUserRepository { ); } + async findByIdUnscoped>( + id: string, + args: Prisma.SelectSubset>, + ) { + return await this.prisma.endUser.findFirstOrThrow( + this.scopeService.scopeFindFirst({ + where: { id }, + ...args, + }), + ); + } + async findByCorrelationId>( id: string, args: Prisma.SelectSubset>, diff --git a/services/workflows-service/src/end-user/end-user.service.ts b/services/workflows-service/src/end-user/end-user.service.ts index be8cbc6cbf..5c1a70b834 100644 --- a/services/workflows-service/src/end-user/end-user.service.ts +++ b/services/workflows-service/src/end-user/end-user.service.ts @@ -76,6 +76,7 @@ export class EndUserService { projectIds, ); } + async updateById(id: string, endUser: Omit) { return await this.repository.updateById(id, endUser); } diff --git a/services/workflows-service/src/filter/filter.repository.ts b/services/workflows-service/src/filter/filter.repository.ts index 28b36d34b7..b102698e4d 100644 --- a/services/workflows-service/src/filter/filter.repository.ts +++ b/services/workflows-service/src/filter/filter.repository.ts @@ -27,10 +27,13 @@ export class FilterRepository { async findById(id: string, args: Prisma.FilterFindFirstArgs, projectIds: TProjectIds) { return await this.prisma.filter.findFirst( - this.scopeService.scopeFindFirst({ - ...args, - where: { ...args?.where, id: id }, - }), + this.scopeService.scopeFindFirst( + { + ...args, + where: { ...args?.where, id: id }, + }, + projectIds, + ), ); } diff --git a/services/workflows-service/src/global.d.ts b/services/workflows-service/src/global.d.ts index ca43e76544..72b36c04a6 100644 --- a/services/workflows-service/src/global.d.ts +++ b/services/workflows-service/src/global.d.ts @@ -2,19 +2,22 @@ declare module '@prisma/client' { import type { WorkflowRuntimeData as _WorkflowRuntimeData, WorkflowDefinition as _WorkflowDefinition, - Customer as Customer_, } from '@prisma/client/index'; + import type { WorkflowConfig } from '@/workflow/schemas/zod-schemas'; + import type { TCustomerConfig, TCustomerSubscription } from '@/customer/schemas/zod-schemas'; export * from '@prisma/client/index'; export type WorkflowRuntimeData = Omit<_WorkflowRuntimeData, 'context'> & { context: any; config: WorkflowConfig | any; }; + export type WorkflowDefinition = Omit<_WorkflowDefinition, 'config'> & { config: WorkflowConfig | any; }; export type Customer = Omit<_Customer, 'subscriptions'> & { + config: TCustomerConfig | any; subscriptions: TCustomerSubscription | any; }; } diff --git a/services/workflows-service/src/webhooks/dtos/aml-webhook-input.ts b/services/workflows-service/src/webhooks/dtos/aml-webhook-input.ts new file mode 100644 index 0000000000..34916c7b9a --- /dev/null +++ b/services/workflows-service/src/webhooks/dtos/aml-webhook-input.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class AmlWebhookInput { + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + entityType!: string; +} diff --git a/services/workflows-service/src/webhooks/dtos/individual-aml-webhook-input.ts b/services/workflows-service/src/webhooks/dtos/individual-aml-webhook-input.ts new file mode 100644 index 0000000000..7f4ec6469e --- /dev/null +++ b/services/workflows-service/src/webhooks/dtos/individual-aml-webhook-input.ts @@ -0,0 +1,53 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class IndividualAmlWebhookInput { + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + id!: string; + + @ApiProperty({ + required: true, + type: Number, + }) + @IsString() + apiVersion!: number; + + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + timestamp!: string; + + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + eventName!: string; + + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + @IsOptional() + entityId!: string; + + @ApiProperty({ + required: false, + type: String, + }) + @IsString() + @IsOptional() + environment?: string; + + @ApiProperty({ + required: true, + }) + data!: unknown; +} diff --git a/services/workflows-service/src/webhooks/webhooks.controller.ts b/services/workflows-service/src/webhooks/webhooks.controller.ts new file mode 100644 index 0000000000..0852928091 --- /dev/null +++ b/services/workflows-service/src/webhooks/webhooks.controller.ts @@ -0,0 +1,55 @@ +import * as common from '@nestjs/common'; +import * as swagger from '@nestjs/swagger'; +import * as nestAccessControl from 'nest-access-control'; +import * as errors from '../errors'; +import { Public } from '@/common/decorators/public.decorator'; +import { AmlWebhookInput } from './dtos/aml-webhook-input'; +import { IndividualAmlWebhookInput } from '@/webhooks/dtos/individual-aml-webhook-input'; +import { WebhooksService } from '@/webhooks/webhooks.service'; + +const WEBHOOKS = { + AML_HIT: 'AML_HIT', +} as const; + +const ENTITY_TYPES = { + BUSINESS: 'business', + INDIVIDUALS: 'individuals', +} as const; + +@swagger.ApiBearerAuth() +@swagger.ApiTags('Webhooks') +@common.Controller('webhooks') +export class WebhooksController { + constructor( + @nestAccessControl.InjectRolesBuilder() + protected readonly rolesBuilder: nestAccessControl.RolesBuilder, + private readonly webhooksService: WebhooksService, + ) {} + + @common.Post('/:entityType/aml') + @swagger.ApiOkResponse() + @common.HttpCode(200) + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) + @Public() + // @VerifyUnifiedApiSignatureDecorator() + async amlHook( + @common.Param() { entityType }: AmlWebhookInput, + @common.Body() data: IndividualAmlWebhookInput, + ) { + try { + if (entityType === ENTITY_TYPES.INDIVIDUALS) { + const { eventName } = data; + + if (eventName === WEBHOOKS.AML_HIT) { + await this.webhooksService.handleAmlHit(data as IndividualAmlWebhookInput); + } + } + } catch (error) { + console.error(error); + + throw error; + } + + return; + } +} diff --git a/services/workflows-service/src/webhooks/webhooks.module.ts b/services/workflows-service/src/webhooks/webhooks.module.ts new file mode 100644 index 0000000000..3a0653b566 --- /dev/null +++ b/services/workflows-service/src/webhooks/webhooks.module.ts @@ -0,0 +1,70 @@ +import { AuthModule } from '@/auth/auth.module'; +import { ACLModule } from '@/common/access-control/acl.module'; +import { CustomerModule } from '@/customer/customer.module'; +import { PrismaModule } from '@/prisma/prisma.module'; +import { ProjectModule } from '@/project/project.module'; +import { WorkflowDefinitionModule } from '@/workflow-defintion/workflow-definition.module'; +import { HttpModule } from '@nestjs/axios'; +import { Module, forwardRef } from '@nestjs/common'; +import { WorkflowService } from '@/workflow/workflow.service'; +import { WorkflowRuntimeDataRepository } from '@/workflow/workflow-runtime-data.repository'; +import { EndUserRepository } from '@/end-user/end-user.repository'; +import { EndUserService } from '@/end-user/end-user.service'; +import { BusinessRepository } from '@/business/business.repository'; +import { EntityRepository } from '@/common/entity/entity.repository'; +import { FileService } from '@/providers/file/file.service'; +import { WorkflowEventEmitterService } from '@/workflow/workflow-event-emitter.service'; +import { UserService } from '@/user/user.service'; +import { SalesforceService } from '@/salesforce/salesforce.service'; +import { WorkflowTokenService } from '@/auth/workflow-token/workflow-token.service'; +import { UiDefinitionService } from '@/ui-definition/ui-definition.service'; +import { StorageService } from '@/storage/storage.service'; +import { UserRepository } from '@/user/user.repository'; +import { SalesforceIntegrationRepository } from '@/salesforce/salesforce-integration.repository'; +import { WorkflowTokenRepository } from '@/auth/workflow-token/workflow-token.repository'; +import { UiDefinitionRepository } from '@/ui-definition/ui-definition.repository'; +import { FileRepository } from '@/storage/storage.repository'; +import { HookCallbackHandlerService } from '@/workflow/hook-callback-handler.service'; +import { FilterService } from '@/filter/filter.service'; +import { FilterRepository } from '@/filter/filter.repository'; +import { WebhooksController } from '@/webhooks/webhooks.controller'; +import { WebhooksService } from '@/webhooks/webhooks.service'; + +@Module({ + controllers: [WebhooksController], + imports: [ + ACLModule, + forwardRef(() => AuthModule), + HttpModule, + ProjectModule, + PrismaModule, + CustomerModule, + WorkflowDefinitionModule, + ], + providers: [ + WorkflowService, + WorkflowRuntimeDataRepository, + EndUserService, + EndUserRepository, + BusinessRepository, + EntityRepository, + FileService, + FileRepository, + WorkflowEventEmitterService, + UserService, + UserRepository, + SalesforceService, + SalesforceIntegrationRepository, + WorkflowTokenService, + WorkflowTokenRepository, + UiDefinitionService, + UiDefinitionRepository, + StorageService, + HookCallbackHandlerService, + FilterService, + FilterRepository, + WebhooksService, + ], + exports: [], +}) +export class WebhooksModule {} diff --git a/services/workflows-service/src/webhooks/webhooks.service.ts b/services/workflows-service/src/webhooks/webhooks.service.ts new file mode 100644 index 0000000000..dd35f12c19 --- /dev/null +++ b/services/workflows-service/src/webhooks/webhooks.service.ts @@ -0,0 +1,50 @@ +import { Injectable } from '@nestjs/common'; +import { IndividualAmlWebhookInput } from '@/webhooks/dtos/individual-aml-webhook-input'; +import { EndUserRepository } from '@/end-user/end-user.repository'; +import { CustomerService } from '@/customer/customer.service'; +import { WorkflowDefinitionService } from '@/workflow-defintion/workflow-definition.service'; +import { WorkflowService } from '@/workflow/workflow.service'; + +@Injectable() +export class WebhooksService { + constructor( + private readonly customerService: CustomerService, + private readonly workflowService: WorkflowService, + private readonly endUserRepository: EndUserRepository, + private readonly workflowDefinitionService: WorkflowDefinitionService, + ) {} + + async handleAmlHit({ entityId }: IndividualAmlWebhookInput) { + const { projectId } = await this.endUserRepository.findByIdUnscoped(entityId, { + select: { + projectId: true, + }, + }); + + const { config } = await this.customerService.getByProjectId(projectId, { + select: { config: true }, + }); + + if (!config?.ongoingWorkflowDefinitionId) { + return; + } + + const { id: workflowDefinitionId } = await this.workflowDefinitionService.getLatestVersion( + config.ongoingWorkflowDefinitionId, + [projectId], + ); + + await this.workflowService.createOrUpdateWorkflowRuntime({ + workflowDefinitionId, + context: { + entity: { + id: entityId, + type: 'individual', + }, + documents: [], + }, + projectIds: [projectId], + currentProjectId: projectId, + }); + } +} diff --git a/services/workflows-service/src/workflow-defintion/workflow-definition.service.ts b/services/workflows-service/src/workflow-defintion/workflow-definition.service.ts index 22dbfe1da5..e573398960 100644 --- a/services/workflows-service/src/workflow-defintion/workflow-definition.service.ts +++ b/services/workflows-service/src/workflow-defintion/workflow-definition.service.ts @@ -80,12 +80,8 @@ export class WorkflowDefinitionService { async getLatestVersion(id: string, projectIds: TProjectIds) { const workflowDefinition = await this.repository.findById(id, {}, projectIds); - const latestVersion = await this.repository.findByLatestVersion( - workflowDefinition.name, - projectIds, - ); - return latestVersion; + return await this.repository.findByLatestVersion(workflowDefinition.name, projectIds); } async getLatestDefinitionWithTransitionSchema( diff --git a/services/workflows-service/src/workflow/workflow.controller.external.ts b/services/workflows-service/src/workflow/workflow.controller.external.ts index 46c96f5d6f..fe4e401cdb 100644 --- a/services/workflows-service/src/workflow/workflow.controller.external.ts +++ b/services/workflows-service/src/workflow/workflow.controller.external.ts @@ -174,6 +174,7 @@ export class WorkflowControllerExternal { const hasSalesforceRecord = Boolean(body.salesforceObjectName) && Boolean(body.salesforceRecordId); + const latestDefinitionVersion = await this.workflowDefinitionService.getLatestVersion( workflowId, projectIds, diff --git a/services/workflows-service/src/workflow/workflow.service.ts b/services/workflows-service/src/workflow/workflow.service.ts index 633c683c57..4872014b21 100644 --- a/services/workflows-service/src/workflow/workflow.service.ts +++ b/services/workflows-service/src/workflow/workflow.service.ts @@ -85,27 +85,8 @@ import { ajv } from '@/common/ajv/ajv.validator'; type TEntityId = string; -export const ResubmissionReason = { - BLURRY_IMAGE: 'BLURRY_IMAGE', - CUT_IMAGE: 'CUT_IMAGE', - UNSUPPORTED_DOCUMENT: 'UNSUPPORTED_DOCUMENT', - DAMAGED_DOCUMENT: 'DAMAGED_DOCUMENT', - EXPIRED_DOCUMENT: 'EXPIRED_DOCUMENT', - COPY_OF_A_COPY: 'COPY_OF_A_COPY', - FACE_IS_UNCLEAR: 'FACE_IS_UNCLEAR', - FACE_IS_NOT_MATCHING: 'FACE_IS_NOT_MATCHING', -} as const; - -export interface WorkflowData { - workflowDefinition: object; - workflowRuntimeData: object; -} - export type TEntityType = 'endUser' | 'business'; -// Discuss model classes location -export type IntentResponse = WorkflowData[]; - // TODO: TEMP (STUB) const policies = { kycSignup: () => { From 1e8a6de6274b175882305f49061b7cf37455f69d Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Tue, 27 Feb 2024 00:05:20 +0200 Subject: [PATCH 02/20] chore(package.json): update db:reset:dev:with-data script to use db:reset:dev script before running db:data-migration:migrate fix(prisma): update subproject commit hash in data-migrations feat(prisma): add config column to Customer table feat(app.module): import WebhooksModule feat(zod-schemas): add CustomerConfigSchema and TCustomerConfig types fix(end-user.repository): update import path for PrismaService feat(end-user.service): add missing line break fix(filter.repository): update scopeService.scopeFindFirst method call fix(global.d.ts): add config and subscriptions fields to Customer type feat(webhooks): add AmlWebhookInput and IndividualAmlWebhookInput DTOs feat(webhooks): add webhooks controller, module, and service - Add a new file `webhooks.controller.ts` to handle webhooks related requests. - Add a new file `webhooks.module.ts` to define the webhooks module and its dependencies. - Add a new file `webhooks.service.ts` to handle webhook events. - Implement the `amlHook` method in the `WebhooksController` to handle AML webhook events. - Implement the `handleAmlHit` method in the `WebhooksService` to handle AML_HIT events. - Update the imports and dependencies in the existing files to include the newly added files. The purpose of these changes is to introduce a new feature that allows handling AML webhook events in the application. This feature will enable the application to respond to AML_HIT events and perform necessary actions based on the event data. --- services/workflows-service/package.json | 2 +- .../workflows-service/prisma/data-migrations | 2 +- .../migration.sql | 1 + .../workflows-service/prisma/schema.prisma | 1 + services/workflows-service/src/app.module.ts | 2 + .../src/customer/schemas/zod-schemas.ts | 4 ++ .../src/end-user/end-user.repository.ts | 14 +++- .../src/end-user/end-user.service.ts | 1 + .../src/filter/filter.repository.ts | 11 +-- services/workflows-service/src/global.d.ts | 5 +- .../src/webhooks/dtos/aml-webhook-input.ts | 11 +++ .../dtos/individual-aml-webhook-input.ts | 53 ++++++++++++++ .../src/webhooks/webhooks.controller.ts | 55 +++++++++++++++ .../src/webhooks/webhooks.module.ts | 70 +++++++++++++++++++ .../src/webhooks/webhooks.service.ts | 50 +++++++++++++ .../workflow-definition.service.ts | 6 +- .../workflow/workflow.controller.external.ts | 1 + .../src/workflow/workflow.service.ts | 19 ----- 18 files changed, 276 insertions(+), 32 deletions(-) create mode 100644 services/workflows-service/prisma/migrations/20240226191757_add_config_column_to_customer/migration.sql create mode 100644 services/workflows-service/src/webhooks/dtos/aml-webhook-input.ts create mode 100644 services/workflows-service/src/webhooks/dtos/individual-aml-webhook-input.ts create mode 100644 services/workflows-service/src/webhooks/webhooks.controller.ts create mode 100644 services/workflows-service/src/webhooks/webhooks.module.ts create mode 100644 services/workflows-service/src/webhooks/webhooks.service.ts diff --git a/services/workflows-service/package.json b/services/workflows-service/package.json index 6b574dd4a1..ab8f3ebf3f 100644 --- a/services/workflows-service/package.json +++ b/services/workflows-service/package.json @@ -25,7 +25,7 @@ "db:migrate-up": "prisma migrate deploy", "db:reset": "prisma migrate reset --skip-seed -f", "db:reset:dev": "npm run db:reset && npm run seed", - "db:reset:dev:with-data": "npm run db:reset && npm run seed && npm run db:data-migration:migrate", + "db:reset:dev:with-data": "npm run db:reset:dev && npm run db:data-migration:migrate", "db:init": "npm run db:migrate-dev -- --name 'initial version' && npm run db:migrate-up seed", "prisma:generate": "prisma generate", "docker:db": "docker compose -f docker-compose.db.yml up -d --wait", diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index 5c2b920641..479921bb5d 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit 5c2b920641fb49cdb87df3a130495938b6fb57d7 +Subproject commit 479921bb5daa0b55686a85ea11ad5a5001e7e0c5 diff --git a/services/workflows-service/prisma/migrations/20240226191757_add_config_column_to_customer/migration.sql b/services/workflows-service/prisma/migrations/20240226191757_add_config_column_to_customer/migration.sql new file mode 100644 index 0000000000..406ae3962b --- /dev/null +++ b/services/workflows-service/prisma/migrations/20240226191757_add_config_column_to_customer/migration.sql @@ -0,0 +1 @@ +ALTER TABLE "Customer" ADD COLUMN "config" JSONB; diff --git a/services/workflows-service/prisma/schema.prisma b/services/workflows-service/prisma/schema.prisma index 4361c94d8c..b32e5ca8ee 100644 --- a/services/workflows-service/prisma/schema.prisma +++ b/services/workflows-service/prisma/schema.prisma @@ -283,6 +283,7 @@ model Customer { logoImageUri String faviconImageUri String @default("") // TODO: remove default value after data migration customerStatus CustomerStatuses @default(onboarding) + config Json? authenticationConfiguration Json? country String? language String? diff --git a/services/workflows-service/src/app.module.ts b/services/workflows-service/src/app.module.ts index cb54c2bf5e..6d3243cb54 100644 --- a/services/workflows-service/src/app.module.ts +++ b/services/workflows-service/src/app.module.ts @@ -37,6 +37,7 @@ import { initHttpMoudle } from '@/common/http-service/http-config.service'; import { DataMigrationModule } from '@/data-migration/data-migration.module'; import { CaseManagementModule } from '@/case-management/case-management.module'; import { WorkflowModule } from '@/workflow/workflow.module'; +import { WebhooksModule } from '@/webhooks/webhooks.module'; @Module({ controllers: [MetricsController], @@ -50,6 +51,7 @@ import { WorkflowModule } from '@/workflow/workflow.module'; EventEmitterModule.forRoot(), UserModule, WorkflowModule, + WebhooksModule, UiDefinitionModule, StorageModule, DataMigrationModule, diff --git a/services/workflows-service/src/customer/schemas/zod-schemas.ts b/services/workflows-service/src/customer/schemas/zod-schemas.ts index 997c3f02f9..a6edbdcb11 100644 --- a/services/workflows-service/src/customer/schemas/zod-schemas.ts +++ b/services/workflows-service/src/customer/schemas/zod-schemas.ts @@ -4,3 +4,7 @@ import { z } from 'zod'; export const CustomerSubscriptionSchema = z.object({ subscriptions: z.array(SubscriptionSchema) }); export type TCustomerSubscription = z.infer; + +const CustomerConfigSchema = z.object({ ongoingWorkflowDefinitionId: z.string() }); + +export type TCustomerConfig = z.infer; diff --git a/services/workflows-service/src/end-user/end-user.repository.ts b/services/workflows-service/src/end-user/end-user.repository.ts index 61d3a72f22..b75a92b935 100644 --- a/services/workflows-service/src/end-user/end-user.repository.ts +++ b/services/workflows-service/src/end-user/end-user.repository.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { Prisma } from '@prisma/client'; -import { PrismaService } from '../prisma/prisma.service'; +import { PrismaService } from '@/prisma/prisma.service'; import { EndUserModel } from './end-user.model'; import type { TProjectIds } from '@/types'; import { ProjectScopeService } from '@/project/project-scope.service'; @@ -48,6 +48,18 @@ export class EndUserRepository { ); } + async findByIdUnscoped>( + id: string, + args: Prisma.SelectSubset>, + ) { + return await this.prisma.endUser.findFirstOrThrow( + this.scopeService.scopeFindFirst({ + where: { id }, + ...args, + }), + ); + } + async findByCorrelationId>( id: string, args: Prisma.SelectSubset>, diff --git a/services/workflows-service/src/end-user/end-user.service.ts b/services/workflows-service/src/end-user/end-user.service.ts index be8cbc6cbf..5c1a70b834 100644 --- a/services/workflows-service/src/end-user/end-user.service.ts +++ b/services/workflows-service/src/end-user/end-user.service.ts @@ -76,6 +76,7 @@ export class EndUserService { projectIds, ); } + async updateById(id: string, endUser: Omit) { return await this.repository.updateById(id, endUser); } diff --git a/services/workflows-service/src/filter/filter.repository.ts b/services/workflows-service/src/filter/filter.repository.ts index 28b36d34b7..b102698e4d 100644 --- a/services/workflows-service/src/filter/filter.repository.ts +++ b/services/workflows-service/src/filter/filter.repository.ts @@ -27,10 +27,13 @@ export class FilterRepository { async findById(id: string, args: Prisma.FilterFindFirstArgs, projectIds: TProjectIds) { return await this.prisma.filter.findFirst( - this.scopeService.scopeFindFirst({ - ...args, - where: { ...args?.where, id: id }, - }), + this.scopeService.scopeFindFirst( + { + ...args, + where: { ...args?.where, id: id }, + }, + projectIds, + ), ); } diff --git a/services/workflows-service/src/global.d.ts b/services/workflows-service/src/global.d.ts index ca43e76544..72b36c04a6 100644 --- a/services/workflows-service/src/global.d.ts +++ b/services/workflows-service/src/global.d.ts @@ -2,19 +2,22 @@ declare module '@prisma/client' { import type { WorkflowRuntimeData as _WorkflowRuntimeData, WorkflowDefinition as _WorkflowDefinition, - Customer as Customer_, } from '@prisma/client/index'; + import type { WorkflowConfig } from '@/workflow/schemas/zod-schemas'; + import type { TCustomerConfig, TCustomerSubscription } from '@/customer/schemas/zod-schemas'; export * from '@prisma/client/index'; export type WorkflowRuntimeData = Omit<_WorkflowRuntimeData, 'context'> & { context: any; config: WorkflowConfig | any; }; + export type WorkflowDefinition = Omit<_WorkflowDefinition, 'config'> & { config: WorkflowConfig | any; }; export type Customer = Omit<_Customer, 'subscriptions'> & { + config: TCustomerConfig | any; subscriptions: TCustomerSubscription | any; }; } diff --git a/services/workflows-service/src/webhooks/dtos/aml-webhook-input.ts b/services/workflows-service/src/webhooks/dtos/aml-webhook-input.ts new file mode 100644 index 0000000000..34916c7b9a --- /dev/null +++ b/services/workflows-service/src/webhooks/dtos/aml-webhook-input.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class AmlWebhookInput { + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + entityType!: string; +} diff --git a/services/workflows-service/src/webhooks/dtos/individual-aml-webhook-input.ts b/services/workflows-service/src/webhooks/dtos/individual-aml-webhook-input.ts new file mode 100644 index 0000000000..7f4ec6469e --- /dev/null +++ b/services/workflows-service/src/webhooks/dtos/individual-aml-webhook-input.ts @@ -0,0 +1,53 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class IndividualAmlWebhookInput { + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + id!: string; + + @ApiProperty({ + required: true, + type: Number, + }) + @IsString() + apiVersion!: number; + + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + timestamp!: string; + + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + eventName!: string; + + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + @IsOptional() + entityId!: string; + + @ApiProperty({ + required: false, + type: String, + }) + @IsString() + @IsOptional() + environment?: string; + + @ApiProperty({ + required: true, + }) + data!: unknown; +} diff --git a/services/workflows-service/src/webhooks/webhooks.controller.ts b/services/workflows-service/src/webhooks/webhooks.controller.ts new file mode 100644 index 0000000000..0852928091 --- /dev/null +++ b/services/workflows-service/src/webhooks/webhooks.controller.ts @@ -0,0 +1,55 @@ +import * as common from '@nestjs/common'; +import * as swagger from '@nestjs/swagger'; +import * as nestAccessControl from 'nest-access-control'; +import * as errors from '../errors'; +import { Public } from '@/common/decorators/public.decorator'; +import { AmlWebhookInput } from './dtos/aml-webhook-input'; +import { IndividualAmlWebhookInput } from '@/webhooks/dtos/individual-aml-webhook-input'; +import { WebhooksService } from '@/webhooks/webhooks.service'; + +const WEBHOOKS = { + AML_HIT: 'AML_HIT', +} as const; + +const ENTITY_TYPES = { + BUSINESS: 'business', + INDIVIDUALS: 'individuals', +} as const; + +@swagger.ApiBearerAuth() +@swagger.ApiTags('Webhooks') +@common.Controller('webhooks') +export class WebhooksController { + constructor( + @nestAccessControl.InjectRolesBuilder() + protected readonly rolesBuilder: nestAccessControl.RolesBuilder, + private readonly webhooksService: WebhooksService, + ) {} + + @common.Post('/:entityType/aml') + @swagger.ApiOkResponse() + @common.HttpCode(200) + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) + @Public() + // @VerifyUnifiedApiSignatureDecorator() + async amlHook( + @common.Param() { entityType }: AmlWebhookInput, + @common.Body() data: IndividualAmlWebhookInput, + ) { + try { + if (entityType === ENTITY_TYPES.INDIVIDUALS) { + const { eventName } = data; + + if (eventName === WEBHOOKS.AML_HIT) { + await this.webhooksService.handleAmlHit(data as IndividualAmlWebhookInput); + } + } + } catch (error) { + console.error(error); + + throw error; + } + + return; + } +} diff --git a/services/workflows-service/src/webhooks/webhooks.module.ts b/services/workflows-service/src/webhooks/webhooks.module.ts new file mode 100644 index 0000000000..3a0653b566 --- /dev/null +++ b/services/workflows-service/src/webhooks/webhooks.module.ts @@ -0,0 +1,70 @@ +import { AuthModule } from '@/auth/auth.module'; +import { ACLModule } from '@/common/access-control/acl.module'; +import { CustomerModule } from '@/customer/customer.module'; +import { PrismaModule } from '@/prisma/prisma.module'; +import { ProjectModule } from '@/project/project.module'; +import { WorkflowDefinitionModule } from '@/workflow-defintion/workflow-definition.module'; +import { HttpModule } from '@nestjs/axios'; +import { Module, forwardRef } from '@nestjs/common'; +import { WorkflowService } from '@/workflow/workflow.service'; +import { WorkflowRuntimeDataRepository } from '@/workflow/workflow-runtime-data.repository'; +import { EndUserRepository } from '@/end-user/end-user.repository'; +import { EndUserService } from '@/end-user/end-user.service'; +import { BusinessRepository } from '@/business/business.repository'; +import { EntityRepository } from '@/common/entity/entity.repository'; +import { FileService } from '@/providers/file/file.service'; +import { WorkflowEventEmitterService } from '@/workflow/workflow-event-emitter.service'; +import { UserService } from '@/user/user.service'; +import { SalesforceService } from '@/salesforce/salesforce.service'; +import { WorkflowTokenService } from '@/auth/workflow-token/workflow-token.service'; +import { UiDefinitionService } from '@/ui-definition/ui-definition.service'; +import { StorageService } from '@/storage/storage.service'; +import { UserRepository } from '@/user/user.repository'; +import { SalesforceIntegrationRepository } from '@/salesforce/salesforce-integration.repository'; +import { WorkflowTokenRepository } from '@/auth/workflow-token/workflow-token.repository'; +import { UiDefinitionRepository } from '@/ui-definition/ui-definition.repository'; +import { FileRepository } from '@/storage/storage.repository'; +import { HookCallbackHandlerService } from '@/workflow/hook-callback-handler.service'; +import { FilterService } from '@/filter/filter.service'; +import { FilterRepository } from '@/filter/filter.repository'; +import { WebhooksController } from '@/webhooks/webhooks.controller'; +import { WebhooksService } from '@/webhooks/webhooks.service'; + +@Module({ + controllers: [WebhooksController], + imports: [ + ACLModule, + forwardRef(() => AuthModule), + HttpModule, + ProjectModule, + PrismaModule, + CustomerModule, + WorkflowDefinitionModule, + ], + providers: [ + WorkflowService, + WorkflowRuntimeDataRepository, + EndUserService, + EndUserRepository, + BusinessRepository, + EntityRepository, + FileService, + FileRepository, + WorkflowEventEmitterService, + UserService, + UserRepository, + SalesforceService, + SalesforceIntegrationRepository, + WorkflowTokenService, + WorkflowTokenRepository, + UiDefinitionService, + UiDefinitionRepository, + StorageService, + HookCallbackHandlerService, + FilterService, + FilterRepository, + WebhooksService, + ], + exports: [], +}) +export class WebhooksModule {} diff --git a/services/workflows-service/src/webhooks/webhooks.service.ts b/services/workflows-service/src/webhooks/webhooks.service.ts new file mode 100644 index 0000000000..dd35f12c19 --- /dev/null +++ b/services/workflows-service/src/webhooks/webhooks.service.ts @@ -0,0 +1,50 @@ +import { Injectable } from '@nestjs/common'; +import { IndividualAmlWebhookInput } from '@/webhooks/dtos/individual-aml-webhook-input'; +import { EndUserRepository } from '@/end-user/end-user.repository'; +import { CustomerService } from '@/customer/customer.service'; +import { WorkflowDefinitionService } from '@/workflow-defintion/workflow-definition.service'; +import { WorkflowService } from '@/workflow/workflow.service'; + +@Injectable() +export class WebhooksService { + constructor( + private readonly customerService: CustomerService, + private readonly workflowService: WorkflowService, + private readonly endUserRepository: EndUserRepository, + private readonly workflowDefinitionService: WorkflowDefinitionService, + ) {} + + async handleAmlHit({ entityId }: IndividualAmlWebhookInput) { + const { projectId } = await this.endUserRepository.findByIdUnscoped(entityId, { + select: { + projectId: true, + }, + }); + + const { config } = await this.customerService.getByProjectId(projectId, { + select: { config: true }, + }); + + if (!config?.ongoingWorkflowDefinitionId) { + return; + } + + const { id: workflowDefinitionId } = await this.workflowDefinitionService.getLatestVersion( + config.ongoingWorkflowDefinitionId, + [projectId], + ); + + await this.workflowService.createOrUpdateWorkflowRuntime({ + workflowDefinitionId, + context: { + entity: { + id: entityId, + type: 'individual', + }, + documents: [], + }, + projectIds: [projectId], + currentProjectId: projectId, + }); + } +} diff --git a/services/workflows-service/src/workflow-defintion/workflow-definition.service.ts b/services/workflows-service/src/workflow-defintion/workflow-definition.service.ts index 22dbfe1da5..e573398960 100644 --- a/services/workflows-service/src/workflow-defintion/workflow-definition.service.ts +++ b/services/workflows-service/src/workflow-defintion/workflow-definition.service.ts @@ -80,12 +80,8 @@ export class WorkflowDefinitionService { async getLatestVersion(id: string, projectIds: TProjectIds) { const workflowDefinition = await this.repository.findById(id, {}, projectIds); - const latestVersion = await this.repository.findByLatestVersion( - workflowDefinition.name, - projectIds, - ); - return latestVersion; + return await this.repository.findByLatestVersion(workflowDefinition.name, projectIds); } async getLatestDefinitionWithTransitionSchema( diff --git a/services/workflows-service/src/workflow/workflow.controller.external.ts b/services/workflows-service/src/workflow/workflow.controller.external.ts index 46c96f5d6f..fe4e401cdb 100644 --- a/services/workflows-service/src/workflow/workflow.controller.external.ts +++ b/services/workflows-service/src/workflow/workflow.controller.external.ts @@ -174,6 +174,7 @@ export class WorkflowControllerExternal { const hasSalesforceRecord = Boolean(body.salesforceObjectName) && Boolean(body.salesforceRecordId); + const latestDefinitionVersion = await this.workflowDefinitionService.getLatestVersion( workflowId, projectIds, diff --git a/services/workflows-service/src/workflow/workflow.service.ts b/services/workflows-service/src/workflow/workflow.service.ts index 633c683c57..4872014b21 100644 --- a/services/workflows-service/src/workflow/workflow.service.ts +++ b/services/workflows-service/src/workflow/workflow.service.ts @@ -85,27 +85,8 @@ import { ajv } from '@/common/ajv/ajv.validator'; type TEntityId = string; -export const ResubmissionReason = { - BLURRY_IMAGE: 'BLURRY_IMAGE', - CUT_IMAGE: 'CUT_IMAGE', - UNSUPPORTED_DOCUMENT: 'UNSUPPORTED_DOCUMENT', - DAMAGED_DOCUMENT: 'DAMAGED_DOCUMENT', - EXPIRED_DOCUMENT: 'EXPIRED_DOCUMENT', - COPY_OF_A_COPY: 'COPY_OF_A_COPY', - FACE_IS_UNCLEAR: 'FACE_IS_UNCLEAR', - FACE_IS_NOT_MATCHING: 'FACE_IS_NOT_MATCHING', -} as const; - -export interface WorkflowData { - workflowDefinition: object; - workflowRuntimeData: object; -} - export type TEntityType = 'endUser' | 'business'; -// Discuss model classes location -export type IntentResponse = WorkflowData[]; - // TODO: TEMP (STUB) const policies = { kycSignup: () => { From 540f796486894e719dd22df9e7a4050b5db314af Mon Sep 17 00:00:00 2001 From: Omri Levy Date: Wed, 28 Feb 2024 14:22:46 +0200 Subject: [PATCH 03/20] refactor(backoffice-v2): moved aml cells to a re-usable hook --- .../hooks/useAmlBlock/useAmlBlock.tsx | 350 +++++++++++++++ .../components/AmlBlock/utils/aml-adapter.ts | 81 ++++ .../hooks/useKycBlock/useKycBlock.tsx | 425 +----------------- 3 files changed, 440 insertions(+), 416 deletions(-) create mode 100644 apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx create mode 100644 apps/backoffice-v2/src/lib/blocks/components/AmlBlock/utils/aml-adapter.ts diff --git a/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx b/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx new file mode 100644 index 0000000000..2a5b9a6a23 --- /dev/null +++ b/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx @@ -0,0 +1,350 @@ +import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; +import * as React from 'react'; +import { ComponentProps, useMemo } from 'react'; +import { Badge } from '@ballerine/ui'; +import { WarningFilledSvg } from '@/common/components/atoms/icons'; +import { buttonVariants } from '@/common/components/atoms/Button/Button'; +import { amlAdapter } from '@/lib/blocks/components/AmlBlock/utils/aml-adapter'; + +export const useAmlBlock = ({ + sessionKeys, + getAmlData, +}: { + sessionKeys: string[]; + getAmlData: (key: string) => any; +}) => { + return useMemo(() => { + if (!sessionKeys?.length) { + return []; + } + + return sessionKeys?.flatMap(key => { + const aml = getAmlData(key); + + if (!Object.keys(aml ?? {}).length) return []; + + const { totalMatches, fullReport, dateOfCheck, matches } = amlAdapter(aml); + + return [ + ...createBlocksTyped() + .addBlock() + .addCell({ + type: 'table', + value: { + props: { + table: { + className: 'my-8', + }, + }, + columns: [ + { + accessorKey: 'totalMatches', + header: 'Total Matches', + cell: props => { + const value = props.getValue(); + const variant: ComponentProps['variant'] = + value === 0 ? 'success' : 'warning'; + + return ( + + {value} {value === 1 ? 'match' : 'matches'} + + ); + }, + }, + { + accessorKey: 'fullReport', + header: 'Full Report', + }, + { + accessorKey: 'dateOfCheck', + header: 'Date Of Check', + }, + { + accessorKey: 'scanStatus', + header: 'Scan Status', + cell: props => { + const value = props.getValue(); + const variant: ComponentProps['variant'] = 'success'; + + return ( + + <>{value} + + ); + }, + }, + ], + data: [ + { + totalMatches, + fullReport, + dateOfCheck, + scanStatus: 'Completed', + }, + ], + }, + }) + .addCell({ + type: 'table', + value: { + props: { + table: { + className: 'my-8', + }, + }, + columns: [ + { + accessorKey: 'matchedName', + header: 'Matched Name', + }, + { + accessorKey: 'dateOfBirth', + header: 'Date Of Birth', + }, + { + accessorKey: 'countries', + header: 'Countries', + }, + { + accessorKey: 'aka', + header: 'AKA', + }, + ], + data: matches, + }, + }) + .build() + .flat(1), + ...(matches?.flatMap(({ warnings, sanctions, pep, adverseMedia }, index) => + createBlocksTyped() + .addBlock() + .addCell({ + type: 'container', + value: createBlocksTyped() + .addBlock() + .addCell({ + type: 'subheading', + value: `Match ${index + 1}`, + props: { + className: 'text-lg block my-6', + }, + }) + .addCell({ + type: 'table', + value: { + props: { + table: { + className: 'my-8 w-full', + }, + }, + columns: [ + { + accessorKey: 'warning', + header: 'Warning', + cell: props => { + const value = props.getValue(); + + return ( +
+ + {value} +
+ ); + }, + }, + { + accessorKey: 'date', + header: 'Date', + }, + { + accessorKey: 'source', + header: 'Source URL', + cell: props => { + const value = props.getValue(); + + return ( + + Link + + ); + }, + }, + ], + data: warnings, + }, + }) + .addCell({ + type: 'table', + props: { + table: { + className: 'my-8 w-full', + }, + }, + value: { + columns: [ + { + accessorKey: 'sanction', + header: 'Sanction', + cell: props => { + const value = props.getValue(); + + return ( +
+ + {value} +
+ ); + }, + }, + { + accessorKey: 'date', + header: 'Date', + }, + { + accessorKey: 'source', + header: 'Source URL', + cell: props => { + const value = props.getValue(); + + return ( + + Link + + ); + }, + }, + ], + data: sanctions, + }, + }) + .addCell({ + type: 'table', + value: { + props: { + table: { + className: 'my-8 w-full', + }, + }, + columns: [ + { + accessorKey: 'person', + header: 'PEP (Politically Exposed Person)', + cell: props => { + const value = props.getValue(); + + return ( +
+ + {value} +
+ ); + }, + }, + { + accessorKey: 'date', + header: 'Date', + }, + { + accessorKey: 'source', + header: 'Source URL', + cell: props => { + const value = props.getValue(); + + return ( + + Link + + ); + }, + }, + ], + data: pep, + }, + }) + .addCell({ + type: 'table', + value: { + props: { + table: { + className: 'my-8', + }, + }, + columns: [ + { + accessorKey: 'entry', + header: 'Adverse Media', + cell: props => { + const value = props.getValue(); + + return ( +
+ + {value} +
+ ); + }, + }, + { + accessorKey: 'date', + header: 'Date', + }, + { + accessorKey: 'source', + header: 'Source URL', + cell: props => { + const value = props.getValue(); + + return ( + + Link + + ); + }, + }, + ], + data: adverseMedia, + }, + }) + .build() + .flat(1), + }) + .build() + .flat(1), + ) ?? []), + ]; + }); + }, [getAmlData, sessionKeys]); +}; diff --git a/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/utils/aml-adapter.ts b/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/utils/aml-adapter.ts new file mode 100644 index 0000000000..b9c7428a5f --- /dev/null +++ b/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/utils/aml-adapter.ts @@ -0,0 +1,81 @@ +interface IAmlAdapterParams { + hits: Array<{ + matchedName: string; + dateOfBirth: string; + countries: string[]; + matchTypes: string[]; + aka: string[]; + listingsRelatedToMatch: { + warnings: Array<{ + sourceName: string; + sourceUrl: string; + date: string; + }>; + sanctions: Array<{ + sourceName: string; + sourceUrl: string; + date: string; + }>; + pep: Array<{ + sourceName: string; + sourceUrl: string; + date: string; + }>; + adverseMedia: Array<{ + sourceName: string; + sourceUrl: string; + date: string; + }>; + }; + }>; + createdAt: string; + totalHits: number; +} + +export const amlAdapter = (aml: IAmlAdapterParams) => { + const { hits, totalHits, createdAt, ...rest } = aml; + + return { + totalMatches: totalHits ?? 0, + fullReport: rest, + dateOfCheck: createdAt, + matches: + hits?.map( + ({ matchedName, dateOfBirth, countries, matchTypes, aka, listingsRelatedToMatch }) => { + const { sanctions, warnings, pep, adverseMedia } = listingsRelatedToMatch ?? {}; + + return { + matchedName, + dateOfBirth, + countries: countries?.join(', ') ?? '', + matchTypes: matchTypes?.join(', ') ?? '', + aka: aka?.join(', ') ?? '', + sanctions: + sanctions?.map(sanction => ({ + sanction: sanction?.sourceName, + date: sanction?.date, + source: sanction?.sourceUrl, + })) ?? [], + warnings: + warnings?.map(warning => ({ + warning: warning?.sourceName, + date: warning?.date, + source: warning?.sourceUrl, + })) ?? [], + pep: + pep?.map(pepItem => ({ + person: pepItem?.sourceName, + date: pepItem?.date, + source: pepItem?.sourceUrl, + })) ?? [], + adverseMedia: + adverseMedia?.map(adverseMediaItem => ({ + entry: adverseMediaItem?.sourceName, + date: adverseMediaItem?.date, + source: adverseMediaItem?.sourceUrl, + })) ?? [], + }; + }, + ) ?? [], + }; +}; diff --git a/apps/backoffice-v2/src/lib/blocks/components/KycBlock/hooks/useKycBlock/useKycBlock.tsx b/apps/backoffice-v2/src/lib/blocks/components/KycBlock/hooks/useKycBlock/useKycBlock.tsx index 541ff4f87a..9507b5cb57 100644 --- a/apps/backoffice-v2/src/lib/blocks/components/KycBlock/hooks/useKycBlock/useKycBlock.tsx +++ b/apps/backoffice-v2/src/lib/blocks/components/KycBlock/hooks/useKycBlock/useKycBlock.tsx @@ -18,11 +18,10 @@ import { useFilterId } from '@/common/hooks/useFilterId/useFilterId'; import { useCaseDecision } from '@/pages/Entity/components/Case/hooks/useCaseDecision/useCaseDecision'; import { ctw } from '@/common/utils/ctw/ctw'; import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; -import { Badge, Button } from '@ballerine/ui'; -import { WarningFilledSvg } from '@/common/components/atoms/icons'; -import { buttonVariants } from '@/common/components/atoms/Button/Button'; +import { Button } from '@ballerine/ui'; import { MotionButton } from '@/common/components/molecules/MotionButton/MotionButton'; import { motionButtonProps } from '@/lib/blocks/hooks/useAssosciatedCompaniesBlock/useAssociatedCompaniesBlock'; +import { useAmlBlock } from '@/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock'; const motionBadgeProps = { exit: { opacity: 0, transition: { duration: 0.2 } }, @@ -99,424 +98,18 @@ export const useKycBlock = ({ ]) ?? [] : []; - const amlAdapter = (aml: { - hits: Array<{ - matchedName: string; - dateOfBirth: string; - countries: string[]; - matchTypes: string[]; - aka: string[]; - listingsRelatedToMatch: { - warnings: Array<{ - sourceName: string; - sourceUrl: string; - date: string; - }>; - sanctions: Array<{ - sourceName: string; - sourceUrl: string; - date: string; - }>; - pep: Array<{ - sourceName: string; - sourceUrl: string; - date: string; - }>; - adverseMedia: Array<{ - sourceName: string; - sourceUrl: string; - date: string; - }>; - }; - }>; - createdAt: string; - totalHits: number; - }) => { - const { hits, totalHits, createdAt, ...rest } = aml; - - return { - totalMatches: totalHits ?? 0, - fullReport: rest, - dateOfCheck: createdAt, - matches: - hits?.map( - ({ matchedName, dateOfBirth, countries, matchTypes, aka, listingsRelatedToMatch }) => { - const { sanctions, warnings, pep, adverseMedia } = listingsRelatedToMatch ?? {}; - - return { - matchedName, - dateOfBirth, - countries: countries?.join(', ') ?? '', - matchTypes: matchTypes?.join(', ') ?? '', - aka: aka?.join(', ') ?? '', - sanctions: - sanctions?.map(sanction => ({ - sanction: sanction?.sourceName, - date: sanction?.date, - source: sanction?.sourceUrl, - })) ?? [], - warnings: - warnings?.map(warning => ({ - warning: warning?.sourceName, - date: warning?.date, - source: warning?.sourceUrl, - })) ?? [], - pep: - pep?.map(pepItem => ({ - person: pepItem?.sourceName, - date: pepItem?.date, - source: pepItem?.sourceUrl, - })) ?? [], - adverseMedia: - adverseMedia?.map(adverseMediaItem => ({ - entry: adverseMediaItem?.sourceName, - date: adverseMediaItem?.date, - source: adverseMediaItem?.sourceUrl, - })) ?? [], - }; - }, - ) ?? [], - }; - }; - const hasAml = kycSessionKeys?.some(key => { return ( !!childWorkflow?.context?.pluginsOutput?.kyc_session[key]?.result?.vendorResult?.aml || !!childWorkflow?.context?.pluginsOutput?.kyc_session[key]?.result?.aml ); }); - const complianceCheckResults = kycSessionKeys?.length - ? kycSessionKeys?.flatMap(key => { - const aml = - childWorkflow?.context?.pluginsOutput?.kyc_session[key]?.result?.vendorResult?.aml ?? - childWorkflow?.context?.pluginsOutput?.kyc_session[key]?.result?.aml; - - if (!Object.keys(aml ?? {}).length) return []; - - const { totalMatches, fullReport, dateOfCheck, matches } = amlAdapter(aml); - - return [ - ...createBlocksTyped() - .addBlock() - .addCell({ - type: 'table', - value: { - props: { - table: { - className: 'my-8', - }, - }, - columns: [ - { - accessorKey: 'totalMatches', - header: 'Total Matches', - cell: props => { - const value = props.getValue(); - const variant: ComponentProps['variant'] = - value === 0 ? 'success' : 'warning'; - - return ( - - {value} {value === 1 ? 'match' : 'matches'} - - ); - }, - }, - { - accessorKey: 'fullReport', - header: 'Full Report', - }, - { - accessorKey: 'dateOfCheck', - header: 'Date Of Check', - }, - { - accessorKey: 'scanStatus', - header: 'Scan Status', - cell: props => { - const value = props.getValue(); - const variant: ComponentProps['variant'] = 'success'; - - return ( - - <>{value} - - ); - }, - }, - ], - data: [ - { - totalMatches, - fullReport, - dateOfCheck, - scanStatus: 'Completed', - }, - ], - }, - }) - .addCell({ - type: 'table', - value: { - props: { - table: { - className: 'my-8', - }, - }, - columns: [ - { - accessorKey: 'matchedName', - header: 'Matched Name', - }, - { - accessorKey: 'dateOfBirth', - header: 'Date Of Birth', - }, - { - accessorKey: 'countries', - header: 'Countries', - }, - { - accessorKey: 'aka', - header: 'AKA', - }, - ], - data: matches, - }, - }) - .build() - .flat(1), - ...(matches?.flatMap(({ warnings, sanctions, pep, adverseMedia }, index) => - createBlocksTyped() - .addBlock() - .addCell({ - type: 'container', - value: createBlocksTyped() - .addBlock() - .addCell({ - type: 'subheading', - value: `Match ${index + 1}`, - props: { - className: 'text-lg block my-6', - }, - }) - .addCell({ - type: 'table', - value: { - props: { - table: { - className: 'my-8 w-full', - }, - }, - columns: [ - { - accessorKey: 'warning', - header: 'Warning', - cell: props => { - const value = props.getValue(); - - return ( -
- - {value} -
- ); - }, - }, - { - accessorKey: 'date', - header: 'Date', - }, - { - accessorKey: 'source', - header: 'Source URL', - cell: props => { - const value = props.getValue(); - - return ( - - Link - - ); - }, - }, - ], - data: warnings, - }, - }) - .addCell({ - type: 'table', - props: { - table: { - className: 'my-8 w-full', - }, - }, - value: { - columns: [ - { - accessorKey: 'sanction', - header: 'Sanction', - cell: props => { - const value = props.getValue(); - - return ( -
- - {value} -
- ); - }, - }, - { - accessorKey: 'date', - header: 'Date', - }, - { - accessorKey: 'source', - header: 'Source URL', - cell: props => { - const value = props.getValue(); - - return ( - - Link - - ); - }, - }, - ], - data: sanctions, - }, - }) - .addCell({ - type: 'table', - value: { - props: { - table: { - className: 'my-8 w-full', - }, - }, - columns: [ - { - accessorKey: 'person', - header: 'PEP (Politically Exposed Person)', - cell: props => { - const value = props.getValue(); - - return ( -
- - {value} -
- ); - }, - }, - { - accessorKey: 'date', - header: 'Date', - }, - { - accessorKey: 'source', - header: 'Source URL', - cell: props => { - const value = props.getValue(); - - return ( - - Link - - ); - }, - }, - ], - data: pep, - }, - }) - .addCell({ - type: 'table', - value: { - props: { - table: { - className: 'my-8', - }, - }, - columns: [ - { - accessorKey: 'entry', - header: 'Adverse Media', - cell: props => { - const value = props.getValue(); - - return ( -
- - {value} -
- ); - }, - }, - { - accessorKey: 'date', - header: 'Date', - }, - { - accessorKey: 'source', - header: 'Source URL', - cell: props => { - const value = props.getValue(); - - return ( - - Link - - ); - }, - }, - ], - data: adverseMedia, - }, - }) - .build() - .flat(1), - }) - .build() - .flat(1), - ) ?? []), - ]; - }) - : []; + const amlBlock = useAmlBlock({ + sessionKeys: kycSessionKeys, + getAmlData: key => + childWorkflow?.context?.pluginsOutput?.kyc_session[key]?.result?.vendorResult?.aml ?? + childWorkflow?.context?.pluginsOutput?.kyc_session[key]?.result?.aml, + }); const documentExtractedData = kycSessionKeys?.length ? kycSessionKeys?.map((key, index, collection) => @@ -836,7 +429,7 @@ export const useKycBlock = ({ value: 'Compliance Check Results', }) .build() - .concat(complianceCheckResults) + .concat(amlBlock) .flat(1), }) .build() From 58820fd05218a5aeed0bf23098dbba5505f4feb0 Mon Sep 17 00:00:00 2001 From: Omri Levy Date: Wed, 28 Feb 2024 16:54:19 +0200 Subject: [PATCH 04/20] refactor(backoffice-v2): added an on-going blocks variant --- .../src/domains/workflows/fetchers.ts | 51 +++++++++++++++++++ .../hooks/useAmlBlock/useAmlBlock.tsx | 33 ++++++++++-- .../hooks/useKycBlock/useKycBlock.tsx | 29 +++-------- .../blocks/components/NoBlocks/NoBlocks.tsx | 2 +- .../variants/BlocksVariant/BlocksVariant.tsx | 7 +++ .../variants/OnGoingBlocks/OnGoingBlocks.tsx | 17 +++++++ .../useOnGoingBlocksLogic.tsx | 47 +++++++++++++++++ .../lib/blocks/variants/variant-checkers.ts | 6 +++ packages/common/src/consts/index.ts | 1 + 9 files changed, 168 insertions(+), 25 deletions(-) create mode 100644 apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/OnGoingBlocks.tsx create mode 100644 apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/hooks/useOnGoingBlocksLogic/useOnGoingBlocksLogic.tsx diff --git a/apps/backoffice-v2/src/domains/workflows/fetchers.ts b/apps/backoffice-v2/src/domains/workflows/fetchers.ts index fb3299c675..7b8003926f 100644 --- a/apps/backoffice-v2/src/domains/workflows/fetchers.ts +++ b/apps/backoffice-v2/src/domains/workflows/fetchers.ts @@ -131,6 +131,10 @@ export const fetchWorkflowById = async ({ method: Method.GET, schema: WorkflowByIdSchema.transform(data => ({ ...data, + workflowDefinition: { + ...data.workflowDefinition, + variant: 'ONGOING', + }, context: { ...data.context, pluginsOutput: { @@ -141,6 +145,53 @@ export const fetchWorkflowById = async ({ ...data.context?.pluginsOutput?.website_monitoring, data: deepCamelKeys(data.context?.pluginsOutput?.website_monitoring?.data ?? {}), }, + kyc_session: { + kyc_session_1: { + aml: { + hits: [ + { + matchedName: 'John Doe', + dateOfBirth: '1985-04-12', + countries: ['USA'], + matchTypes: ['sanction', 'pep'], + aka: ['J. Doe', 'Johnny D.'], + listingsRelatedToMatch: { + warnings: [ + { + sourceName: 'Global Warning List', + sourceUrl: 'https://globalwarninglist.com/warning/johndoe', + date: '2022-05-01', + }, + ], + sanctions: [ + { + sourceName: 'United Nations Sanctions', + sourceUrl: 'https://un.org/sanctions/johndoe', + date: '2022-06-15', + }, + ], + pep: [ + { + sourceName: 'Political Exposed Persons List', + sourceUrl: 'https://peplist.com/politicians/johndoe', + date: '2022-07-20', + }, + ], + adverseMedia: [ + { + sourceName: 'AdverseMedia News', + sourceUrl: 'https://adversemedianews.com/articles/johndoe', + date: '2022-08-05', + }, + ], + }, + }, + ], + createdAt: '2024-02-28T12:00:00Z', + totalHits: 1, + }, + }, + }, }, }, })), diff --git a/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx b/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx index 2a5b9a6a23..29bab3844c 100644 --- a/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx +++ b/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx @@ -5,16 +5,28 @@ import { Badge } from '@ballerine/ui'; import { WarningFilledSvg } from '@/common/components/atoms/icons'; import { buttonVariants } from '@/common/components/atoms/Button/Button'; import { amlAdapter } from '@/lib/blocks/components/AmlBlock/utils/aml-adapter'; +import { safeEvery } from '@ballerine/common'; export const useAmlBlock = ({ sessionKeys, getAmlData, }: { sessionKeys: string[]; - getAmlData: (key: string) => any; + getAmlData: (key: string) => Parameters[0]; }) => { - return useMemo(() => { + const isAmlEmpty = useMemo(() => { if (!sessionKeys?.length) { + return true; + } + + return safeEvery(sessionKeys, key => { + const aml = getAmlData(key); + + return !aml; + }); + }, [getAmlData, sessionKeys]); + const amlBlock = useMemo(() => { + if (isAmlEmpty) { return []; } @@ -346,5 +358,20 @@ export const useAmlBlock = ({ ) ?? []), ]; }); - }, [getAmlData, sessionKeys]); + }, [getAmlData, isAmlEmpty, sessionKeys]); + + if (isAmlEmpty) { + return []; + } + + return createBlocksTyped() + .addBlock() + .addCell({ + id: 'header', + type: 'heading', + value: 'Compliance Check Results', + }) + .build() + .concat(amlBlock) + .flat(1); }; diff --git a/apps/backoffice-v2/src/lib/blocks/components/KycBlock/hooks/useKycBlock/useKycBlock.tsx b/apps/backoffice-v2/src/lib/blocks/components/KycBlock/hooks/useKycBlock/useKycBlock.tsx index 9507b5cb57..f82dee7623 100644 --- a/apps/backoffice-v2/src/lib/blocks/components/KycBlock/hooks/useKycBlock/useKycBlock.tsx +++ b/apps/backoffice-v2/src/lib/blocks/components/KycBlock/hooks/useKycBlock/useKycBlock.tsx @@ -98,17 +98,15 @@ export const useKycBlock = ({ ]) ?? [] : []; - const hasAml = kycSessionKeys?.some(key => { - return ( - !!childWorkflow?.context?.pluginsOutput?.kyc_session[key]?.result?.vendorResult?.aml || - !!childWorkflow?.context?.pluginsOutput?.kyc_session[key]?.result?.aml - ); - }); - const amlBlock = useAmlBlock({ - sessionKeys: kycSessionKeys, - getAmlData: key => + const getAmlData = useCallback( + (key: string) => childWorkflow?.context?.pluginsOutput?.kyc_session[key]?.result?.vendorResult?.aml ?? childWorkflow?.context?.pluginsOutput?.kyc_session[key]?.result?.aml, + [childWorkflow?.context?.pluginsOutput?.kyc_session], + ); + const amlBlock = useAmlBlock({ + sessionKeys: kycSessionKeys, + getAmlData, }); const documentExtractedData = kycSessionKeys?.length @@ -419,18 +417,7 @@ export const useKycBlock = ({ }) .addCell({ type: 'container', - value: !hasAml - ? [] - : createBlocksTyped() - .addBlock() - .addCell({ - id: 'header', - type: 'heading', - value: 'Compliance Check Results', - }) - .build() - .concat(amlBlock) - .flat(1), + value: amlBlock, }) .build() .flat(1), diff --git a/apps/backoffice-v2/src/lib/blocks/components/NoBlocks/NoBlocks.tsx b/apps/backoffice-v2/src/lib/blocks/components/NoBlocks/NoBlocks.tsx index d7db19a181..41d44f24d1 100644 --- a/apps/backoffice-v2/src/lib/blocks/components/NoBlocks/NoBlocks.tsx +++ b/apps/backoffice-v2/src/lib/blocks/components/NoBlocks/NoBlocks.tsx @@ -2,7 +2,7 @@ import { NoTasksSvg } from '@/common/components/atoms/icons'; export const NoBlocks = () => { return ( -
+
diff --git a/apps/backoffice-v2/src/lib/blocks/variants/BlocksVariant/BlocksVariant.tsx b/apps/backoffice-v2/src/lib/blocks/variants/BlocksVariant/BlocksVariant.tsx index 5ab811339b..e51e1a27b9 100644 --- a/apps/backoffice-v2/src/lib/blocks/variants/BlocksVariant/BlocksVariant.tsx +++ b/apps/backoffice-v2/src/lib/blocks/variants/BlocksVariant/BlocksVariant.tsx @@ -6,13 +6,16 @@ import { TWorkflowById } from '@/domains/workflows/fetchers'; import { checkIsKybExampleVariant, checkIsManualReviewVariant, + checkIsOnGoingVariant, } from '@/lib/blocks/variants/variant-checkers'; +import { OnGoingBlocks } from '@/lib/blocks/variants/OnGoingBlocks/OnGoingBlocks'; export const BlocksVariant: FunctionComponent<{ workflowDefinition: Pick; }> = ({ workflowDefinition }) => { const isKybExampleVariant = checkIsKybExampleVariant(workflowDefinition); const isManualReviewVariant = checkIsManualReviewVariant(workflowDefinition); + const isOnGoingVariant = checkIsOnGoingVariant(workflowDefinition); if (isKybExampleVariant) { return ; @@ -22,5 +25,9 @@ export const BlocksVariant: FunctionComponent<{ return ; } + if (isOnGoingVariant) { + return ; + } + return ; }; diff --git a/apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/OnGoingBlocks.tsx b/apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/OnGoingBlocks.tsx new file mode 100644 index 0000000000..a12f87fd01 --- /dev/null +++ b/apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/OnGoingBlocks.tsx @@ -0,0 +1,17 @@ +import { BlocksComponent } from '@ballerine/blocks'; +import { NoBlocks } from '@/lib/blocks/components/NoBlocks/NoBlocks'; +import { cells } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; +import { useOnGoingBlocksLogic } from '@/lib/blocks/variants/OnGoingBlocks/hooks/useOnGoingBlocksLogic/useOnGoingBlocksLogic'; + +export const OnGoingBlocks = () => { + const { blocks, isLoading } = useOnGoingBlocksLogic(); + + return ( + <> + + {(Cell, cell) => } + + {!isLoading && !blocks?.length && } + + ); +}; diff --git a/apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/hooks/useOnGoingBlocksLogic/useOnGoingBlocksLogic.tsx b/apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/hooks/useOnGoingBlocksLogic/useOnGoingBlocksLogic.tsx new file mode 100644 index 0000000000..b447f96367 --- /dev/null +++ b/apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/hooks/useOnGoingBlocksLogic/useOnGoingBlocksLogic.tsx @@ -0,0 +1,47 @@ +import { useParams } from 'react-router-dom'; +import { useFilterId } from '@/common/hooks/useFilterId/useFilterId'; +import { useWorkflowByIdQuery } from '@/domains/workflows/hooks/queries/useWorkflowByIdQuery/useWorkflowByIdQuery'; +import { useCallback, useMemo } from 'react'; +import { useAmlBlock } from '@/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock'; +import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; + +export const useOnGoingBlocksLogic = () => { + const { entityId: workflowId } = useParams(); + const filterId = useFilterId(); + const { data: workflow, isLoading } = useWorkflowByIdQuery({ + workflowId: workflowId ?? '', + filterId: filterId ?? '', + }); + const kycSessionKeys = Object.keys(workflow?.context?.pluginsOutput?.kyc_session ?? {}); + const getAmlData = useCallback( + (key: string) => + workflow?.context?.pluginsOutput?.kyc_session[key]?.result?.vendorResult?.aml ?? + workflow?.context?.pluginsOutput?.kyc_session[key]?.result?.aml, + [workflow?.context?.pluginsOutput?.kyc_session], + ); + const amlBlock = useAmlBlock({ + sessionKeys: kycSessionKeys, + getAmlData, + }); + const amlWithContainerBlock = useMemo(() => { + if (!amlBlock?.length) { + return []; + } + + return createBlocksTyped() + .addBlock() + .addCell({ + type: 'block', + value: amlBlock, + }) + .build(); + }, [amlBlock]); + const blocks = useMemo(() => { + return [...amlWithContainerBlock]; + }, [amlWithContainerBlock]); + + return { + blocks, + isLoading, + }; +}; diff --git a/apps/backoffice-v2/src/lib/blocks/variants/variant-checkers.ts b/apps/backoffice-v2/src/lib/blocks/variants/variant-checkers.ts index 98d3d0eb9b..68292d8aed 100644 --- a/apps/backoffice-v2/src/lib/blocks/variants/variant-checkers.ts +++ b/apps/backoffice-v2/src/lib/blocks/variants/variant-checkers.ts @@ -14,3 +14,9 @@ export const checkIsManualReviewVariant = ( workflowDefinition?.version >= 0 && workflowDefinition?.variant === WorkflowDefinitionVariant.MANUAL_REVIEW && workflowDefinition?.config?.isLegacyReject; + +export const checkIsOnGoingVariant = ( + workflowDefinition: Pick, +) => + workflowDefinition?.version >= 0 && + workflowDefinition?.variant === WorkflowDefinitionVariant.ONGOING; diff --git a/packages/common/src/consts/index.ts b/packages/common/src/consts/index.ts index 8fcbd6a1c3..2ea8b1be76 100644 --- a/packages/common/src/consts/index.ts +++ b/packages/common/src/consts/index.ts @@ -49,6 +49,7 @@ export const WorkflowDefinitionVariant = { KYB_WITH_ASSOCIATED_COMPANIES: 'KYB_WITH_ASSOCIATED_COMPANIES', KYC: 'KYC', DEFAULT: 'DEFAULT', + ONGOING: 'ONGOING', } as const; export type TStateTags = typeof StateTags; From a14ce74a42b53dc3632449267bd542c10c6e7edf Mon Sep 17 00:00:00 2001 From: Omri Levy Date: Wed, 28 Feb 2024 19:08:35 +0200 Subject: [PATCH 05/20] feat(backoffice-v2): added an actions variant for on-going --- .../public/locales/en/toast.json | 8 ++ apps/backoffice-v2/src/common/enums.ts | 2 + .../useApproveCaseMutation.tsx | 4 +- .../useDismissCaseMutation.tsx | 38 ++++++ .../useFlagCaseMutation.tsx | 38 ++++++ .../useRejectCaseMutation.tsx} | 6 +- .../useSelectNextCase.tsx} | 2 +- .../src/domains/workflows/fetchers.ts | 4 - .../useRevisionCaseMutation.tsx | 4 +- .../Entity/components/Case/Case.Actions.tsx | 98 ++-------------- .../ActionsVariant/ActionsVariant.tsx | 17 +++ .../DefaultActions/DefaultActions.tsx | 98 ++++++++++++++++ .../useDefaultActionsLogic.tsx | 82 +++++++++++++ .../OnGoingActions/OnGoingActions.tsx | 110 ++++++++++++++++++ .../useOnGoingActionsLogic.tsx | 64 ++++++++++ .../useCaseActionsLogic.tsx | 46 +------- 16 files changed, 476 insertions(+), 145 deletions(-) create mode 100644 apps/backoffice-v2/src/domains/entities/hooks/mutations/useDismissCaseMutation/useDismissCaseMutation.tsx create mode 100644 apps/backoffice-v2/src/domains/entities/hooks/mutations/useFlagCaseMutation/useFlagCaseMutation.tsx rename apps/backoffice-v2/src/domains/entities/hooks/mutations/{useRejectEntityMutation/useRejectEntityMutation.tsx => useRejectCaseMutation/useRejectCaseMutation.tsx} (90%) rename apps/backoffice-v2/src/domains/entities/hooks/{useSelectNextEntity/useSelectNextEntity.tsx => useSelectNextCase/useSelectNextCase.tsx} (77%) create mode 100644 apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/ActionsVariant/ActionsVariant.tsx create mode 100644 apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/DefaultActions/DefaultActions.tsx create mode 100644 apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/DefaultActions/hooks/useDefaultActionsLogic/useDefaultActionsLogic.tsx create mode 100644 apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/OnGoingActions.tsx create mode 100644 apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/hooks/useOnGoingActionsLogic/useOnGoingActionsLogic.tsx diff --git a/apps/backoffice-v2/public/locales/en/toast.json b/apps/backoffice-v2/public/locales/en/toast.json index 9fa4d58c7d..e5a91496eb 100644 --- a/apps/backoffice-v2/public/locales/en/toast.json +++ b/apps/backoffice-v2/public/locales/en/toast.json @@ -1,4 +1,12 @@ { + "dismiss_case": { + "success": "The case has been dismissed successfully.", + "error": "Error occurred while dismissing the case." + }, + "flag_case": { + "success": "The case has been flagged successfully.", + "error": "Error occurred while flagging the case." + }, "approve_case": { "success": "The case has been approved successfully.", "error": "Error occurred while approving the case." diff --git a/apps/backoffice-v2/src/common/enums.ts b/apps/backoffice-v2/src/common/enums.ts index badad106cd..3d8e922ed9 100644 --- a/apps/backoffice-v2/src/common/enums.ts +++ b/apps/backoffice-v2/src/common/enums.ts @@ -40,6 +40,8 @@ export const Action = { REVISION: 'revision', TASK_REVIEWED: 'TASK_REVIEWED', CASE_REVIEWED: 'CASE_REVIEWED', + DISMISS: 'dismiss', + FLAG: 'flag', } as const; export const Resource = { INDIVIDUAL: 'INDIVIDUAL', diff --git a/apps/backoffice-v2/src/domains/entities/hooks/mutations/useApproveCaseMutation/useApproveCaseMutation.tsx b/apps/backoffice-v2/src/domains/entities/hooks/mutations/useApproveCaseMutation/useApproveCaseMutation.tsx index cee01be486..dd9d3a2eca 100644 --- a/apps/backoffice-v2/src/domains/entities/hooks/mutations/useApproveCaseMutation/useApproveCaseMutation.tsx +++ b/apps/backoffice-v2/src/domains/entities/hooks/mutations/useApproveCaseMutation/useApproveCaseMutation.tsx @@ -8,10 +8,10 @@ import { Action } from '../../../../../common/enums'; // @TODO: Refactor to be under cases/workflows domain export const useApproveCaseMutation = ({ workflowId, - onSelectNextEntity, + onSelectNextCase, }: { workflowId: string; - onSelectNextEntity?: VoidFunction; + onSelectNextCase?: VoidFunction; }) => { const queryClient = useQueryClient(); diff --git a/apps/backoffice-v2/src/domains/entities/hooks/mutations/useDismissCaseMutation/useDismissCaseMutation.tsx b/apps/backoffice-v2/src/domains/entities/hooks/mutations/useDismissCaseMutation/useDismissCaseMutation.tsx new file mode 100644 index 0000000000..8f4f447860 --- /dev/null +++ b/apps/backoffice-v2/src/domains/entities/hooks/mutations/useDismissCaseMutation/useDismissCaseMutation.tsx @@ -0,0 +1,38 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import toast from 'react-hot-toast'; +import { t } from 'i18next'; +import { fetchWorkflowEvent } from '../../../../workflows/fetchers'; +import { workflowsQueryKeys } from '../../../../workflows/query-keys'; +import { Action } from '../../../../../common/enums'; + +export const useDismissCaseMutation = ({ + workflowId, + onSelectNextCase, +}: { + workflowId: string; + onSelectNextCase?: VoidFunction; +}) => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: () => + fetchWorkflowEvent({ + workflowId, + body: { + name: Action.DISMISS, + }, + }), + onSuccess: () => { + // workflowsQueryKeys._def is the base key for all workflows queries + void queryClient.invalidateQueries(workflowsQueryKeys._def); + + toast.success(t('toast:dismiss_case.success')); + + // TODO: Re-implement + // onSelectNextEntity(); + }, + onError: () => { + toast.error(t('toast:dismiss_case.error')); + }, + }); +}; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/mutations/useFlagCaseMutation/useFlagCaseMutation.tsx b/apps/backoffice-v2/src/domains/entities/hooks/mutations/useFlagCaseMutation/useFlagCaseMutation.tsx new file mode 100644 index 0000000000..710165c693 --- /dev/null +++ b/apps/backoffice-v2/src/domains/entities/hooks/mutations/useFlagCaseMutation/useFlagCaseMutation.tsx @@ -0,0 +1,38 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import toast from 'react-hot-toast'; +import { t } from 'i18next'; +import { fetchWorkflowEvent } from '../../../../workflows/fetchers'; +import { workflowsQueryKeys } from '../../../../workflows/query-keys'; +import { Action } from '../../../../../common/enums'; + +export const useFlagCaseMutation = ({ + workflowId, + onSelectNextCase, +}: { + workflowId: string; + onSelectNextCase?: VoidFunction; +}) => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: () => + fetchWorkflowEvent({ + workflowId, + body: { + name: Action.FLAG, + }, + }), + onSuccess: () => { + // workflowsQueryKeys._def is the base key for all workflows queries + void queryClient.invalidateQueries(workflowsQueryKeys._def); + + toast.success(t('toast:flag_case.success')); + + // TODO: Re-implement + // onSelectNextEntity(); + }, + onError: () => { + toast.error(t('toast:flag_case.error')); + }, + }); +}; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/mutations/useRejectEntityMutation/useRejectEntityMutation.tsx b/apps/backoffice-v2/src/domains/entities/hooks/mutations/useRejectCaseMutation/useRejectCaseMutation.tsx similarity index 90% rename from apps/backoffice-v2/src/domains/entities/hooks/mutations/useRejectEntityMutation/useRejectEntityMutation.tsx rename to apps/backoffice-v2/src/domains/entities/hooks/mutations/useRejectCaseMutation/useRejectCaseMutation.tsx index 04a923abf4..8ff46537f7 100644 --- a/apps/backoffice-v2/src/domains/entities/hooks/mutations/useRejectEntityMutation/useRejectEntityMutation.tsx +++ b/apps/backoffice-v2/src/domains/entities/hooks/mutations/useRejectCaseMutation/useRejectCaseMutation.tsx @@ -6,12 +6,12 @@ import { fetchWorkflowEvent } from '../../../../workflows/fetchers'; import { workflowsQueryKeys } from '../../../../workflows/query-keys'; // @TODO: Refactor to be under cases/workflows domain -export const useRejectEntityMutation = ({ +export const useRejectCaseMutation = ({ workflowId, - onSelectNextEntity, + onSelectNextCase, }: { workflowId: string; - onSelectNextEntity?: VoidFunction; + onSelectNextCase?: VoidFunction; }) => { const queryClient = useQueryClient(); diff --git a/apps/backoffice-v2/src/domains/entities/hooks/useSelectNextEntity/useSelectNextEntity.tsx b/apps/backoffice-v2/src/domains/entities/hooks/useSelectNextCase/useSelectNextCase.tsx similarity index 77% rename from apps/backoffice-v2/src/domains/entities/hooks/useSelectNextEntity/useSelectNextEntity.tsx rename to apps/backoffice-v2/src/domains/entities/hooks/useSelectNextCase/useSelectNextCase.tsx index a13c1d537d..87ad9bfd60 100644 --- a/apps/backoffice-v2/src/domains/entities/hooks/useSelectNextEntity/useSelectNextEntity.tsx +++ b/apps/backoffice-v2/src/domains/entities/hooks/useSelectNextCase/useSelectNextCase.tsx @@ -1,6 +1,6 @@ import { useCallback } from 'react'; -export const useSelectNextEntity = () => { +export const useSelectNextCase = () => { return useCallback(() => { // @TODO: Implement throw new Error('Not implemented'); diff --git a/apps/backoffice-v2/src/domains/workflows/fetchers.ts b/apps/backoffice-v2/src/domains/workflows/fetchers.ts index 7b8003926f..3407b7db74 100644 --- a/apps/backoffice-v2/src/domains/workflows/fetchers.ts +++ b/apps/backoffice-v2/src/domains/workflows/fetchers.ts @@ -131,10 +131,6 @@ export const fetchWorkflowById = async ({ method: Method.GET, schema: WorkflowByIdSchema.transform(data => ({ ...data, - workflowDefinition: { - ...data.workflowDefinition, - variant: 'ONGOING', - }, context: { ...data.context, pluginsOutput: { diff --git a/apps/backoffice-v2/src/domains/workflows/hooks/mutations/useRevisionCaseMutation/useRevisionCaseMutation.tsx b/apps/backoffice-v2/src/domains/workflows/hooks/mutations/useRevisionCaseMutation/useRevisionCaseMutation.tsx index faf527d511..f38b06bc66 100644 --- a/apps/backoffice-v2/src/domains/workflows/hooks/mutations/useRevisionCaseMutation/useRevisionCaseMutation.tsx +++ b/apps/backoffice-v2/src/domains/workflows/hooks/mutations/useRevisionCaseMutation/useRevisionCaseMutation.tsx @@ -6,9 +6,9 @@ import { fetchWorkflowEvent } from '../../../fetchers'; import { workflowsQueryKeys } from '../../../query-keys'; export const useRevisionCaseMutation = ({ - onSelectNextEntity, + onSelectNextCase, }: { - onSelectNextEntity?: VoidFunction; + onSelectNextCase?: VoidFunction; }) => { const queryClient = useQueryClient(); diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Actions.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Actions.tsx index b412719774..65df83fda3 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Actions.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Actions.tsx @@ -1,22 +1,13 @@ import React, { FunctionComponent } from 'react'; -import { Send } from 'lucide-react'; import { Badge } from '@ballerine/ui'; import { StateTag } from '@ballerine/common'; -import { DialogClose } from '@radix-ui/react-dialog'; import { IActionsProps } from './interfaces'; import { useCaseActionsLogic } from './hooks/useCaseActionsLogic/useCaseActionsLogic'; import { ctw } from '../../../../common/utils/ctw/ctw'; import { AssignDropdown } from '../../../../common/components/atoms/AssignDropdown/AssignDropdown'; -import { Button } from '../../../../common/components/atoms/Button/Button'; -import { Dialog } from '../../../../common/components/organisms/Dialog/Dialog'; -import { DialogTrigger } from '../../../../common/components/organisms/Dialog/Dialog.Trigger'; -import { DialogContent } from '../../../../common/components/organisms/Dialog/Dialog.Content'; -import { DialogHeader } from '../../../../common/components/organisms/Dialog/Dialog.Header'; -import { DialogTitle } from '../../../../common/components/organisms/Dialog/Dialog.Title'; -import { DialogDescription } from '../../../../common/components/organisms/Dialog/Dialog.Description'; -import { DialogFooter } from '../../../../common/components/organisms/Dialog/Dialog.Footer'; import { tagToBadgeData } from './consts'; +import { ActionsVariant } from '@/pages/Entity/components/Case/actions-variants/ActionsVariant/ActionsVariant'; /** * @description To be used by {@link Case}. Displays the entity's full name, avatar, and handles the reject/approve mutation. @@ -40,20 +31,10 @@ export const Actions: FunctionComponent = ({ tag, assignedUser, authenticatedUser, - isLoading, isLoadingCase, - canApprove, - canReject, - canRevision, assignees, - debouncedIsLoadingApproveEntity, - debouncedIsLoadingRejectEntity, - debouncedIsLoadingRevisionCase, - documentsToReviseCount, - onMutateApproveEntity, - onMutateRevisionCase, - onMutateRejectEntity, onMutateAssignWorkflow, + workflowDefinition, } = useCaseActionsLogic({ workflowId: id, fullName }); return ( @@ -96,73 +77,14 @@ export const Actions: FunctionComponent = ({
)}
- {showResolutionButtons && ( -
- - - - - - - Ask for all re-uploads - -
- By clicking the button below, an email with a link will be sent to the - customer, directing them to re-upload the documents you have marked as - “re-upload needed”. -
-
- The case’s status will then change to “Revisions” until the customer will - provide the needed documents and fixes. -
-
-
- - - - - -
-
- - -
+ {showResolutionButtons && workflowDefinition && ( + )}
diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/ActionsVariant/ActionsVariant.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/ActionsVariant/ActionsVariant.tsx new file mode 100644 index 0000000000..7a8d234fee --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/ActionsVariant/ActionsVariant.tsx @@ -0,0 +1,17 @@ +import { FunctionComponent } from 'react'; +import { TWorkflowById } from '@/domains/workflows/fetchers'; +import { checkIsOnGoingVariant } from '@/lib/blocks/variants/variant-checkers'; +import { DefaultActions } from '@/pages/Entity/components/Case/actions-variants/DefaultActions/DefaultActions'; +import { OnGoingActions } from '@/pages/Entity/components/Case/actions-variants/OnGoingActions/OnGoingActions'; + +export const ActionsVariant: FunctionComponent<{ + workflowDefinition: Pick; +}> = ({ workflowDefinition }) => { + const isOnGoingVariant = checkIsOnGoingVariant(workflowDefinition); + + if (isOnGoingVariant) { + return ; + } + + return ; +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/DefaultActions/DefaultActions.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/DefaultActions/DefaultActions.tsx new file mode 100644 index 0000000000..7895943f88 --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/DefaultActions/DefaultActions.tsx @@ -0,0 +1,98 @@ +import { Dialog } from '@/common/components/organisms/Dialog/Dialog'; +import { DialogTrigger } from '@/common/components/organisms/Dialog/Dialog.Trigger'; +import { Button } from '@/common/components/atoms/Button/Button'; +import { ctw } from '@/common/utils/ctw/ctw'; +import { DialogContent } from '@/common/components/organisms/Dialog/Dialog.Content'; +import { DialogHeader } from '@/common/components/organisms/Dialog/Dialog.Header'; +import { DialogTitle } from '@/common/components/organisms/Dialog/Dialog.Title'; +import { DialogDescription } from '@/common/components/organisms/Dialog/Dialog.Description'; +import { DialogFooter } from '@/common/components/organisms/Dialog/Dialog.Footer'; +import { DialogClose } from '@radix-ui/react-dialog'; +import { Send } from 'lucide-react'; +import React from 'react'; + +import { useDefaultActionsLogic } from '@/pages/Entity/components/Case/actions-variants/DefaultActions/hooks/useDefaultActionsLogic/useDefaultActionsLogic'; + +export const DefaultActions = () => { + const { + isLoadingActions, + canRevision, + debouncedIsLoadingRejectCase, + documentsToReviseCount, + debouncedIsLoadingRevisionCase, + onMutateRevisionCase, + onMutateRejectCase, + canReject, + onMutateApproveCase, + canApprove, + debouncedIsLoadingApproveCase, + } = useDefaultActionsLogic(); + + return ( +
+ + + + + + + Ask for all re-uploads + +
+ By clicking the button below, an email with a link will be sent to the customer, + directing them to re-upload the documents you have marked as “re-upload needed”. +
+
+ The case’s status will then change to “Revisions” until the customer will provide + the needed documents and fixes. +
+
+
+ + + + + +
+
+ + +
+ ); +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/DefaultActions/hooks/useDefaultActionsLogic/useDefaultActionsLogic.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/DefaultActions/hooks/useDefaultActionsLogic/useDefaultActionsLogic.tsx new file mode 100644 index 0000000000..a968bb5fb1 --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/DefaultActions/hooks/useDefaultActionsLogic/useDefaultActionsLogic.tsx @@ -0,0 +1,82 @@ +import { useParams } from 'react-router-dom'; +import { useFilterId } from '@/common/hooks/useFilterId/useFilterId'; +import { useWorkflowByIdQuery } from '@/domains/workflows/hooks/queries/useWorkflowByIdQuery/useWorkflowByIdQuery'; +import { useCaseDecision } from '@/pages/Entity/components/Case/hooks/useCaseDecision/useCaseDecision'; +import { useSelectNextCase } from '@/domains/entities/hooks/useSelectNextCase/useSelectNextCase'; +import { useApproveCaseMutation } from '@/domains/entities/hooks/mutations/useApproveCaseMutation/useApproveCaseMutation'; +import { useRejectCaseMutation } from '@/domains/entities/hooks/mutations/useRejectCaseMutation/useRejectCaseMutation'; +import { useRevisionCaseMutation } from '@/domains/workflows/hooks/mutations/useRevisionCaseMutation/useRevisionCaseMutation'; +import { useAssignWorkflowMutation } from '@/domains/workflows/hooks/mutations/useAssignWorkflowMutation/useAssignWorkflowMutation'; +import { useCallback, useMemo } from 'react'; +import { usePendingRevisionEvents } from '@/pages/Entity/components/Case/hooks/usePendingRevisionEvents/usePendingRevisionEvents'; +import { CommonWorkflowEvent } from '@ballerine/common'; +import { useDebounce } from '@/common/hooks/useDebounce/useDebounce'; + +export const useDefaultActionsLogic = () => { + const { entityId } = useParams(); + const filterId = useFilterId(); + + const { data: workflow } = useWorkflowByIdQuery({ + workflowId: entityId ?? '', + filterId: filterId ?? '', + }); + + const { canApprove, canReject, canRevision } = useCaseDecision(); + + const onSelectNextCase = useSelectNextCase(); + + const { isLoading: isLoadingApproveCase, mutate: mutateApproveCase } = useApproveCaseMutation({ + workflowId: workflow?.id, + onSelectNextCase, + }); + const { isLoading: isLoadingRejectCase, mutate: mutateRejectCase } = useRejectCaseMutation({ + workflowId: workflow?.id, + onSelectNextCase, + }); + const { mutate: mutateRevisionCase, isLoading: isLoadingRevisionCase } = useRevisionCaseMutation({ + onSelectNextCase, + }); + + const { isLoading: isLoadingAssignWorkflow } = useAssignWorkflowMutation({ + workflowRuntimeId: workflow?.id, + }); + + const isLoadingActions = + isLoadingApproveCase || isLoadingRejectCase || isLoadingRevisionCase || isLoadingAssignWorkflow; + + // Avoid passing the onClick event to mutate + const onMutateApproveCase = useCallback(() => mutateApproveCase(), [mutateApproveCase]); + const onMutateRejectCase = useCallback(() => mutateRejectCase(), [mutateRejectCase]); + + const { onMutateRevisionCase, pendingWorkflowEvents } = usePendingRevisionEvents( + mutateRevisionCase, + workflow, + ); + + const documentsToReviseCount = useMemo( + () => + pendingWorkflowEvents?.filter( + pendingEvent => pendingEvent.eventName === CommonWorkflowEvent.REVISION, + )?.length, + [pendingWorkflowEvents], + ); + + // Only display the button spinners if the request is longer than 300ms + const debouncedIsLoadingRejectCase = useDebounce(isLoadingRejectCase, 300); + const debouncedIsLoadingRevisionCase = useDebounce(isLoadingRevisionCase, 300); + const debouncedIsLoadingApproveCase = useDebounce(isLoadingApproveCase, 300); + + return { + isLoadingActions, + canRevision, + debouncedIsLoadingRejectCase, + documentsToReviseCount, + debouncedIsLoadingRevisionCase, + onMutateRevisionCase, + onMutateRejectCase, + canReject, + onMutateApproveCase, + canApprove, + debouncedIsLoadingApproveCase, + }; +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/OnGoingActions.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/OnGoingActions.tsx new file mode 100644 index 0000000000..c4e0e13282 --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/OnGoingActions.tsx @@ -0,0 +1,110 @@ +import { Dialog } from '@/common/components/organisms/Dialog/Dialog'; +import { DialogTrigger } from '@/common/components/organisms/Dialog/Dialog.Trigger'; +import { Button } from '@/common/components/atoms/Button/Button'; +import { ctw } from '@/common/utils/ctw/ctw'; +import { DialogContent } from '@/common/components/organisms/Dialog/Dialog.Content'; +import { DialogHeader } from '@/common/components/organisms/Dialog/Dialog.Header'; +import { DialogTitle } from '@/common/components/organisms/Dialog/Dialog.Title'; +import { DialogDescription } from '@/common/components/organisms/Dialog/Dialog.Description'; +import { DialogFooter } from '@/common/components/organisms/Dialog/Dialog.Footer'; +import { DialogClose } from '@radix-ui/react-dialog'; +import React from 'react'; + +import { useOnGoingActionsLogic } from '@/pages/Entity/components/Case/actions-variants/OnGoingActions/hooks/useOnGoingActionsLogic/useOnGoingActionsLogic'; + +export const OnGoingActions = () => { + const { + isLoadingActions, + debouncedIsLoadingFlagCase, + onMutateFlagCase, + canFlag, + onMutateDismissCase, + canDismiss, + debouncedIsLoadingDismissCase, + } = useOnGoingActionsLogic(); + + return ( +
+ + + + + + + Dismissal Confirmation + +

Are you sure you want to dismiss?

+
+
+ + +
+ + +
+
+
+
+
+ + + + + + + Flagging Confirmation + +

Are you sure you want to confirm match?

+
+
+ + +
+ + +
+
+
+
+
+
+ ); +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/hooks/useOnGoingActionsLogic/useOnGoingActionsLogic.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/hooks/useOnGoingActionsLogic/useOnGoingActionsLogic.tsx new file mode 100644 index 0000000000..dfe83ed18d --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/hooks/useOnGoingActionsLogic/useOnGoingActionsLogic.tsx @@ -0,0 +1,64 @@ +import { useParams } from 'react-router-dom'; +import { useFilterId } from '@/common/hooks/useFilterId/useFilterId'; +import { useWorkflowByIdQuery } from '@/domains/workflows/hooks/queries/useWorkflowByIdQuery/useWorkflowByIdQuery'; +import { useSelectNextCase } from '@/domains/entities/hooks/useSelectNextCase/useSelectNextCase'; +import { useAssignWorkflowMutation } from '@/domains/workflows/hooks/mutations/useAssignWorkflowMutation/useAssignWorkflowMutation'; +import { useCallback } from 'react'; +import { useDebounce } from '@/common/hooks/useDebounce/useDebounce'; +import { useDismissCaseMutation } from '@/domains/entities/hooks/mutations/useDismissCaseMutation/useDismissCaseMutation'; +import { useFlagCaseMutation } from '@/domains/entities/hooks/mutations/useFlagCaseMutation/useFlagCaseMutation'; +import { Action } from '@/common/enums'; +import { useAuthenticatedUserQuery } from '@/domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; +import { useCaseState } from '@/pages/Entity/components/Case/hooks/useCaseState/useCaseState'; + +export const useOnGoingActionsLogic = () => { + const { entityId } = useParams(); + const filterId = useFilterId(); + + const { data: workflow } = useWorkflowByIdQuery({ + workflowId: entityId ?? '', + filterId: filterId ?? '', + }); + + const { data: session } = useAuthenticatedUserQuery(); + const caseState = useCaseState(session?.user, workflow); + + const canDismiss = + caseState.actionButtonsEnabled && workflow?.nextEvents?.includes(Action.DISMISS); + const canFlag = caseState.actionButtonsEnabled && workflow?.nextEvents?.includes(Action.FLAG); + + const onSelectNextCase = useSelectNextCase(); + + const { isLoading: isLoadingDismissCase, mutate: mutateDismissCase } = useDismissCaseMutation({ + workflowId: workflow?.id, + onSelectNextCase, + }); + const { isLoading: isLoadingFlagCase, mutate: mutateFlagCase } = useFlagCaseMutation({ + workflowId: workflow?.id, + onSelectNextCase, + }); + + const { isLoading: isLoadingAssignWorkflow } = useAssignWorkflowMutation({ + workflowRuntimeId: workflow?.id, + }); + + const isLoadingActions = isLoadingDismissCase || isLoadingFlagCase || isLoadingAssignWorkflow; + + // Avoid passing the onClick event to mutate + const onMutateDismissCase = useCallback(() => mutateDismissCase(), [mutateDismissCase]); + const onMutateFlagCase = useCallback(() => mutateFlagCase(), [mutateFlagCase]); + + // Only display the button spinners if the request is longer than 300ms + const debouncedIsLoadingFlagCase = useDebounce(isLoadingFlagCase, 300); + const debouncedIsLoadingDismissCase = useDebounce(isLoadingDismissCase, 300); + + return { + isLoadingActions, + debouncedIsLoadingFlagCase, + onMutateFlagCase, + canFlag, + onMutateDismissCase, + canDismiss, + debouncedIsLoadingDismissCase, + }; +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useCaseActionsLogic/useCaseActionsLogic.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useCaseActionsLogic/useCaseActionsLogic.tsx index 7dfb7e30ce..b5207a073d 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useCaseActionsLogic/useCaseActionsLogic.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useCaseActionsLogic/useCaseActionsLogic.tsx @@ -1,5 +1,4 @@ import { useCallback, useMemo } from 'react'; -import { useApproveCaseMutation } from '../../../../../../domains/entities/hooks/mutations/useApproveCaseMutation/useApproveCaseMutation'; import { useDebounce } from '../../../../../../common/hooks/useDebounce/useDebounce'; import { createInitials } from '../../../../../../common/utils/create-initials/create-initials'; import { IUseActions } from './interfaces'; @@ -7,41 +6,21 @@ import { useAuthenticatedUserQuery } from '../../../../../../domains/auth/hooks/ import { useCaseState } from '../useCaseState/useCaseState'; import { useUsersQuery } from '../../../../../../domains/users/hooks/queries/useUsersQuery/useUsersQuery'; import { useAssignWorkflowMutation } from '../../../../../../domains/workflows/hooks/mutations/useAssignWorkflowMutation/useAssignWorkflowMutation'; -import { useRejectEntityMutation } from '../../../../../../domains/entities/hooks/mutations/useRejectEntityMutation/useRejectEntityMutation'; -import { useSelectNextEntity } from '../../../../../../domains/entities/hooks/useSelectNextEntity/useSelectNextEntity'; import { useWorkflowByIdQuery } from '@/domains/workflows/hooks/queries/useWorkflowByIdQuery/useWorkflowByIdQuery'; import { useFilterId } from '../../../../../../common/hooks/useFilterId/useFilterId'; -import { useRevisionCaseMutation } from '../../../../../../domains/workflows/hooks/mutations/useRevisionCaseMutation/useRevisionCaseMutation'; import { useCaseDecision } from '../useCaseDecision/useCaseDecision'; import { tagToBadgeData } from '../../consts'; -import { usePendingRevisionEvents } from '@/pages/Entity/components/Case/hooks/usePendingRevisionEvents/usePendingRevisionEvents'; -import { CommonWorkflowEvent } from '@ballerine/common'; export const useCaseActionsLogic = ({ workflowId, fullName }: IUseActions) => { - const onSelectNextEntity = useSelectNextEntity(); const filterId = useFilterId(); const { data: workflow, isLoading: isLoadingCase } = useWorkflowByIdQuery({ workflowId, filterId, }); - const { mutate: mutateApproveEntity, isLoading: isLoadingApproveEntity } = useApproveCaseMutation( - { - workflowId: workflowId, - onSelectNextEntity, - }, - ); - const { mutate: mutateRevisionCase, isLoading: isLoadingRevisionCase } = useRevisionCaseMutation({ - onSelectNextEntity, - }); - const { mutate: mutateRejectEntity, isLoading: isLoadingRejectEntity } = useRejectEntityMutation({ - workflowId: workflowId, - onSelectNextEntity, - }); const { mutate: mutateAssignWorkflow, isLoading: isLoadingAssignWorkflow } = useAssignWorkflowMutation({ workflowRuntimeId: workflowId }); - const isLoading = isLoadingApproveEntity || isLoadingRejectEntity || isLoadingAssignWorkflow; // Create initials from the first character of the first name, middle name, and last name. const initials = createInitials(fullName); @@ -52,18 +31,9 @@ export const useCaseActionsLogic = ({ workflowId, fullName }: IUseActions) => { const { hasDecision, canApprove, canReject, canRevision } = useCaseDecision(); // Only display the button spinners if the request is longer than 300ms - const debouncedIsLoadingRejectEntity = useDebounce(isLoadingRejectEntity, 300); - const debouncedIsLoadingRevisionCase = useDebounce(isLoadingRevisionCase, 300); - const debouncedIsLoadingApproveEntity = useDebounce(isLoadingApproveEntity, 300); const debouncedIsLoadingAssignEntity = useDebounce(isLoadingAssignWorkflow, 300); // Avoid passing the onClick event to mutate - const onMutateApproveEntity = useCallback(() => mutateApproveEntity(), [mutateApproveEntity]); - const { onMutateRevisionCase, pendingWorkflowEvents } = usePendingRevisionEvents( - mutateRevisionCase, - workflow, - ); - const onMutateRejectEntity = useCallback(() => mutateRejectEntity(), [mutateRejectEntity]); const onMutateAssignWorkflow = useCallback( (assigneeId: string, isAssignedToMe: boolean) => mutateAssignWorkflow({ @@ -79,13 +49,6 @@ export const useCaseActionsLogic = ({ workflowId, fullName }: IUseActions) => { const isActionButtonDisabled = !caseState.actionButtonsEnabled; - const documentsToReviseCount = useMemo( - () => - pendingWorkflowEvents?.filter( - pendingEvent => pendingEvent.eventName === CommonWorkflowEvent.REVISION, - )?.length, - [pendingWorkflowEvents], - ); const assignedUser = workflow?.assignee ? { id: workflow?.assignee?.id, @@ -96,15 +59,8 @@ export const useCaseActionsLogic = ({ workflowId, fullName }: IUseActions) => { return { isActionButtonDisabled, - onMutateApproveEntity, - onMutateRevisionCase, - onMutateRejectEntity, onMutateAssignWorkflow, - debouncedIsLoadingRejectEntity, - debouncedIsLoadingApproveEntity, - debouncedIsLoadingRevisionCase, debouncedIsLoadingAssignEntity, - isLoading, initials, canReject, canApprove, @@ -115,7 +71,7 @@ export const useCaseActionsLogic = ({ workflowId, fullName }: IUseActions) => { assignedUser, hasDecision, isLoadingCase, - documentsToReviseCount, tag, + workflowDefinition: workflow?.workflowDefinition, }; }; From 85685abc7347d3f49ea6bcc3e32eafa8a953ca59 Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Tue, 27 Feb 2024 00:05:20 +0200 Subject: [PATCH 06/20] chore(package.json): update db:reset:dev:with-data script to use db:reset:dev script before running db:data-migration:migrate chore(package.json): add new migration script for adding a config column to the Customer table chore(schema.prisma): add config field to the Customer model chore(app.module.ts): import and add WebhooksModule to the AppModule chore(zod-schemas.ts): add CustomerConfigSchema and TCustomerConfig types chore(end-user.repository.ts): update import path for PrismaService chore(end-user.repository.ts): add new method findByIdUnscoped to retrieve end user by id without applying project scope chore(end-user.service.ts): add missing line break chore(filter.repository.ts): update scopeService.scopeFindFirst to scopeService.scopeFindFirst with projectIds parameter chore(global.d.ts): add config field to the Customer type chore(webhooks/dtos/aml-webhook-input.ts): create AmlWebhookInput class chore(webhooks/dtos/individual-aml-webhook-input.ts): create IndividualAmlWebhookInput class feat(webhooks): add webhooks controller, module, and service - Add a new file `webhooks.controller.ts` to handle webhooks related requests. - Add a new file `webhooks.module.ts` to define the webhooks module and its dependencies. - Add a new file `webhooks.service.ts` to handle webhook events. - Implement the `amlHook` method in the `WebhooksController` to handle AML webhook events. - Implement the `handleAmlHit` method in the `WebhooksService` to handle AML_HIT events. - Update the imports and dependencies in the existing files to include the newly added files. The purpose of these changes is to introduce a new feature that allows handling AML webhook events in the application. This feature will enable the application to respond to AML_HIT events and perform necessary actions based on the event data. --- services/workflows-service/package.json | 2 +- .../workflows-service/prisma/data-migrations | 2 +- .../migration.sql | 1 + .../workflows-service/prisma/schema.prisma | 1 + services/workflows-service/src/app.module.ts | 2 + .../src/customer/schemas/zod-schemas.ts | 4 ++ .../src/end-user/end-user.repository.ts | 14 +++- .../src/end-user/end-user.service.ts | 1 + .../src/filter/filter.repository.ts | 11 +-- services/workflows-service/src/global.d.ts | 5 +- .../src/webhooks/dtos/aml-webhook-input.ts | 11 +++ .../dtos/individual-aml-webhook-input.ts | 53 ++++++++++++++ .../src/webhooks/webhooks.controller.ts | 55 +++++++++++++++ .../src/webhooks/webhooks.module.ts | 70 +++++++++++++++++++ .../src/webhooks/webhooks.service.ts | 50 +++++++++++++ .../workflow-definition.service.ts | 6 +- .../workflow/workflow.controller.external.ts | 1 + .../src/workflow/workflow.service.ts | 19 ----- 18 files changed, 276 insertions(+), 32 deletions(-) create mode 100644 services/workflows-service/prisma/migrations/20240226191757_add_config_column_to_customer/migration.sql create mode 100644 services/workflows-service/src/webhooks/dtos/aml-webhook-input.ts create mode 100644 services/workflows-service/src/webhooks/dtos/individual-aml-webhook-input.ts create mode 100644 services/workflows-service/src/webhooks/webhooks.controller.ts create mode 100644 services/workflows-service/src/webhooks/webhooks.module.ts create mode 100644 services/workflows-service/src/webhooks/webhooks.service.ts diff --git a/services/workflows-service/package.json b/services/workflows-service/package.json index 6b574dd4a1..ab8f3ebf3f 100644 --- a/services/workflows-service/package.json +++ b/services/workflows-service/package.json @@ -25,7 +25,7 @@ "db:migrate-up": "prisma migrate deploy", "db:reset": "prisma migrate reset --skip-seed -f", "db:reset:dev": "npm run db:reset && npm run seed", - "db:reset:dev:with-data": "npm run db:reset && npm run seed && npm run db:data-migration:migrate", + "db:reset:dev:with-data": "npm run db:reset:dev && npm run db:data-migration:migrate", "db:init": "npm run db:migrate-dev -- --name 'initial version' && npm run db:migrate-up seed", "prisma:generate": "prisma generate", "docker:db": "docker compose -f docker-compose.db.yml up -d --wait", diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index 5c2b920641..479921bb5d 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit 5c2b920641fb49cdb87df3a130495938b6fb57d7 +Subproject commit 479921bb5daa0b55686a85ea11ad5a5001e7e0c5 diff --git a/services/workflows-service/prisma/migrations/20240226191757_add_config_column_to_customer/migration.sql b/services/workflows-service/prisma/migrations/20240226191757_add_config_column_to_customer/migration.sql new file mode 100644 index 0000000000..406ae3962b --- /dev/null +++ b/services/workflows-service/prisma/migrations/20240226191757_add_config_column_to_customer/migration.sql @@ -0,0 +1 @@ +ALTER TABLE "Customer" ADD COLUMN "config" JSONB; diff --git a/services/workflows-service/prisma/schema.prisma b/services/workflows-service/prisma/schema.prisma index 4361c94d8c..b32e5ca8ee 100644 --- a/services/workflows-service/prisma/schema.prisma +++ b/services/workflows-service/prisma/schema.prisma @@ -283,6 +283,7 @@ model Customer { logoImageUri String faviconImageUri String @default("") // TODO: remove default value after data migration customerStatus CustomerStatuses @default(onboarding) + config Json? authenticationConfiguration Json? country String? language String? diff --git a/services/workflows-service/src/app.module.ts b/services/workflows-service/src/app.module.ts index cb54c2bf5e..6d3243cb54 100644 --- a/services/workflows-service/src/app.module.ts +++ b/services/workflows-service/src/app.module.ts @@ -37,6 +37,7 @@ import { initHttpMoudle } from '@/common/http-service/http-config.service'; import { DataMigrationModule } from '@/data-migration/data-migration.module'; import { CaseManagementModule } from '@/case-management/case-management.module'; import { WorkflowModule } from '@/workflow/workflow.module'; +import { WebhooksModule } from '@/webhooks/webhooks.module'; @Module({ controllers: [MetricsController], @@ -50,6 +51,7 @@ import { WorkflowModule } from '@/workflow/workflow.module'; EventEmitterModule.forRoot(), UserModule, WorkflowModule, + WebhooksModule, UiDefinitionModule, StorageModule, DataMigrationModule, diff --git a/services/workflows-service/src/customer/schemas/zod-schemas.ts b/services/workflows-service/src/customer/schemas/zod-schemas.ts index 997c3f02f9..a6edbdcb11 100644 --- a/services/workflows-service/src/customer/schemas/zod-schemas.ts +++ b/services/workflows-service/src/customer/schemas/zod-schemas.ts @@ -4,3 +4,7 @@ import { z } from 'zod'; export const CustomerSubscriptionSchema = z.object({ subscriptions: z.array(SubscriptionSchema) }); export type TCustomerSubscription = z.infer; + +const CustomerConfigSchema = z.object({ ongoingWorkflowDefinitionId: z.string() }); + +export type TCustomerConfig = z.infer; diff --git a/services/workflows-service/src/end-user/end-user.repository.ts b/services/workflows-service/src/end-user/end-user.repository.ts index 61d3a72f22..b75a92b935 100644 --- a/services/workflows-service/src/end-user/end-user.repository.ts +++ b/services/workflows-service/src/end-user/end-user.repository.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { Prisma } from '@prisma/client'; -import { PrismaService } from '../prisma/prisma.service'; +import { PrismaService } from '@/prisma/prisma.service'; import { EndUserModel } from './end-user.model'; import type { TProjectIds } from '@/types'; import { ProjectScopeService } from '@/project/project-scope.service'; @@ -48,6 +48,18 @@ export class EndUserRepository { ); } + async findByIdUnscoped>( + id: string, + args: Prisma.SelectSubset>, + ) { + return await this.prisma.endUser.findFirstOrThrow( + this.scopeService.scopeFindFirst({ + where: { id }, + ...args, + }), + ); + } + async findByCorrelationId>( id: string, args: Prisma.SelectSubset>, diff --git a/services/workflows-service/src/end-user/end-user.service.ts b/services/workflows-service/src/end-user/end-user.service.ts index be8cbc6cbf..5c1a70b834 100644 --- a/services/workflows-service/src/end-user/end-user.service.ts +++ b/services/workflows-service/src/end-user/end-user.service.ts @@ -76,6 +76,7 @@ export class EndUserService { projectIds, ); } + async updateById(id: string, endUser: Omit) { return await this.repository.updateById(id, endUser); } diff --git a/services/workflows-service/src/filter/filter.repository.ts b/services/workflows-service/src/filter/filter.repository.ts index 28b36d34b7..b102698e4d 100644 --- a/services/workflows-service/src/filter/filter.repository.ts +++ b/services/workflows-service/src/filter/filter.repository.ts @@ -27,10 +27,13 @@ export class FilterRepository { async findById(id: string, args: Prisma.FilterFindFirstArgs, projectIds: TProjectIds) { return await this.prisma.filter.findFirst( - this.scopeService.scopeFindFirst({ - ...args, - where: { ...args?.where, id: id }, - }), + this.scopeService.scopeFindFirst( + { + ...args, + where: { ...args?.where, id: id }, + }, + projectIds, + ), ); } diff --git a/services/workflows-service/src/global.d.ts b/services/workflows-service/src/global.d.ts index ca43e76544..72b36c04a6 100644 --- a/services/workflows-service/src/global.d.ts +++ b/services/workflows-service/src/global.d.ts @@ -2,19 +2,22 @@ declare module '@prisma/client' { import type { WorkflowRuntimeData as _WorkflowRuntimeData, WorkflowDefinition as _WorkflowDefinition, - Customer as Customer_, } from '@prisma/client/index'; + import type { WorkflowConfig } from '@/workflow/schemas/zod-schemas'; + import type { TCustomerConfig, TCustomerSubscription } from '@/customer/schemas/zod-schemas'; export * from '@prisma/client/index'; export type WorkflowRuntimeData = Omit<_WorkflowRuntimeData, 'context'> & { context: any; config: WorkflowConfig | any; }; + export type WorkflowDefinition = Omit<_WorkflowDefinition, 'config'> & { config: WorkflowConfig | any; }; export type Customer = Omit<_Customer, 'subscriptions'> & { + config: TCustomerConfig | any; subscriptions: TCustomerSubscription | any; }; } diff --git a/services/workflows-service/src/webhooks/dtos/aml-webhook-input.ts b/services/workflows-service/src/webhooks/dtos/aml-webhook-input.ts new file mode 100644 index 0000000000..34916c7b9a --- /dev/null +++ b/services/workflows-service/src/webhooks/dtos/aml-webhook-input.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class AmlWebhookInput { + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + entityType!: string; +} diff --git a/services/workflows-service/src/webhooks/dtos/individual-aml-webhook-input.ts b/services/workflows-service/src/webhooks/dtos/individual-aml-webhook-input.ts new file mode 100644 index 0000000000..7f4ec6469e --- /dev/null +++ b/services/workflows-service/src/webhooks/dtos/individual-aml-webhook-input.ts @@ -0,0 +1,53 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class IndividualAmlWebhookInput { + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + id!: string; + + @ApiProperty({ + required: true, + type: Number, + }) + @IsString() + apiVersion!: number; + + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + timestamp!: string; + + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + eventName!: string; + + @ApiProperty({ + required: true, + type: String, + }) + @IsString() + @IsOptional() + entityId!: string; + + @ApiProperty({ + required: false, + type: String, + }) + @IsString() + @IsOptional() + environment?: string; + + @ApiProperty({ + required: true, + }) + data!: unknown; +} diff --git a/services/workflows-service/src/webhooks/webhooks.controller.ts b/services/workflows-service/src/webhooks/webhooks.controller.ts new file mode 100644 index 0000000000..0852928091 --- /dev/null +++ b/services/workflows-service/src/webhooks/webhooks.controller.ts @@ -0,0 +1,55 @@ +import * as common from '@nestjs/common'; +import * as swagger from '@nestjs/swagger'; +import * as nestAccessControl from 'nest-access-control'; +import * as errors from '../errors'; +import { Public } from '@/common/decorators/public.decorator'; +import { AmlWebhookInput } from './dtos/aml-webhook-input'; +import { IndividualAmlWebhookInput } from '@/webhooks/dtos/individual-aml-webhook-input'; +import { WebhooksService } from '@/webhooks/webhooks.service'; + +const WEBHOOKS = { + AML_HIT: 'AML_HIT', +} as const; + +const ENTITY_TYPES = { + BUSINESS: 'business', + INDIVIDUALS: 'individuals', +} as const; + +@swagger.ApiBearerAuth() +@swagger.ApiTags('Webhooks') +@common.Controller('webhooks') +export class WebhooksController { + constructor( + @nestAccessControl.InjectRolesBuilder() + protected readonly rolesBuilder: nestAccessControl.RolesBuilder, + private readonly webhooksService: WebhooksService, + ) {} + + @common.Post('/:entityType/aml') + @swagger.ApiOkResponse() + @common.HttpCode(200) + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) + @Public() + // @VerifyUnifiedApiSignatureDecorator() + async amlHook( + @common.Param() { entityType }: AmlWebhookInput, + @common.Body() data: IndividualAmlWebhookInput, + ) { + try { + if (entityType === ENTITY_TYPES.INDIVIDUALS) { + const { eventName } = data; + + if (eventName === WEBHOOKS.AML_HIT) { + await this.webhooksService.handleAmlHit(data as IndividualAmlWebhookInput); + } + } + } catch (error) { + console.error(error); + + throw error; + } + + return; + } +} diff --git a/services/workflows-service/src/webhooks/webhooks.module.ts b/services/workflows-service/src/webhooks/webhooks.module.ts new file mode 100644 index 0000000000..3a0653b566 --- /dev/null +++ b/services/workflows-service/src/webhooks/webhooks.module.ts @@ -0,0 +1,70 @@ +import { AuthModule } from '@/auth/auth.module'; +import { ACLModule } from '@/common/access-control/acl.module'; +import { CustomerModule } from '@/customer/customer.module'; +import { PrismaModule } from '@/prisma/prisma.module'; +import { ProjectModule } from '@/project/project.module'; +import { WorkflowDefinitionModule } from '@/workflow-defintion/workflow-definition.module'; +import { HttpModule } from '@nestjs/axios'; +import { Module, forwardRef } from '@nestjs/common'; +import { WorkflowService } from '@/workflow/workflow.service'; +import { WorkflowRuntimeDataRepository } from '@/workflow/workflow-runtime-data.repository'; +import { EndUserRepository } from '@/end-user/end-user.repository'; +import { EndUserService } from '@/end-user/end-user.service'; +import { BusinessRepository } from '@/business/business.repository'; +import { EntityRepository } from '@/common/entity/entity.repository'; +import { FileService } from '@/providers/file/file.service'; +import { WorkflowEventEmitterService } from '@/workflow/workflow-event-emitter.service'; +import { UserService } from '@/user/user.service'; +import { SalesforceService } from '@/salesforce/salesforce.service'; +import { WorkflowTokenService } from '@/auth/workflow-token/workflow-token.service'; +import { UiDefinitionService } from '@/ui-definition/ui-definition.service'; +import { StorageService } from '@/storage/storage.service'; +import { UserRepository } from '@/user/user.repository'; +import { SalesforceIntegrationRepository } from '@/salesforce/salesforce-integration.repository'; +import { WorkflowTokenRepository } from '@/auth/workflow-token/workflow-token.repository'; +import { UiDefinitionRepository } from '@/ui-definition/ui-definition.repository'; +import { FileRepository } from '@/storage/storage.repository'; +import { HookCallbackHandlerService } from '@/workflow/hook-callback-handler.service'; +import { FilterService } from '@/filter/filter.service'; +import { FilterRepository } from '@/filter/filter.repository'; +import { WebhooksController } from '@/webhooks/webhooks.controller'; +import { WebhooksService } from '@/webhooks/webhooks.service'; + +@Module({ + controllers: [WebhooksController], + imports: [ + ACLModule, + forwardRef(() => AuthModule), + HttpModule, + ProjectModule, + PrismaModule, + CustomerModule, + WorkflowDefinitionModule, + ], + providers: [ + WorkflowService, + WorkflowRuntimeDataRepository, + EndUserService, + EndUserRepository, + BusinessRepository, + EntityRepository, + FileService, + FileRepository, + WorkflowEventEmitterService, + UserService, + UserRepository, + SalesforceService, + SalesforceIntegrationRepository, + WorkflowTokenService, + WorkflowTokenRepository, + UiDefinitionService, + UiDefinitionRepository, + StorageService, + HookCallbackHandlerService, + FilterService, + FilterRepository, + WebhooksService, + ], + exports: [], +}) +export class WebhooksModule {} diff --git a/services/workflows-service/src/webhooks/webhooks.service.ts b/services/workflows-service/src/webhooks/webhooks.service.ts new file mode 100644 index 0000000000..dd35f12c19 --- /dev/null +++ b/services/workflows-service/src/webhooks/webhooks.service.ts @@ -0,0 +1,50 @@ +import { Injectable } from '@nestjs/common'; +import { IndividualAmlWebhookInput } from '@/webhooks/dtos/individual-aml-webhook-input'; +import { EndUserRepository } from '@/end-user/end-user.repository'; +import { CustomerService } from '@/customer/customer.service'; +import { WorkflowDefinitionService } from '@/workflow-defintion/workflow-definition.service'; +import { WorkflowService } from '@/workflow/workflow.service'; + +@Injectable() +export class WebhooksService { + constructor( + private readonly customerService: CustomerService, + private readonly workflowService: WorkflowService, + private readonly endUserRepository: EndUserRepository, + private readonly workflowDefinitionService: WorkflowDefinitionService, + ) {} + + async handleAmlHit({ entityId }: IndividualAmlWebhookInput) { + const { projectId } = await this.endUserRepository.findByIdUnscoped(entityId, { + select: { + projectId: true, + }, + }); + + const { config } = await this.customerService.getByProjectId(projectId, { + select: { config: true }, + }); + + if (!config?.ongoingWorkflowDefinitionId) { + return; + } + + const { id: workflowDefinitionId } = await this.workflowDefinitionService.getLatestVersion( + config.ongoingWorkflowDefinitionId, + [projectId], + ); + + await this.workflowService.createOrUpdateWorkflowRuntime({ + workflowDefinitionId, + context: { + entity: { + id: entityId, + type: 'individual', + }, + documents: [], + }, + projectIds: [projectId], + currentProjectId: projectId, + }); + } +} diff --git a/services/workflows-service/src/workflow-defintion/workflow-definition.service.ts b/services/workflows-service/src/workflow-defintion/workflow-definition.service.ts index 22dbfe1da5..e573398960 100644 --- a/services/workflows-service/src/workflow-defintion/workflow-definition.service.ts +++ b/services/workflows-service/src/workflow-defintion/workflow-definition.service.ts @@ -80,12 +80,8 @@ export class WorkflowDefinitionService { async getLatestVersion(id: string, projectIds: TProjectIds) { const workflowDefinition = await this.repository.findById(id, {}, projectIds); - const latestVersion = await this.repository.findByLatestVersion( - workflowDefinition.name, - projectIds, - ); - return latestVersion; + return await this.repository.findByLatestVersion(workflowDefinition.name, projectIds); } async getLatestDefinitionWithTransitionSchema( diff --git a/services/workflows-service/src/workflow/workflow.controller.external.ts b/services/workflows-service/src/workflow/workflow.controller.external.ts index 46c96f5d6f..fe4e401cdb 100644 --- a/services/workflows-service/src/workflow/workflow.controller.external.ts +++ b/services/workflows-service/src/workflow/workflow.controller.external.ts @@ -174,6 +174,7 @@ export class WorkflowControllerExternal { const hasSalesforceRecord = Boolean(body.salesforceObjectName) && Boolean(body.salesforceRecordId); + const latestDefinitionVersion = await this.workflowDefinitionService.getLatestVersion( workflowId, projectIds, diff --git a/services/workflows-service/src/workflow/workflow.service.ts b/services/workflows-service/src/workflow/workflow.service.ts index 633c683c57..4872014b21 100644 --- a/services/workflows-service/src/workflow/workflow.service.ts +++ b/services/workflows-service/src/workflow/workflow.service.ts @@ -85,27 +85,8 @@ import { ajv } from '@/common/ajv/ajv.validator'; type TEntityId = string; -export const ResubmissionReason = { - BLURRY_IMAGE: 'BLURRY_IMAGE', - CUT_IMAGE: 'CUT_IMAGE', - UNSUPPORTED_DOCUMENT: 'UNSUPPORTED_DOCUMENT', - DAMAGED_DOCUMENT: 'DAMAGED_DOCUMENT', - EXPIRED_DOCUMENT: 'EXPIRED_DOCUMENT', - COPY_OF_A_COPY: 'COPY_OF_A_COPY', - FACE_IS_UNCLEAR: 'FACE_IS_UNCLEAR', - FACE_IS_NOT_MATCHING: 'FACE_IS_NOT_MATCHING', -} as const; - -export interface WorkflowData { - workflowDefinition: object; - workflowRuntimeData: object; -} - export type TEntityType = 'endUser' | 'business'; -// Discuss model classes location -export type IntentResponse = WorkflowData[]; - // TODO: TEMP (STUB) const policies = { kycSignup: () => { From 8b41f9862779e9e414832e6b90a6f3ba4c86bea3 Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Tue, 27 Feb 2024 00:05:20 +0200 Subject: [PATCH 07/20] chore(package.json): update db:reset:dev:with-data script to use db:reset:dev script before running db:data-migration:migrate fix(prisma): update subproject commit hash in data-migrations feat(prisma): add config column to Customer table feat(app.module): import WebhooksModule feat(zod-schemas): add CustomerConfigSchema and TCustomerConfig types fix(end-user.repository): update import path for PrismaService feat(end-user.service): add missing line break fix(filter.repository): update scopeService.scopeFindFirst method call fix(global.d.ts): add config and subscriptions fields to Customer type feat(webhooks): add AmlWebhookInput and IndividualAmlWebhookInput DTOs feat(webhooks): add webhooks controller, module, and service - Add a new file `webhooks.controller.ts` to handle webhooks related requests. - Add a new file `webhooks.module.ts` to define the webhooks module and its dependencies. - Add a new file `webhooks.service.ts` to handle webhook events. - Implement the `amlHook` method in the `WebhooksController` to handle AML webhook events. - Implement the `handleAmlHit` method in the `WebhooksService` to handle AML_HIT events. - Update the imports and dependencies in the existing files to include the newly added files. The purpose of these changes is to introduce a new feature that allows handling AML webhook events in the application. This feature will enable the application to respond to AML_HIT events and perform necessary actions based on the event data. From 4e95cc44fb7b2dcbbc250fac66c1b22a8ed1322a Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Thu, 29 Feb 2024 11:57:35 +0200 Subject: [PATCH 08/20] feat: more work --- packages/common/src/consts/index.ts | 7 +++++ .../workflows-service/prisma/data-migrations | 2 +- .../src/end-user/end-user.repository.ts | 2 +- .../src/webhooks/webhooks.controller.ts | 2 +- .../src/webhooks/webhooks.service.ts | 30 +++++++++++++++++-- 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/packages/common/src/consts/index.ts b/packages/common/src/consts/index.ts index 8fcbd6a1c3..0dff461cbf 100644 --- a/packages/common/src/consts/index.ts +++ b/packages/common/src/consts/index.ts @@ -8,6 +8,8 @@ export const StateTag = { COLLECTION_FLOW: 'collection_flow', FAILURE: 'failure', DATA_ENRICHMENT: 'data_enrichment', + DISMISSED: 'dismissed', + FLAGGED: 'flagged', } as const; export const StateTags = [ @@ -30,6 +32,8 @@ export const CommonWorkflowEvent = { APPROVE: 'approve', REVISION: 'revision', RESOLVE: 'resolve', + DISMISS: 'dimiss', + FLAG: 'flag', } as const; export const CommonWorkflowStates = { @@ -38,6 +42,8 @@ export const CommonWorkflowStates = { APPROVED: 'approved', RESOLVED: 'resolved', REVISION: 'revision', + DISMISSED: 'dismissed', + FLAGGED: 'flagged', } as const; export type TStateTag = (typeof StateTags)[number]; @@ -49,6 +55,7 @@ export const WorkflowDefinitionVariant = { KYB_WITH_ASSOCIATED_COMPANIES: 'KYB_WITH_ASSOCIATED_COMPANIES', KYC: 'KYC', DEFAULT: 'DEFAULT', + ONGOING: 'ONGOING', } as const; export type TStateTags = typeof StateTags; diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index 479921bb5d..aeecc3cd52 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit 479921bb5daa0b55686a85ea11ad5a5001e7e0c5 +Subproject commit aeecc3cd5291c2d447c1d5e0bb9c14518d9dc3c4 diff --git a/services/workflows-service/src/end-user/end-user.repository.ts b/services/workflows-service/src/end-user/end-user.repository.ts index b75a92b935..c3d96af187 100644 --- a/services/workflows-service/src/end-user/end-user.repository.ts +++ b/services/workflows-service/src/end-user/end-user.repository.ts @@ -50,7 +50,7 @@ export class EndUserRepository { async findByIdUnscoped>( id: string, - args: Prisma.SelectSubset>, + args?: Prisma.SelectSubset>, ) { return await this.prisma.endUser.findFirstOrThrow( this.scopeService.scopeFindFirst({ diff --git a/services/workflows-service/src/webhooks/webhooks.controller.ts b/services/workflows-service/src/webhooks/webhooks.controller.ts index 0852928091..5d7c6eee74 100644 --- a/services/workflows-service/src/webhooks/webhooks.controller.ts +++ b/services/workflows-service/src/webhooks/webhooks.controller.ts @@ -41,7 +41,7 @@ export class WebhooksController { const { eventName } = data; if (eventName === WEBHOOKS.AML_HIT) { - await this.webhooksService.handleAmlHit(data as IndividualAmlWebhookInput); + await this.webhooksService.handleIndividualAmlHit(data as IndividualAmlWebhookInput); } } } catch (error) { diff --git a/services/workflows-service/src/webhooks/webhooks.service.ts b/services/workflows-service/src/webhooks/webhooks.service.ts index dd35f12c19..418ef5640a 100644 --- a/services/workflows-service/src/webhooks/webhooks.service.ts +++ b/services/workflows-service/src/webhooks/webhooks.service.ts @@ -14,10 +14,35 @@ export class WebhooksService { private readonly workflowDefinitionService: WorkflowDefinitionService, ) {} - async handleAmlHit({ entityId }: IndividualAmlWebhookInput) { - const { projectId } = await this.endUserRepository.findByIdUnscoped(entityId, { + async handleIndividualAmlHit({ entityId }: IndividualAmlWebhookInput) { + const { projectId, ...rest } = await this.endUserRepository.findByIdUnscoped(entityId, { select: { + approvalState: true, + stateReason: true, + firstName: true, + lastName: true, + email: true, + phone: true, + country: true, + nationalId: true, + dateOfBirth: true, + additionalInfo: true, + createdAt: true, + updatedAt: true, projectId: true, + businesses: { + select: { + address: true, + country: true, + website: true, + companyName: true, + businessType: true, + approvalState: true, + registrationNumber: true, + dateOfIncorporation: true, + countryOfIncorporation: true, + }, + }, }, }); @@ -38,6 +63,7 @@ export class WebhooksService { workflowDefinitionId, context: { entity: { + ...rest, id: entityId, type: 'individual', }, From 0e32e39146a22216fa214162d5f3f00d7087fb71 Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Thu, 29 Feb 2024 12:50:58 +0200 Subject: [PATCH 09/20] feat: test fix --- services/workflows-service/prisma/data-migrations | 2 +- .../src/business/business.controller.internal.ts | 2 -- .../workflows-service/src/customer/customer.repository.ts | 2 +- .../workflow-definition.service.intg.test.ts | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index aeecc3cd52..440ab4d4ee 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit aeecc3cd5291c2d447c1d5e0bb9c14518d9dc3c4 +Subproject commit 440ab4d4eea1739a5f49a6b7fd662bf958ca5488 diff --git a/services/workflows-service/src/business/business.controller.internal.ts b/services/workflows-service/src/business/business.controller.internal.ts index 02549ea7a8..36248f4f0b 100644 --- a/services/workflows-service/src/business/business.controller.internal.ts +++ b/services/workflows-service/src/business/business.controller.internal.ts @@ -13,7 +13,6 @@ import { isRecordNotFoundError } from '@/prisma/prisma.util'; import type { InputJsonValue, TProjectIds } from '@/types'; import type { JsonValue } from 'type-fest'; import { ProjectIds } from '@/common/decorators/project-ids.decorator'; -import { ProjectScopeService } from '@/project/project-scope.service'; @swagger.ApiTags('internal/businesses') @common.Controller('internal/businesses') @@ -22,7 +21,6 @@ export class BusinessControllerInternal { protected readonly service: BusinessService, @nestAccessControl.InjectRolesBuilder() protected readonly rolesBuilder: nestAccessControl.RolesBuilder, - protected readonly projectScopeService: ProjectScopeService, ) {} @common.Get() diff --git a/services/workflows-service/src/customer/customer.repository.ts b/services/workflows-service/src/customer/customer.repository.ts index b103358b54..02fbbbf171 100644 --- a/services/workflows-service/src/customer/customer.repository.ts +++ b/services/workflows-service/src/customer/customer.repository.ts @@ -1,4 +1,4 @@ -import { PrismaService } from '../prisma/prisma.service'; +import { PrismaService } from '@/prisma/prisma.service'; import { Customer, Prisma } from '@prisma/client'; import { Injectable } from '@nestjs/common'; import { CustomerWithProjects } from '@/types'; diff --git a/services/workflows-service/src/workflow-defintion/workflow-definition.service.intg.test.ts b/services/workflows-service/src/workflow-defintion/workflow-definition.service.intg.test.ts index 816d5541c3..f909205fd5 100644 --- a/services/workflows-service/src/workflow-defintion/workflow-definition.service.intg.test.ts +++ b/services/workflows-service/src/workflow-defintion/workflow-definition.service.intg.test.ts @@ -176,8 +176,8 @@ describe('WorkflowDefinitionService', () => { ); // Assert - const updatedFilter1 = await filterService.getById(filter1.id, {}, [filter1.id]); - const updatedFilter2 = await filterService.getById(filter2.id, {}, [filter2.id]); + const updatedFilter1 = await filterService.getById(filter1.id, {}, [project.id]); + const updatedFilter2 = await filterService.getById(filter2.id, {}, [project.id]); const otherProjectId = await filterService.getById(otherProjectFilter.id, {}, [ otherProjectFilter.projectId, ]); From 9e703c3371231dc3372954eba65687508c835cb5 Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Thu, 29 Feb 2024 12:50:58 +0200 Subject: [PATCH 10/20] chore(workflows-service): update subproject commit hash in data-migrations refactor(business.controller.internal): remove unused import of ProjectScopeService fix(customer.repository): fix import path for PrismaService test(workflow-definition.service.intg.test): update filterService.getById calls to pass correct project id argument --- services/workflows-service/prisma/data-migrations | 2 +- .../src/business/business.controller.internal.ts | 2 -- .../workflows-service/src/customer/customer.repository.ts | 2 +- .../workflow-definition.service.intg.test.ts | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index aeecc3cd52..440ab4d4ee 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit aeecc3cd5291c2d447c1d5e0bb9c14518d9dc3c4 +Subproject commit 440ab4d4eea1739a5f49a6b7fd662bf958ca5488 diff --git a/services/workflows-service/src/business/business.controller.internal.ts b/services/workflows-service/src/business/business.controller.internal.ts index 02549ea7a8..36248f4f0b 100644 --- a/services/workflows-service/src/business/business.controller.internal.ts +++ b/services/workflows-service/src/business/business.controller.internal.ts @@ -13,7 +13,6 @@ import { isRecordNotFoundError } from '@/prisma/prisma.util'; import type { InputJsonValue, TProjectIds } from '@/types'; import type { JsonValue } from 'type-fest'; import { ProjectIds } from '@/common/decorators/project-ids.decorator'; -import { ProjectScopeService } from '@/project/project-scope.service'; @swagger.ApiTags('internal/businesses') @common.Controller('internal/businesses') @@ -22,7 +21,6 @@ export class BusinessControllerInternal { protected readonly service: BusinessService, @nestAccessControl.InjectRolesBuilder() protected readonly rolesBuilder: nestAccessControl.RolesBuilder, - protected readonly projectScopeService: ProjectScopeService, ) {} @common.Get() diff --git a/services/workflows-service/src/customer/customer.repository.ts b/services/workflows-service/src/customer/customer.repository.ts index b103358b54..02fbbbf171 100644 --- a/services/workflows-service/src/customer/customer.repository.ts +++ b/services/workflows-service/src/customer/customer.repository.ts @@ -1,4 +1,4 @@ -import { PrismaService } from '../prisma/prisma.service'; +import { PrismaService } from '@/prisma/prisma.service'; import { Customer, Prisma } from '@prisma/client'; import { Injectable } from '@nestjs/common'; import { CustomerWithProjects } from '@/types'; diff --git a/services/workflows-service/src/workflow-defintion/workflow-definition.service.intg.test.ts b/services/workflows-service/src/workflow-defintion/workflow-definition.service.intg.test.ts index 816d5541c3..f909205fd5 100644 --- a/services/workflows-service/src/workflow-defintion/workflow-definition.service.intg.test.ts +++ b/services/workflows-service/src/workflow-defintion/workflow-definition.service.intg.test.ts @@ -176,8 +176,8 @@ describe('WorkflowDefinitionService', () => { ); // Assert - const updatedFilter1 = await filterService.getById(filter1.id, {}, [filter1.id]); - const updatedFilter2 = await filterService.getById(filter2.id, {}, [filter2.id]); + const updatedFilter1 = await filterService.getById(filter1.id, {}, [project.id]); + const updatedFilter2 = await filterService.getById(filter2.id, {}, [project.id]); const otherProjectId = await filterService.getById(otherProjectFilter.id, {}, [ otherProjectFilter.projectId, ]); From c615ccec373efa14e3b2b6eae8122bf0ced91a1c Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Thu, 29 Feb 2024 12:50:58 +0200 Subject: [PATCH 11/20] chore(workflows-service): update subproject commit hash in data-migrations refactor(business.controller.internal): remove unused import of ProjectScopeService fix(customer.repository): fix import path for PrismaService test(workflow-definition.service.intg.test): update filterService.getById calls to pass correct project id parameter --- services/workflows-service/prisma/data-migrations | 2 +- .../src/business/business.controller.internal.ts | 2 -- .../workflows-service/src/customer/customer.repository.ts | 2 +- .../workflow-definition.service.intg.test.ts | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index aeecc3cd52..440ab4d4ee 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit aeecc3cd5291c2d447c1d5e0bb9c14518d9dc3c4 +Subproject commit 440ab4d4eea1739a5f49a6b7fd662bf958ca5488 diff --git a/services/workflows-service/src/business/business.controller.internal.ts b/services/workflows-service/src/business/business.controller.internal.ts index 02549ea7a8..36248f4f0b 100644 --- a/services/workflows-service/src/business/business.controller.internal.ts +++ b/services/workflows-service/src/business/business.controller.internal.ts @@ -13,7 +13,6 @@ import { isRecordNotFoundError } from '@/prisma/prisma.util'; import type { InputJsonValue, TProjectIds } from '@/types'; import type { JsonValue } from 'type-fest'; import { ProjectIds } from '@/common/decorators/project-ids.decorator'; -import { ProjectScopeService } from '@/project/project-scope.service'; @swagger.ApiTags('internal/businesses') @common.Controller('internal/businesses') @@ -22,7 +21,6 @@ export class BusinessControllerInternal { protected readonly service: BusinessService, @nestAccessControl.InjectRolesBuilder() protected readonly rolesBuilder: nestAccessControl.RolesBuilder, - protected readonly projectScopeService: ProjectScopeService, ) {} @common.Get() diff --git a/services/workflows-service/src/customer/customer.repository.ts b/services/workflows-service/src/customer/customer.repository.ts index b103358b54..02fbbbf171 100644 --- a/services/workflows-service/src/customer/customer.repository.ts +++ b/services/workflows-service/src/customer/customer.repository.ts @@ -1,4 +1,4 @@ -import { PrismaService } from '../prisma/prisma.service'; +import { PrismaService } from '@/prisma/prisma.service'; import { Customer, Prisma } from '@prisma/client'; import { Injectable } from '@nestjs/common'; import { CustomerWithProjects } from '@/types'; diff --git a/services/workflows-service/src/workflow-defintion/workflow-definition.service.intg.test.ts b/services/workflows-service/src/workflow-defintion/workflow-definition.service.intg.test.ts index 816d5541c3..f909205fd5 100644 --- a/services/workflows-service/src/workflow-defintion/workflow-definition.service.intg.test.ts +++ b/services/workflows-service/src/workflow-defintion/workflow-definition.service.intg.test.ts @@ -176,8 +176,8 @@ describe('WorkflowDefinitionService', () => { ); // Assert - const updatedFilter1 = await filterService.getById(filter1.id, {}, [filter1.id]); - const updatedFilter2 = await filterService.getById(filter2.id, {}, [filter2.id]); + const updatedFilter1 = await filterService.getById(filter1.id, {}, [project.id]); + const updatedFilter2 = await filterService.getById(filter2.id, {}, [project.id]); const otherProjectId = await filterService.getById(otherProjectFilter.id, {}, [ otherProjectFilter.projectId, ]); From 07e41798f95f8f56ebec376de25555e6ab13aa78 Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Thu, 29 Feb 2024 12:50:58 +0200 Subject: [PATCH 12/20] chore(workflows-service): update subproject commit hash in data-migrations refactor(business.controller.internal): remove unused import of ProjectScopeService fix(customer.repository): fix import path for PrismaService test(workflow-definition.service.intg.test): update filterService.getById calls to pass correct project id argument From ac610a6e0d70ff2f71b351ba3891754d3fcca070 Mon Sep 17 00:00:00 2001 From: Alon Peretz Date: Thu, 29 Feb 2024 13:14:10 +0200 Subject: [PATCH 13/20] service --- services/workflows-service/prisma/data-migrations | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index d3f4a68eeb..44ebe0abaa 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit d3f4a68eeb1e96e8dadbda831c6b47a070c1ff3b +Subproject commit 44ebe0abaa2e7ea4202c64470709018fa0e92d35 From 3c744aeb884934b40b9e6676c4ded35114165739 Mon Sep 17 00:00:00 2001 From: Alon Peretz Date: Thu, 29 Feb 2024 13:16:41 +0200 Subject: [PATCH 14/20] service --- services/workflows-service/prisma/data-migrations | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index 44ebe0abaa..86a167736a 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit 44ebe0abaa2e7ea4202c64470709018fa0e92d35 +Subproject commit 86a167736ad8559c1c28f5bd26e3134f066d98fb From 104faf2c058b85008d5b285fe9a92e6a82aeedbf Mon Sep 17 00:00:00 2001 From: Omri Levy Date: Thu, 29 Feb 2024 16:00:06 +0200 Subject: [PATCH 15/20] feat(backoffice-v2): added temporary mocks --- .../src/domains/workflows/fetchers.ts | 84 ++++++++++--------- .../pages/Entity/components/Case/consts.ts | 2 + packages/common/.eslintrc.cjs | 3 + packages/common/src/consts/index.ts | 6 ++ .../workflows-service/prisma/data-migrations | 2 +- 5 files changed, 55 insertions(+), 42 deletions(-) diff --git a/apps/backoffice-v2/src/domains/workflows/fetchers.ts b/apps/backoffice-v2/src/domains/workflows/fetchers.ts index 3407b7db74..0288710141 100644 --- a/apps/backoffice-v2/src/domains/workflows/fetchers.ts +++ b/apps/backoffice-v2/src/domains/workflows/fetchers.ts @@ -143,48 +143,50 @@ export const fetchWorkflowById = async ({ }, kyc_session: { kyc_session_1: { - aml: { - hits: [ - { - matchedName: 'John Doe', - dateOfBirth: '1985-04-12', - countries: ['USA'], - matchTypes: ['sanction', 'pep'], - aka: ['J. Doe', 'Johnny D.'], - listingsRelatedToMatch: { - warnings: [ - { - sourceName: 'Global Warning List', - sourceUrl: 'https://globalwarninglist.com/warning/johndoe', - date: '2022-05-01', - }, - ], - sanctions: [ - { - sourceName: 'United Nations Sanctions', - sourceUrl: 'https://un.org/sanctions/johndoe', - date: '2022-06-15', - }, - ], - pep: [ - { - sourceName: 'Political Exposed Persons List', - sourceUrl: 'https://peplist.com/politicians/johndoe', - date: '2022-07-20', - }, - ], - adverseMedia: [ - { - sourceName: 'AdverseMedia News', - sourceUrl: 'https://adversemedianews.com/articles/johndoe', - date: '2022-08-05', - }, - ], + result: { + aml: { + hits: [ + { + matchedName: 'John Doe', + dateOfBirth: '1985-04-12', + countries: ['USA'], + matchTypes: ['sanction', 'pep'], + aka: ['J. Doe', 'Johnny D.'], + listingsRelatedToMatch: { + warnings: [ + { + sourceName: 'Global Warning List', + sourceUrl: 'https://globalwarninglist.com/warning/johndoe', + date: '2022-05-01', + }, + ], + sanctions: [ + { + sourceName: 'United Nations Sanctions', + sourceUrl: 'https://un.org/sanctions/johndoe', + date: '2022-06-15', + }, + ], + pep: [ + { + sourceName: 'Political Exposed Persons List', + sourceUrl: 'https://peplist.com/politicians/johndoe', + date: '2022-07-20', + }, + ], + adverseMedia: [ + { + sourceName: 'AdverseMedia News', + sourceUrl: 'https://adversemedianews.com/articles/johndoe', + date: '2022-08-05', + }, + ], + }, }, - }, - ], - createdAt: '2024-02-28T12:00:00Z', - totalHits: 1, + ], + createdAt: '2024-02-28T12:00:00Z', + totalHits: 1, + }, }, }, }, diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/consts.ts b/apps/backoffice-v2/src/pages/Entity/components/Case/consts.ts index 154d56048e..e17a05d5d8 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/consts.ts +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/consts.ts @@ -10,4 +10,6 @@ export const tagToBadgeData = { [StateTag.PENDING_PROCESS]: { variant: 'warning', text: 'Pending ID Verification' }, [StateTag.FAILURE]: { variant: 'destructive', text: 'Failed' }, [StateTag.DATA_ENRICHMENT]: { variant: 'violet', text: 'Awaiting 3rd Party Data' }, + [StateTag.DISMISSED]: { variant: 'success', text: 'Dismissed' }, + [StateTag.FLAGGED]: { variant: 'success', text: 'Flagged' }, } as const; diff --git a/packages/common/.eslintrc.cjs b/packages/common/.eslintrc.cjs index a830309a0d..8949c95e0c 100644 --- a/packages/common/.eslintrc.cjs +++ b/packages/common/.eslintrc.cjs @@ -1,4 +1,7 @@ /** @type {import('eslint').Linter.Config} */ module.exports = { extends: ['@ballerine/eslint-config'], + parserOptions: { + project: './tsconfig.eslint.json', + }, }; diff --git a/packages/common/src/consts/index.ts b/packages/common/src/consts/index.ts index 2ea8b1be76..8e97e2b822 100644 --- a/packages/common/src/consts/index.ts +++ b/packages/common/src/consts/index.ts @@ -8,6 +8,8 @@ export const StateTag = { COLLECTION_FLOW: 'collection_flow', FAILURE: 'failure', DATA_ENRICHMENT: 'data_enrichment', + DISMISSED: 'dismissed', + FLAGGED: 'flagged', } as const; export const StateTags = [ @@ -30,6 +32,8 @@ export const CommonWorkflowEvent = { APPROVE: 'approve', REVISION: 'revision', RESOLVE: 'resolve', + DISMISS: 'dismiss', + FLAG: 'flag', } as const; export const CommonWorkflowStates = { @@ -38,6 +42,8 @@ export const CommonWorkflowStates = { APPROVED: 'approved', RESOLVED: 'resolved', REVISION: 'revision', + DISMISSED: 'dismissed', + FLAGGED: 'flagged', } as const; export type TStateTag = (typeof StateTags)[number]; diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index 944b6b6407..aeecc3cd52 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit 944b6b6407ae37f7c951021dfece56a3db303fde +Subproject commit aeecc3cd5291c2d447c1d5e0bb9c14518d9dc3c4 From 1ae872b71f880636767ed1811e42b0f06761c3bb Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Thu, 29 Feb 2024 16:14:44 +0200 Subject: [PATCH 16/20] feat: small fixes --- .../src/schemas/documents/default-context-schema.ts | 1 + .../src/webhooks/webhooks.controller.ts | 8 ++++---- .../workflows-service/src/webhooks/webhooks.service.ts | 9 +++++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/common/src/schemas/documents/default-context-schema.ts b/packages/common/src/schemas/documents/default-context-schema.ts index 398e34bc49..623f2080d3 100644 --- a/packages/common/src/schemas/documents/default-context-schema.ts +++ b/packages/common/src/schemas/documents/default-context-schema.ts @@ -16,6 +16,7 @@ const entitySchema = Type.Object( ); export const defaultContextSchema = Type.Object({ + aml: Type.Optional(Type.Unknown()), entity: Type.Union([ Type.Composite([entitySchema, Type.Object({ id: Type.String() })]), Type.Composite([entitySchema, Type.Object({ ballerineEntityId: Type.String() })]), diff --git a/services/workflows-service/src/webhooks/webhooks.controller.ts b/services/workflows-service/src/webhooks/webhooks.controller.ts index 5d7c6eee74..819f0a19cc 100644 --- a/services/workflows-service/src/webhooks/webhooks.controller.ts +++ b/services/workflows-service/src/webhooks/webhooks.controller.ts @@ -8,12 +8,12 @@ import { IndividualAmlWebhookInput } from '@/webhooks/dtos/individual-aml-webhoo import { WebhooksService } from '@/webhooks/webhooks.service'; const WEBHOOKS = { - AML_HIT: 'AML_HIT', + AML_INDIVIDUAL_MONITORING_UPDATE: 'aml.individuals.monitoring.update', } as const; const ENTITY_TYPES = { BUSINESS: 'business', - INDIVIDUALS: 'individuals', + INDIVIDUAL: 'individual', } as const; @swagger.ApiBearerAuth() @@ -37,10 +37,10 @@ export class WebhooksController { @common.Body() data: IndividualAmlWebhookInput, ) { try { - if (entityType === ENTITY_TYPES.INDIVIDUALS) { + if (entityType === ENTITY_TYPES.INDIVIDUAL) { const { eventName } = data; - if (eventName === WEBHOOKS.AML_HIT) { + if (eventName === WEBHOOKS.AML_INDIVIDUAL_MONITORING_UPDATE) { await this.webhooksService.handleIndividualAmlHit(data as IndividualAmlWebhookInput); } } diff --git a/services/workflows-service/src/webhooks/webhooks.service.ts b/services/workflows-service/src/webhooks/webhooks.service.ts index 418ef5640a..b6fbd6c741 100644 --- a/services/workflows-service/src/webhooks/webhooks.service.ts +++ b/services/workflows-service/src/webhooks/webhooks.service.ts @@ -14,7 +14,7 @@ export class WebhooksService { private readonly workflowDefinitionService: WorkflowDefinitionService, ) {} - async handleIndividualAmlHit({ entityId }: IndividualAmlWebhookInput) { + async handleIndividualAmlHit({ entityId, data }: IndividualAmlWebhookInput) { const { projectId, ...rest } = await this.endUserRepository.findByIdUnscoped(entityId, { select: { approvalState: true, @@ -62,9 +62,14 @@ export class WebhooksService { await this.workflowService.createOrUpdateWorkflowRuntime({ workflowDefinitionId, context: { + aml: data, entity: { - ...rest, + data: { + ...rest, + additionalInfo: rest.additionalInfo ?? {}, + }, id: entityId, + ballerineEntityId: entityId, type: 'individual', }, documents: [], From 0dcfc3f516c236bdc70f0eb9054e0d0fd0f25886 Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Thu, 29 Feb 2024 17:05:34 +0200 Subject: [PATCH 17/20] feat: small fix --- services/workflows-service/src/workflow/workflow.service.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/workflows-service/src/workflow/workflow.service.ts b/services/workflows-service/src/workflow/workflow.service.ts index 7dc2a2cc87..e790cec5c0 100644 --- a/services/workflows-service/src/workflow/workflow.service.ts +++ b/services/workflows-service/src/workflow/workflow.service.ts @@ -56,8 +56,6 @@ import { } from '@nestjs/common'; import { ApprovalState, - Business, - EndUser, Prisma, PrismaClient, UiDefinitionContext, @@ -1537,7 +1535,7 @@ export class WorkflowService { { data: { ...entityConnect, - context: contextToInsert, + context: contextToInsert as InputJsonValue, config: merge( existingWorkflowRuntimeData.config, validatedConfig || {}, From 0753ecd80f7f36d95af183342918aba334dc2d9f Mon Sep 17 00:00:00 2001 From: Omri Levy <61207713+Omri-Levy@users.noreply.github.com> Date: Mon, 4 Mar 2024 10:52:27 +0200 Subject: [PATCH 18/20] Backoffice on-going (#2143) * refactor(backoffice-v2): moved mocks position * fix(backoffice-v2): fixed flagged badge variant * refactor(backoffice-v2): removed aml mocks * refactor(*): addressed pr comments --------- Co-authored-by: Alon Peretz --- .../src/domains/workflows/fetchers.ts | 49 ------------------- .../hooks/useAmlBlock/useAmlBlock.tsx | 6 +-- .../variants/BlocksVariant/BlocksVariant.tsx | 10 ++-- .../OngoingBlocks.tsx} | 6 +-- .../useOngoingBlocksLogic.tsx} | 9 +--- .../lib/blocks/variants/variant-checkers.ts | 2 +- .../ActionsVariant/ActionsVariant.tsx | 10 ++-- .../OngoingActions.tsx} | 46 ++++++++--------- .../useOngoingActionsLogic.tsx} | 2 +- .../pages/Entity/components/Case/consts.ts | 2 +- .../workflows-service/prisma/data-migrations | 2 +- 11 files changed, 43 insertions(+), 101 deletions(-) rename apps/backoffice-v2/src/lib/blocks/variants/{OnGoingBlocks/OnGoingBlocks.tsx => OngoingBlocks/OngoingBlocks.tsx} (65%) rename apps/backoffice-v2/src/lib/blocks/variants/{OnGoingBlocks/hooks/useOnGoingBlocksLogic/useOnGoingBlocksLogic.tsx => OngoingBlocks/hooks/useOngoingBlocksLogic/useOngoingBlocksLogic.tsx} (80%) rename apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/{OnGoingActions/OnGoingActions.tsx => OngoingActions/OngoingActions.tsx} (94%) rename apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/{OnGoingActions/hooks/useOnGoingActionsLogic/useOnGoingActionsLogic.tsx => OngoingActions/hooks/useOngoingActionsLogic/useOngoingActionsLogic.tsx} (98%) diff --git a/apps/backoffice-v2/src/domains/workflows/fetchers.ts b/apps/backoffice-v2/src/domains/workflows/fetchers.ts index c3fa5e451f..6fd1b47908 100644 --- a/apps/backoffice-v2/src/domains/workflows/fetchers.ts +++ b/apps/backoffice-v2/src/domains/workflows/fetchers.ts @@ -141,55 +141,6 @@ export const fetchWorkflowById = async ({ ...data.context?.pluginsOutput?.website_monitoring, data: deepCamelKeys(data.context?.pluginsOutput?.website_monitoring?.data ?? {}), }, - kyc_session: { - kyc_session_1: { - result: { - aml: { - hits: [ - { - matchedName: 'John Doe', - dateOfBirth: '1985-04-12', - countries: ['USA'], - matchTypes: ['sanction', 'pep'], - aka: ['J. Doe', 'Johnny D.'], - listingsRelatedToMatch: { - warnings: [ - { - sourceName: 'Global Warning List', - sourceUrl: 'https://globalwarninglist.com/warning/johndoe', - date: '2022-05-01', - }, - ], - sanctions: [ - { - sourceName: 'United Nations Sanctions', - sourceUrl: 'https://un.org/sanctions/johndoe', - date: '2022-06-15', - }, - ], - pep: [ - { - sourceName: 'Political Exposed Persons List', - sourceUrl: 'https://peplist.com/politicians/johndoe', - date: '2022-07-20', - }, - ], - adverseMedia: [ - { - sourceName: 'AdverseMedia News', - sourceUrl: 'https://adversemedianews.com/articles/johndoe', - date: '2022-08-05', - }, - ], - }, - }, - ], - createdAt: '2024-02-28T12:00:00Z', - totalHits: 1, - }, - }, - }, - }, }, }, })), diff --git a/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx b/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx index 29bab3844c..941e739501 100644 --- a/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx +++ b/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx @@ -19,11 +19,7 @@ export const useAmlBlock = ({ return true; } - return safeEvery(sessionKeys, key => { - const aml = getAmlData(key); - - return !aml; - }); + return safeEvery(sessionKeys, key => !getAmlData(key)); }, [getAmlData, sessionKeys]); const amlBlock = useMemo(() => { if (isAmlEmpty) { diff --git a/apps/backoffice-v2/src/lib/blocks/variants/BlocksVariant/BlocksVariant.tsx b/apps/backoffice-v2/src/lib/blocks/variants/BlocksVariant/BlocksVariant.tsx index e51e1a27b9..f0d16d2767 100644 --- a/apps/backoffice-v2/src/lib/blocks/variants/BlocksVariant/BlocksVariant.tsx +++ b/apps/backoffice-v2/src/lib/blocks/variants/BlocksVariant/BlocksVariant.tsx @@ -6,16 +6,16 @@ import { TWorkflowById } from '@/domains/workflows/fetchers'; import { checkIsKybExampleVariant, checkIsManualReviewVariant, - checkIsOnGoingVariant, + checkIsOngoingVariant, } from '@/lib/blocks/variants/variant-checkers'; -import { OnGoingBlocks } from '@/lib/blocks/variants/OnGoingBlocks/OnGoingBlocks'; +import { OngoingBlocks } from '@/lib/blocks/variants/OngoingBlocks/OngoingBlocks'; export const BlocksVariant: FunctionComponent<{ workflowDefinition: Pick; }> = ({ workflowDefinition }) => { const isKybExampleVariant = checkIsKybExampleVariant(workflowDefinition); const isManualReviewVariant = checkIsManualReviewVariant(workflowDefinition); - const isOnGoingVariant = checkIsOnGoingVariant(workflowDefinition); + const isOngoingVariant = checkIsOngoingVariant(workflowDefinition); if (isKybExampleVariant) { return ; @@ -25,8 +25,8 @@ export const BlocksVariant: FunctionComponent<{ return ; } - if (isOnGoingVariant) { - return ; + if (isOngoingVariant) { + return ; } return ; diff --git a/apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/OnGoingBlocks.tsx b/apps/backoffice-v2/src/lib/blocks/variants/OngoingBlocks/OngoingBlocks.tsx similarity index 65% rename from apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/OnGoingBlocks.tsx rename to apps/backoffice-v2/src/lib/blocks/variants/OngoingBlocks/OngoingBlocks.tsx index a12f87fd01..9f5e141231 100644 --- a/apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/OnGoingBlocks.tsx +++ b/apps/backoffice-v2/src/lib/blocks/variants/OngoingBlocks/OngoingBlocks.tsx @@ -1,10 +1,10 @@ import { BlocksComponent } from '@ballerine/blocks'; import { NoBlocks } from '@/lib/blocks/components/NoBlocks/NoBlocks'; import { cells } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; -import { useOnGoingBlocksLogic } from '@/lib/blocks/variants/OnGoingBlocks/hooks/useOnGoingBlocksLogic/useOnGoingBlocksLogic'; +import { useOngoingBlocksLogic } from '@/lib/blocks/variants/OngoingBlocks/hooks/useOngoingBlocksLogic/useOngoingBlocksLogic'; -export const OnGoingBlocks = () => { - const { blocks, isLoading } = useOnGoingBlocksLogic(); +export const OngoingBlocks = () => { + const { blocks, isLoading } = useOngoingBlocksLogic(); return ( <> diff --git a/apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/hooks/useOnGoingBlocksLogic/useOnGoingBlocksLogic.tsx b/apps/backoffice-v2/src/lib/blocks/variants/OngoingBlocks/hooks/useOngoingBlocksLogic/useOngoingBlocksLogic.tsx similarity index 80% rename from apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/hooks/useOnGoingBlocksLogic/useOnGoingBlocksLogic.tsx rename to apps/backoffice-v2/src/lib/blocks/variants/OngoingBlocks/hooks/useOngoingBlocksLogic/useOngoingBlocksLogic.tsx index b447f96367..57456cdfcc 100644 --- a/apps/backoffice-v2/src/lib/blocks/variants/OnGoingBlocks/hooks/useOnGoingBlocksLogic/useOnGoingBlocksLogic.tsx +++ b/apps/backoffice-v2/src/lib/blocks/variants/OngoingBlocks/hooks/useOngoingBlocksLogic/useOngoingBlocksLogic.tsx @@ -5,7 +5,7 @@ import { useCallback, useMemo } from 'react'; import { useAmlBlock } from '@/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock'; import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; -export const useOnGoingBlocksLogic = () => { +export const useOngoingBlocksLogic = () => { const { entityId: workflowId } = useParams(); const filterId = useFilterId(); const { data: workflow, isLoading } = useWorkflowByIdQuery({ @@ -13,12 +13,7 @@ export const useOnGoingBlocksLogic = () => { filterId: filterId ?? '', }); const kycSessionKeys = Object.keys(workflow?.context?.pluginsOutput?.kyc_session ?? {}); - const getAmlData = useCallback( - (key: string) => - workflow?.context?.pluginsOutput?.kyc_session[key]?.result?.vendorResult?.aml ?? - workflow?.context?.pluginsOutput?.kyc_session[key]?.result?.aml, - [workflow?.context?.pluginsOutput?.kyc_session], - ); + const getAmlData = useCallback((key: string) => workflow?.context?.aml, [workflow?.context?.aml]); const amlBlock = useAmlBlock({ sessionKeys: kycSessionKeys, getAmlData, diff --git a/apps/backoffice-v2/src/lib/blocks/variants/variant-checkers.ts b/apps/backoffice-v2/src/lib/blocks/variants/variant-checkers.ts index 68292d8aed..2e6e3cebcb 100644 --- a/apps/backoffice-v2/src/lib/blocks/variants/variant-checkers.ts +++ b/apps/backoffice-v2/src/lib/blocks/variants/variant-checkers.ts @@ -15,7 +15,7 @@ export const checkIsManualReviewVariant = ( workflowDefinition?.variant === WorkflowDefinitionVariant.MANUAL_REVIEW && workflowDefinition?.config?.isLegacyReject; -export const checkIsOnGoingVariant = ( +export const checkIsOngoingVariant = ( workflowDefinition: Pick, ) => workflowDefinition?.version >= 0 && diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/ActionsVariant/ActionsVariant.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/ActionsVariant/ActionsVariant.tsx index 7a8d234fee..e134bc4231 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/ActionsVariant/ActionsVariant.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/ActionsVariant/ActionsVariant.tsx @@ -1,16 +1,16 @@ import { FunctionComponent } from 'react'; import { TWorkflowById } from '@/domains/workflows/fetchers'; -import { checkIsOnGoingVariant } from '@/lib/blocks/variants/variant-checkers'; +import { checkIsOngoingVariant } from '@/lib/blocks/variants/variant-checkers'; import { DefaultActions } from '@/pages/Entity/components/Case/actions-variants/DefaultActions/DefaultActions'; -import { OnGoingActions } from '@/pages/Entity/components/Case/actions-variants/OnGoingActions/OnGoingActions'; +import { OngoingActions } from '@/pages/Entity/components/Case/actions-variants/OngoingActions/OngoingActions'; export const ActionsVariant: FunctionComponent<{ workflowDefinition: Pick; }> = ({ workflowDefinition }) => { - const isOnGoingVariant = checkIsOnGoingVariant(workflowDefinition); + const isOngoingVariant = checkIsOngoingVariant(workflowDefinition); - if (isOnGoingVariant) { - return ; + if (isOngoingVariant) { + return ; } return ; diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/OnGoingActions.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OngoingActions/OngoingActions.tsx similarity index 94% rename from apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/OnGoingActions.tsx rename to apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OngoingActions/OngoingActions.tsx index c4e0e13282..82f6b19dc4 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/OnGoingActions.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OngoingActions/OngoingActions.tsx @@ -10,9 +10,9 @@ import { DialogFooter } from '@/common/components/organisms/Dialog/Dialog.Footer import { DialogClose } from '@radix-ui/react-dialog'; import React from 'react'; -import { useOnGoingActionsLogic } from '@/pages/Entity/components/Case/actions-variants/OnGoingActions/hooks/useOnGoingActionsLogic/useOnGoingActionsLogic'; +import { useOngoingActionsLogic } from '@/pages/Entity/components/Case/actions-variants/OngoingActions/hooks/useOngoingActionsLogic/useOngoingActionsLogic'; -export const OnGoingActions = () => { +export const OngoingActions = () => { const { isLoadingActions, debouncedIsLoadingFlagCase, @@ -21,7 +21,7 @@ export const OnGoingActions = () => { onMutateDismissCase, canDismiss, debouncedIsLoadingDismissCase, - } = useOnGoingActionsLogic(); + } = useOngoingActionsLogic(); return (
@@ -29,20 +29,20 @@ export const OnGoingActions = () => { - Dismissal Confirmation + Flagging Confirmation -

Are you sure you want to dismiss?

+

Are you sure you want to confirm match?

@@ -52,13 +52,13 @@ export const OnGoingActions = () => { Cancel
@@ -69,20 +69,20 @@ export const OnGoingActions = () => { - Flagging Confirmation + Dismissal Confirmation -

Are you sure you want to confirm match?

+

Are you sure you want to dismiss?

@@ -92,13 +92,13 @@ export const OnGoingActions = () => { Cancel diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/hooks/useOnGoingActionsLogic/useOnGoingActionsLogic.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OngoingActions/hooks/useOngoingActionsLogic/useOngoingActionsLogic.tsx similarity index 98% rename from apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/hooks/useOnGoingActionsLogic/useOnGoingActionsLogic.tsx rename to apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OngoingActions/hooks/useOngoingActionsLogic/useOngoingActionsLogic.tsx index dfe83ed18d..b14a9cfafd 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OnGoingActions/hooks/useOnGoingActionsLogic/useOnGoingActionsLogic.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/actions-variants/OngoingActions/hooks/useOngoingActionsLogic/useOngoingActionsLogic.tsx @@ -11,7 +11,7 @@ import { Action } from '@/common/enums'; import { useAuthenticatedUserQuery } from '@/domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; import { useCaseState } from '@/pages/Entity/components/Case/hooks/useCaseState/useCaseState'; -export const useOnGoingActionsLogic = () => { +export const useOngoingActionsLogic = () => { const { entityId } = useParams(); const filterId = useFilterId(); diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/consts.ts b/apps/backoffice-v2/src/pages/Entity/components/Case/consts.ts index e17a05d5d8..6425d3dfc9 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/consts.ts +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/consts.ts @@ -11,5 +11,5 @@ export const tagToBadgeData = { [StateTag.FAILURE]: { variant: 'destructive', text: 'Failed' }, [StateTag.DATA_ENRICHMENT]: { variant: 'violet', text: 'Awaiting 3rd Party Data' }, [StateTag.DISMISSED]: { variant: 'success', text: 'Dismissed' }, - [StateTag.FLAGGED]: { variant: 'success', text: 'Flagged' }, + [StateTag.FLAGGED]: { variant: 'destructive', text: 'Flagged' }, } as const; diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index d3f4a68eeb..aeecc3cd52 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit d3f4a68eeb1e96e8dadbda831c6b47a070c1ff3b +Subproject commit aeecc3cd5291c2d447c1d5e0bb9c14518d9dc3c4 From c3705c686e8d450ed4f6211827e717e08b409e6c Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Mon, 4 Mar 2024 11:17:05 +0200 Subject: [PATCH 19/20] Ongoing Monitoring (#2133) * feat: ts fix * feat: pr comments fix --- .../workflows-service/src/webhooks/webhooks.controller.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/workflows-service/src/webhooks/webhooks.controller.ts b/services/workflows-service/src/webhooks/webhooks.controller.ts index 819f0a19cc..a5010014a5 100644 --- a/services/workflows-service/src/webhooks/webhooks.controller.ts +++ b/services/workflows-service/src/webhooks/webhooks.controller.ts @@ -7,11 +7,11 @@ import { AmlWebhookInput } from './dtos/aml-webhook-input'; import { IndividualAmlWebhookInput } from '@/webhooks/dtos/individual-aml-webhook-input'; import { WebhooksService } from '@/webhooks/webhooks.service'; -const WEBHOOKS = { +const Webhook = { AML_INDIVIDUAL_MONITORING_UPDATE: 'aml.individuals.monitoring.update', } as const; -const ENTITY_TYPES = { +const EntityType = { BUSINESS: 'business', INDIVIDUAL: 'individual', } as const; @@ -37,10 +37,10 @@ export class WebhooksController { @common.Body() data: IndividualAmlWebhookInput, ) { try { - if (entityType === ENTITY_TYPES.INDIVIDUAL) { + if (entityType === EntityType.INDIVIDUAL) { const { eventName } = data; - if (eventName === WEBHOOKS.AML_INDIVIDUAL_MONITORING_UPDATE) { + if (eventName === Webhook.AML_INDIVIDUAL_MONITORING_UPDATE) { await this.webhooksService.handleIndividualAmlHit(data as IndividualAmlWebhookInput); } } From 254a06831acad857b3ed8a459e5e3b55328471a7 Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Tue, 5 Mar 2024 18:15:36 +0200 Subject: [PATCH 20/20] Fixes for AML block (#2152) * feat: ongoing monitoring * chore(package.json): update db:reset:dev:with-data script to use db:reset:dev script before running db:data-migration:migrate fix(prisma): update subproject commit hash in data-migrations feat(prisma): add config column to Customer table feat(app.module): import WebhooksModule feat(zod-schemas): add CustomerConfigSchema and TCustomerConfig types fix(end-user.repository): update import path for PrismaService feat(end-user.service): add missing line break fix(filter.repository): update scopeService.scopeFindFirst method call fix(global.d.ts): add config and subscriptions fields to Customer type feat(webhooks): add AmlWebhookInput and IndividualAmlWebhookInput DTOs feat(webhooks): add webhooks controller, module, and service - Add a new file `webhooks.controller.ts` to handle webhooks related requests. - Add a new file `webhooks.module.ts` to define the webhooks module and its dependencies. - Add a new file `webhooks.service.ts` to handle webhook events. - Implement the `amlHook` method in the `WebhooksController` to handle AML webhook events. - Implement the `handleAmlHit` method in the `WebhooksService` to handle AML_HIT events. - Update the imports and dependencies in the existing files to include the newly added files. The purpose of these changes is to introduce a new feature that allows handling AML webhook events in the application. This feature will enable the application to respond to AML_HIT events and perform necessary actions based on the event data. * refactor(backoffice-v2): moved aml cells to a re-usable hook * refactor(backoffice-v2): added an on-going blocks variant * feat(backoffice-v2): added an actions variant for on-going * chore(package.json): update db:reset:dev:with-data script to use db:reset:dev script before running db:data-migration:migrate chore(package.json): add new migration script for adding a config column to the Customer table chore(schema.prisma): add config field to the Customer model chore(app.module.ts): import and add WebhooksModule to the AppModule chore(zod-schemas.ts): add CustomerConfigSchema and TCustomerConfig types chore(end-user.repository.ts): update import path for PrismaService chore(end-user.repository.ts): add new method findByIdUnscoped to retrieve end user by id without applying project scope chore(end-user.service.ts): add missing line break chore(filter.repository.ts): update scopeService.scopeFindFirst to scopeService.scopeFindFirst with projectIds parameter chore(global.d.ts): add config field to the Customer type chore(webhooks/dtos/aml-webhook-input.ts): create AmlWebhookInput class chore(webhooks/dtos/individual-aml-webhook-input.ts): create IndividualAmlWebhookInput class feat(webhooks): add webhooks controller, module, and service - Add a new file `webhooks.controller.ts` to handle webhooks related requests. - Add a new file `webhooks.module.ts` to define the webhooks module and its dependencies. - Add a new file `webhooks.service.ts` to handle webhook events. - Implement the `amlHook` method in the `WebhooksController` to handle AML webhook events. - Implement the `handleAmlHit` method in the `WebhooksService` to handle AML_HIT events. - Update the imports and dependencies in the existing files to include the newly added files. The purpose of these changes is to introduce a new feature that allows handling AML webhook events in the application. This feature will enable the application to respond to AML_HIT events and perform necessary actions based on the event data. * chore(package.json): update db:reset:dev:with-data script to use db:reset:dev script before running db:data-migration:migrate fix(prisma): update subproject commit hash in data-migrations feat(prisma): add config column to Customer table feat(app.module): import WebhooksModule feat(zod-schemas): add CustomerConfigSchema and TCustomerConfig types fix(end-user.repository): update import path for PrismaService feat(end-user.service): add missing line break fix(filter.repository): update scopeService.scopeFindFirst method call fix(global.d.ts): add config and subscriptions fields to Customer type feat(webhooks): add AmlWebhookInput and IndividualAmlWebhookInput DTOs feat(webhooks): add webhooks controller, module, and service - Add a new file `webhooks.controller.ts` to handle webhooks related requests. - Add a new file `webhooks.module.ts` to define the webhooks module and its dependencies. - Add a new file `webhooks.service.ts` to handle webhook events. - Implement the `amlHook` method in the `WebhooksController` to handle AML webhook events. - Implement the `handleAmlHit` method in the `WebhooksService` to handle AML_HIT events. - Update the imports and dependencies in the existing files to include the newly added files. The purpose of these changes is to introduce a new feature that allows handling AML webhook events in the application. This feature will enable the application to respond to AML_HIT events and perform necessary actions based on the event data. * feat: more work * feat: test fix * chore(workflows-service): update subproject commit hash in data-migrations refactor(business.controller.internal): remove unused import of ProjectScopeService fix(customer.repository): fix import path for PrismaService test(workflow-definition.service.intg.test): update filterService.getById calls to pass correct project id argument * chore(workflows-service): update subproject commit hash in data-migrations refactor(business.controller.internal): remove unused import of ProjectScopeService fix(customer.repository): fix import path for PrismaService test(workflow-definition.service.intg.test): update filterService.getById calls to pass correct project id parameter * chore(workflows-service): update subproject commit hash in data-migrations refactor(business.controller.internal): remove unused import of ProjectScopeService fix(customer.repository): fix import path for PrismaService test(workflow-definition.service.intg.test): update filterService.getById calls to pass correct project id argument * service * service * feat(backoffice-v2): added temporary mocks * feat: small fixes * feat: small fix * Backoffice on-going (#2143) * refactor(backoffice-v2): moved mocks position * fix(backoffice-v2): fixed flagged badge variant * refactor(backoffice-v2): removed aml mocks * refactor(*): addressed pr comments --------- Co-authored-by: Alon Peretz * Ongoing Monitoring (#2133) * feat: ts fix * feat: pr comments fix * feat: fixes for aml block --------- Co-authored-by: Omri Levy Co-authored-by: Alon Peretz Co-authored-by: Omri Levy <61207713+Omri-Levy@users.noreply.github.com> --- apps/backoffice-v2/CHANGELOG.md | 12 +++ apps/backoffice-v2/package.json | 16 +-- .../organisms/Header/Header.Navbar.tsx | 11 +- .../src/domains/workflows/fetchers.ts | 2 + .../hooks/useAmlBlock/useAmlBlock.tsx | 30 ++---- .../components/AmlBlock/utils/aml-adapter.ts | 66 ++++++------ .../hooks/useKycBlock/useKycBlock.tsx | 23 ++-- .../useOngoingBlocksLogic.tsx | 14 +-- apps/kyb-app/CHANGELOG.md | 11 ++ apps/kyb-app/package.json | 14 +-- apps/workflows-dashboard/CHANGELOG.md | 6 ++ apps/workflows-dashboard/package.json | 6 +- examples/headless-example/CHANGELOG.md | 9 ++ examples/headless-example/package.json | 6 +- .../report-generation-example/CHANGELOG.md | 8 ++ .../report-generation-example/package.json | 4 +- packages/blocks/CHANGELOG.md | 8 ++ packages/blocks/package.json | 8 +- packages/common/CHANGELOG.md | 6 ++ packages/common/package.json | 6 +- packages/common/src/consts/index.ts | 2 + packages/config/CHANGELOG.md | 6 ++ packages/config/package.json | 2 +- packages/eslint-config-react/CHANGELOG.md | 8 ++ packages/eslint-config-react/package.json | 4 +- packages/eslint-config/CHANGELOG.md | 6 ++ packages/eslint-config/package.json | 2 +- packages/react-pdf-toolkit/CHANGELOG.md | 9 ++ packages/react-pdf-toolkit/package.json | 6 +- packages/rules-engine/CHANGELOG.md | 6 ++ packages/rules-engine/package.json | 6 +- packages/ui/CHANGELOG.md | 8 ++ packages/ui/package.json | 8 +- packages/workflow-core/CHANGELOG.md | 8 ++ packages/workflow-core/package.json | 8 +- pnpm-lock.yaml | 100 +++++++++--------- sdks/web-ui-sdk/CHANGELOG.md | 8 ++ sdks/web-ui-sdk/package.json | 4 +- sdks/workflow-browser-sdk/CHANGELOG.md | 9 ++ sdks/workflow-browser-sdk/package.json | 10 +- sdks/workflow-node-sdk/CHANGELOG.md | 8 ++ sdks/workflow-node-sdk/package.json | 8 +- services/websocket-service/CHANGELOG.md | 6 ++ services/websocket-service/package.json | 2 +- services/workflows-service/CHANGELOG.md | 10 ++ services/workflows-service/package.json | 12 +-- .../workflows-service/prisma/data-migrations | 2 +- websites/docs/package.json | 6 +- 48 files changed, 338 insertions(+), 202 deletions(-) diff --git a/apps/backoffice-v2/CHANGELOG.md b/apps/backoffice-v2/CHANGELOG.md index 2dbda13d9b..578beb3531 100644 --- a/apps/backoffice-v2/CHANGELOG.md +++ b/apps/backoffice-v2/CHANGELOG.md @@ -1,5 +1,17 @@ # @ballerine/backoffice-v2 +## 0.5.52 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/common@0.7.46 + - @ballerine/blocks@0.1.28 + - @ballerine/ui@0.3.30 + - @ballerine/workflow-browser-sdk@0.5.46 + - @ballerine/workflow-node-sdk@0.5.46 + ## 0.5.51 ### Patch Changes diff --git a/apps/backoffice-v2/package.json b/apps/backoffice-v2/package.json index 2fa8d3b7bf..1950460d6f 100644 --- a/apps/backoffice-v2/package.json +++ b/apps/backoffice-v2/package.json @@ -1,6 +1,6 @@ { "name": "@ballerine/backoffice-v2", - "version": "0.5.51", + "version": "0.5.52", "description": "Ballerine - Backoffice", "homepage": "https://github.com/ballerine-io/ballerine", "repository": { @@ -50,11 +50,11 @@ "preview": "vite preview" }, "dependencies": { - "@ballerine/blocks": "0.1.27", - "@ballerine/common": "0.7.45", - "@ballerine/ui": "^0.3.29", - "@ballerine/workflow-browser-sdk": "0.5.45", - "@ballerine/workflow-node-sdk": "0.5.45", + "@ballerine/blocks": "0.1.28", + "@ballerine/common": "0.7.46", + "@ballerine/ui": "^0.3.30", + "@ballerine/workflow-browser-sdk": "0.5.46", + "@ballerine/workflow-node-sdk": "0.5.46", "@fontsource/inter": "^4.5.15", "@formkit/auto-animate": "1.0.0-beta.5", "@hookform/resolvers": "^3.1.0", @@ -109,8 +109,8 @@ "zod": "^3.22.3" }, "devDependencies": { - "@ballerine/config": "^1.0.6", - "@ballerine/eslint-config-react": "^1.0.7", + "@ballerine/config": "^1.0.7", + "@ballerine/eslint-config-react": "^1.0.8", "@cspell/cspell-types": "^6.31.1", "@faker-js/faker": "^7.6.0", "@playwright/test": "^1.32.1", diff --git a/apps/backoffice-v2/src/common/components/organisms/Header/Header.Navbar.tsx b/apps/backoffice-v2/src/common/components/organisms/Header/Header.Navbar.tsx index f743ac7509..c5218f089f 100644 --- a/apps/backoffice-v2/src/common/components/organisms/Header/Header.Navbar.tsx +++ b/apps/backoffice-v2/src/common/components/organisms/Header/Header.Navbar.tsx @@ -6,6 +6,7 @@ import { Collapsible } from '@/common/components/molecules/Collapsible/Collapsib import { CollapsibleTrigger } from '@/common/components/molecules/Collapsible/Collapsible.Trigger'; import { CollapsibleContent } from '@/common/components/molecules/Collapsible/Collapsible.Content'; import { useNavbarLogic } from '@/common/components/organisms/Header/hooks/useNavbarLogic/useNavbarLogic'; +import { Fragment } from 'react'; /** * @description A nav element which wraps {@link NavItem} components of the app's routes. Supports nested routes. @@ -23,13 +24,9 @@ export const Navbar: FunctionComponent = () => { const isActiveFilterGroup = checkIsActiveFilterGroup(navItem); return ( - <> + {!!navItem.children && ( - + svg]:rotate-0`, @@ -95,7 +92,7 @@ export const Navbar: FunctionComponent = () => { )} - + ); })} diff --git a/apps/backoffice-v2/src/domains/workflows/fetchers.ts b/apps/backoffice-v2/src/domains/workflows/fetchers.ts index 6fd1b47908..9e0e30ae94 100644 --- a/apps/backoffice-v2/src/domains/workflows/fetchers.ts +++ b/apps/backoffice-v2/src/domains/workflows/fetchers.ts @@ -10,6 +10,7 @@ import { zPropertyKey } from '@/lib/zod/utils/z-property-key/z-property-key'; import { IWorkflowId } from './interfaces'; import { getOriginUrl } from '@/common/utils/get-origin-url/get-url-origin'; import { WorkflowDefinitionByIdSchema } from '@/domains/workflow-definitions/fetchers'; +import { AmlSchema } from '@/lib/blocks/components/AmlBlock/utils/aml-adapter'; export const fetchWorkflows = async (params: { filterId: string; @@ -65,6 +66,7 @@ export const BaseWorkflowByIdSchema = z.object({ workflowDefinition: WorkflowDefinitionByIdSchema, createdAt: z.string().datetime(), context: z.object({ + aml: AmlSchema.optional(), documents: z.array(z.any()).default([]), entity: z.record(z.any(), z.any()), parentMachine: ObjectWithIdSchema.extend({ diff --git a/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx b/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx index 941e739501..d97e3c2b92 100644 --- a/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx +++ b/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock.tsx @@ -1,35 +1,19 @@ import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; -import * as React from 'react'; import { ComponentProps, useMemo } from 'react'; import { Badge } from '@ballerine/ui'; import { WarningFilledSvg } from '@/common/components/atoms/icons'; import { buttonVariants } from '@/common/components/atoms/Button/Button'; import { amlAdapter } from '@/lib/blocks/components/AmlBlock/utils/aml-adapter'; -import { safeEvery } from '@ballerine/common'; +import { TWorkflowById } from '@/domains/workflows/fetchers'; -export const useAmlBlock = ({ - sessionKeys, - getAmlData, -}: { - sessionKeys: string[]; - getAmlData: (key: string) => Parameters[0]; -}) => { - const isAmlEmpty = useMemo(() => { - if (!sessionKeys?.length) { - return true; - } - - return safeEvery(sessionKeys, key => !getAmlData(key)); - }, [getAmlData, sessionKeys]); +export const useAmlBlock = (data: Array) => { const amlBlock = useMemo(() => { - if (isAmlEmpty) { + if (!data?.length) { return []; } - return sessionKeys?.flatMap(key => { - const aml = getAmlData(key); - - if (!Object.keys(aml ?? {}).length) return []; + return data.flatMap(aml => { + if (!aml || !Object.keys(aml ?? {}).length) return []; const { totalMatches, fullReport, dateOfCheck, matches } = amlAdapter(aml); @@ -354,9 +338,9 @@ export const useAmlBlock = ({ ) ?? []), ]; }); - }, [getAmlData, isAmlEmpty, sessionKeys]); + }, [data]); - if (isAmlEmpty) { + if (!amlBlock.length) { return []; } diff --git a/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/utils/aml-adapter.ts b/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/utils/aml-adapter.ts index b9c7428a5f..60d3c9c06b 100644 --- a/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/utils/aml-adapter.ts +++ b/apps/backoffice-v2/src/lib/blocks/components/AmlBlock/utils/aml-adapter.ts @@ -1,38 +1,36 @@ -interface IAmlAdapterParams { - hits: Array<{ - matchedName: string; - dateOfBirth: string; - countries: string[]; - matchTypes: string[]; - aka: string[]; - listingsRelatedToMatch: { - warnings: Array<{ - sourceName: string; - sourceUrl: string; - date: string; - }>; - sanctions: Array<{ - sourceName: string; - sourceUrl: string; - date: string; - }>; - pep: Array<{ - sourceName: string; - sourceUrl: string; - date: string; - }>; - adverseMedia: Array<{ - sourceName: string; - sourceUrl: string; - date: string; - }>; - }; - }>; - createdAt: string; - totalHits: number; -} +import { z } from 'zod'; -export const amlAdapter = (aml: IAmlAdapterParams) => { +const SourceInfoSchema = z.object({ + sourceName: z.string().optional().nullable(), + sourceUrl: z.string().optional().nullable(), + date: z.string().optional().nullable(), +}); + +const ListingRelatedToMatchSchema = z.object({ + warnings: z.array(SourceInfoSchema).optional().nullable(), + sanctions: z.array(SourceInfoSchema).optional().nullable(), + pep: z.array(SourceInfoSchema).optional().nullable(), + adverseMedia: z.array(SourceInfoSchema).optional().nullable(), +}); + +const HitSchema = z.object({ + matchedName: z.string().optional().nullable(), + dateOfBirth: z.string().optional().nullable(), + countries: z.array(z.string()).optional().nullable(), + matchTypes: z.array(z.string()).optional().nullable(), + aka: z.array(z.string()).optional().nullable(), + listingsRelatedToMatch: ListingRelatedToMatchSchema.optional().nullable(), +}); + +export const AmlSchema = z.object({ + hits: z.array(HitSchema).optional().nullable(), + createdAt: z.string().optional().nullable(), + totalHits: z.number().optional().nullable(), +}); + +export type TAml = z.output; + +export const amlAdapter = (aml: TAml) => { const { hits, totalHits, createdAt, ...rest } = aml; return { diff --git a/apps/backoffice-v2/src/lib/blocks/components/KycBlock/hooks/useKycBlock/useKycBlock.tsx b/apps/backoffice-v2/src/lib/blocks/components/KycBlock/hooks/useKycBlock/useKycBlock.tsx index f82dee7623..b440851969 100644 --- a/apps/backoffice-v2/src/lib/blocks/components/KycBlock/hooks/useKycBlock/useKycBlock.tsx +++ b/apps/backoffice-v2/src/lib/blocks/components/KycBlock/hooks/useKycBlock/useKycBlock.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { ComponentProps, useCallback } from 'react'; +import { ComponentProps, useCallback, useMemo } from 'react'; import { isObject, StateTag, TStateTags } from '@ballerine/common'; import { TWorkflowById } from '../../../../../../domains/workflows/fetchers'; @@ -98,16 +98,17 @@ export const useKycBlock = ({ ]) ?? [] : []; - const getAmlData = useCallback( - (key: string) => - childWorkflow?.context?.pluginsOutput?.kyc_session[key]?.result?.vendorResult?.aml ?? - childWorkflow?.context?.pluginsOutput?.kyc_session[key]?.result?.aml, - [childWorkflow?.context?.pluginsOutput?.kyc_session], - ); - const amlBlock = useAmlBlock({ - sessionKeys: kycSessionKeys, - getAmlData, - }); + const amlData = useMemo(() => { + if (!kycSessionKeys?.length) { + return []; + } + + return kycSessionKeys.map( + key => kycSessionKeys[key]?.result?.vendorResult?.aml ?? kycSessionKeys[key]?.result?.aml, + ); + }, [kycSessionKeys]); + + const amlBlock = useAmlBlock(amlData); const documentExtractedData = kycSessionKeys?.length ? kycSessionKeys?.map((key, index, collection) => diff --git a/apps/backoffice-v2/src/lib/blocks/variants/OngoingBlocks/hooks/useOngoingBlocksLogic/useOngoingBlocksLogic.tsx b/apps/backoffice-v2/src/lib/blocks/variants/OngoingBlocks/hooks/useOngoingBlocksLogic/useOngoingBlocksLogic.tsx index 57456cdfcc..387b0018ce 100644 --- a/apps/backoffice-v2/src/lib/blocks/variants/OngoingBlocks/hooks/useOngoingBlocksLogic/useOngoingBlocksLogic.tsx +++ b/apps/backoffice-v2/src/lib/blocks/variants/OngoingBlocks/hooks/useOngoingBlocksLogic/useOngoingBlocksLogic.tsx @@ -1,23 +1,23 @@ import { useParams } from 'react-router-dom'; import { useFilterId } from '@/common/hooks/useFilterId/useFilterId'; import { useWorkflowByIdQuery } from '@/domains/workflows/hooks/queries/useWorkflowByIdQuery/useWorkflowByIdQuery'; -import { useCallback, useMemo } from 'react'; +import { useMemo } from 'react'; import { useAmlBlock } from '@/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock'; import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; export const useOngoingBlocksLogic = () => { const { entityId: workflowId } = useParams(); const filterId = useFilterId(); + const { data: workflow, isLoading } = useWorkflowByIdQuery({ workflowId: workflowId ?? '', filterId: filterId ?? '', }); - const kycSessionKeys = Object.keys(workflow?.context?.pluginsOutput?.kyc_session ?? {}); - const getAmlData = useCallback((key: string) => workflow?.context?.aml, [workflow?.context?.aml]); - const amlBlock = useAmlBlock({ - sessionKeys: kycSessionKeys, - getAmlData, - }); + + const amlData = useMemo(() => [workflow?.context?.aml], [workflow?.context?.aml]); + + const amlBlock = useAmlBlock(amlData); + const amlWithContainerBlock = useMemo(() => { if (!amlBlock?.length) { return []; diff --git a/apps/kyb-app/CHANGELOG.md b/apps/kyb-app/CHANGELOG.md index 4fde34d954..82eba9a34f 100644 --- a/apps/kyb-app/CHANGELOG.md +++ b/apps/kyb-app/CHANGELOG.md @@ -1,5 +1,16 @@ # kyb-app +## 0.1.47 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/common@0.7.46 + - @ballerine/blocks@0.1.28 + - @ballerine/ui@0.3.30 + - @ballerine/workflow-browser-sdk@0.5.46 + ## 0.1.46 ### Patch Changes diff --git a/apps/kyb-app/package.json b/apps/kyb-app/package.json index 862edac57a..ed87fd92ec 100644 --- a/apps/kyb-app/package.json +++ b/apps/kyb-app/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/kyb-app", "private": true, - "version": "0.1.46", + "version": "0.1.47", "type": "module", "scripts": { "dev": "vite", @@ -14,10 +14,10 @@ "test:dev": "vitest" }, "dependencies": { - "@ballerine/blocks": "0.1.27", - "@ballerine/common": "^0.7.45", - "@ballerine/ui": "0.3.29", - "@ballerine/workflow-browser-sdk": "0.5.45", + "@ballerine/blocks": "0.1.28", + "@ballerine/common": "^0.7.46", + "@ballerine/ui": "0.3.30", + "@ballerine/workflow-browser-sdk": "0.5.46", "@lukemorales/query-key-factory": "^1.0.3", "@radix-ui/react-icons": "^1.3.0", "@rjsf/core": "^5.9.0", @@ -58,8 +58,8 @@ "zod": "^3.21.4" }, "devDependencies": { - "@ballerine/config": "^1.0.6", - "@ballerine/eslint-config-react": "^1.0.7", + "@ballerine/config": "^1.0.7", + "@ballerine/eslint-config-react": "^1.0.8", "@jest/globals": "^29.7.0", "@sentry/vite-plugin": "^2.9.0", "@testing-library/jest-dom": "^6.1.4", diff --git a/apps/workflows-dashboard/CHANGELOG.md b/apps/workflows-dashboard/CHANGELOG.md index 00f1088b9b..a9bc8f7ee1 100644 --- a/apps/workflows-dashboard/CHANGELOG.md +++ b/apps/workflows-dashboard/CHANGELOG.md @@ -1,5 +1,11 @@ # @ballerine/workflows-dashboard +## 0.1.14 + +### Patch Changes + +- version bump + ## 0.1.13 ### Patch Changes diff --git a/apps/workflows-dashboard/package.json b/apps/workflows-dashboard/package.json index dc3f7f986f..77f0cff8bb 100644 --- a/apps/workflows-dashboard/package.json +++ b/apps/workflows-dashboard/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/workflows-dashboard", "private": false, - "version": "0.1.13", + "version": "0.1.14", "type": "module", "scripts": { "spellcheck": "cspell \"*\"", @@ -53,8 +53,8 @@ "zod": "^3.22.3" }, "devDependencies": { - "@ballerine/config": "^1.0.6", - "@ballerine/eslint-config-react": "^1.0.7", + "@ballerine/config": "^1.0.7", + "@ballerine/eslint-config-react": "^1.0.8", "@cspell/cspell-types": "^6.31.1", "@types/axios": "^0.14.0", "@types/classnames": "^2.3.1", diff --git a/examples/headless-example/CHANGELOG.md b/examples/headless-example/CHANGELOG.md index 0c09bbf54f..aba1686b6d 100644 --- a/examples/headless-example/CHANGELOG.md +++ b/examples/headless-example/CHANGELOG.md @@ -1,5 +1,14 @@ # @ballerine/headless-example +## 0.1.46 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/common@0.7.46 + - @ballerine/workflow-browser-sdk@0.5.46 + ## 0.1.45 ### Patch Changes diff --git a/examples/headless-example/package.json b/examples/headless-example/package.json index 5a7f621448..d72fef67c3 100644 --- a/examples/headless-example/package.json +++ b/examples/headless-example/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/headless-example", "private": true, - "version": "0.1.45", + "version": "0.1.46", "type": "module", "scripts": { "spellcheck": "cspell \"*\"", @@ -34,8 +34,8 @@ "vite": "^4.1.0" }, "dependencies": { - "@ballerine/common": "0.7.45", - "@ballerine/workflow-browser-sdk": "0.5.45", + "@ballerine/common": "0.7.46", + "@ballerine/workflow-browser-sdk": "0.5.46", "@felte/reporter-svelte": "^1.1.5", "@felte/validator-zod": "^1.0.13", "@fontsource/inter": "^4.5.15", diff --git a/examples/report-generation-example/CHANGELOG.md b/examples/report-generation-example/CHANGELOG.md index efa7bc6990..d496d04dd9 100644 --- a/examples/report-generation-example/CHANGELOG.md +++ b/examples/report-generation-example/CHANGELOG.md @@ -1,5 +1,13 @@ # @ballerine/report-generation-example +## 0.0.4 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/react-pdf-toolkit@1.0.15 + ## 0.0.3 ### Patch Changes diff --git a/examples/report-generation-example/package.json b/examples/report-generation-example/package.json index f43f525109..7129261a64 100644 --- a/examples/report-generation-example/package.json +++ b/examples/report-generation-example/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/report-generation-example", "private": false, - "version": "0.0.3", + "version": "0.0.4", "type": "module", "scripts": { "dev": "vite", @@ -10,7 +10,7 @@ "preview": "vite preview" }, "dependencies": { - "@ballerine/react-pdf-toolkit": "^1.0.14", + "@ballerine/react-pdf-toolkit": "^1.0.15", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md index ea28adf4db..05b4996bc8 100644 --- a/packages/blocks/CHANGELOG.md +++ b/packages/blocks/CHANGELOG.md @@ -1,5 +1,13 @@ # @ballerine/blocks +## 0.1.28 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/common@0.7.46 + ## 0.1.27 ### Patch Changes diff --git a/packages/blocks/package.json b/packages/blocks/package.json index b49e0b6019..e517088a77 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -2,7 +2,7 @@ "private": false, "name": "@ballerine/blocks", "author": "Ballerine ", - "version": "0.1.27", + "version": "0.1.28", "description": "blocks", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", @@ -42,8 +42,8 @@ "@babel/preset-env": "7.16.11", "@babel/preset-react": "^7.22.5", "@babel/preset-typescript": "7.16.7", - "@ballerine/eslint-config": "^1.0.6", - "@ballerine/config": "^1.0.6", + "@ballerine/eslint-config": "^1.0.7", + "@ballerine/config": "^1.0.7", "@rollup/plugin-babel": "5.3.1", "@rollup/plugin-commonjs": "^24.0.1", "@rollup/plugin-node-resolve": "13.2.1", @@ -90,6 +90,6 @@ "vitest": "^0.33.0" }, "dependencies": { - "@ballerine/common": "^0.7.43" + "@ballerine/common": "^0.7.46" } } diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md index ceb7ce8fb0..f728105e94 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/common/CHANGELOG.md @@ -1,5 +1,11 @@ # @ballerine/common +## 0.7.46 + +### Patch Changes + +- version bump + ## 0.7.45 ### Patch Changes diff --git a/packages/common/package.json b/packages/common/package.json index 03d2a2ed9c..a6b8b3def2 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -2,7 +2,7 @@ "private": false, "name": "@ballerine/common", "author": "Ballerine ", - "version": "0.7.45", + "version": "0.7.46", "description": "common", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", @@ -38,8 +38,8 @@ "@babel/core": "7.17.9", "@babel/preset-env": "7.16.11", "@babel/preset-typescript": "7.16.7", - "@ballerine/config": "^1.0.6", - "@ballerine/eslint-config": "^1.0.6", + "@ballerine/config": "^1.0.7", + "@ballerine/eslint-config": "^1.0.7", "@cspell/cspell-types": "^6.31.1", "@rollup/plugin-babel": "5.3.1", "@rollup/plugin-commonjs": "^24.0.1", diff --git a/packages/common/src/consts/index.ts b/packages/common/src/consts/index.ts index 7f5f415186..7c2256d2b5 100644 --- a/packages/common/src/consts/index.ts +++ b/packages/common/src/consts/index.ts @@ -26,6 +26,7 @@ export const StateTags = [ ] as const; export const CommonWorkflowEvent = { + START: 'START', CASE_REVIEWED: 'CASE_REVIEWED', RETURN_TO_REVIEW: 'RETURN_TO_REVIEW', RESUBMITTED: 'RESUBMITTED', @@ -38,6 +39,7 @@ export const CommonWorkflowEvent = { } as const; export const CommonWorkflowStates = { + IDLE: 'idle', MANUAL_REVIEW: 'manual_review', REJECTED: 'rejected', APPROVED: 'approved', diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index 00f7dafe14..85db33876a 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,5 +1,11 @@ # @ballerine/config +## 1.0.7 + +### Patch Changes + +- version bump + ## 1.0.6 ### Patch Changes diff --git a/packages/config/package.json b/packages/config/package.json index 1ea21a4131..7f4fc6fa45 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@ballerine/config", - "version": "1.0.6", + "version": "1.0.7", "description": "", "main": "index.js", "scripts": {}, diff --git a/packages/eslint-config-react/CHANGELOG.md b/packages/eslint-config-react/CHANGELOG.md index 7acd4edb81..e845de6691 100644 --- a/packages/eslint-config-react/CHANGELOG.md +++ b/packages/eslint-config-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @ballerine/eslint-config-react +## 1.0.8 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/eslint-config@1.0.7 + ## 1.0.7 ### Patch Changes diff --git a/packages/eslint-config-react/package.json b/packages/eslint-config-react/package.json index 6ece90ce8d..29cdfdc0ce 100644 --- a/packages/eslint-config-react/package.json +++ b/packages/eslint-config-react/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@ballerine/eslint-config-react", - "version": "1.0.7", + "version": "1.0.8", "description": "", "main": "index.js", "scripts": {}, @@ -10,7 +10,7 @@ "license": "ISC", "peerDependencies": { "eslint-plugin-react": "^7.33.2", - "@ballerine/eslint-config": "^1.0.6", + "@ballerine/eslint-config": "^1.0.7", "eslint-plugin-react-hooks": "^4.6.0" } } diff --git a/packages/eslint-config/CHANGELOG.md b/packages/eslint-config/CHANGELOG.md index 88ebb92b56..dac264ef89 100644 --- a/packages/eslint-config/CHANGELOG.md +++ b/packages/eslint-config/CHANGELOG.md @@ -1,5 +1,11 @@ # @ballerine/eslint-config +## 1.0.7 + +### Patch Changes + +- version bump + ## 1.0.6 ### Patch Changes diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 1c0bac2a7d..2f8aa5f138 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@ballerine/eslint-config", - "version": "1.0.6", + "version": "1.0.7", "description": "", "main": "index.js", "scripts": {}, diff --git a/packages/react-pdf-toolkit/CHANGELOG.md b/packages/react-pdf-toolkit/CHANGELOG.md index 8e918916ab..4a81554646 100644 --- a/packages/react-pdf-toolkit/CHANGELOG.md +++ b/packages/react-pdf-toolkit/CHANGELOG.md @@ -1,5 +1,14 @@ # @ballerine/react-pdf-toolkit +## 1.0.15 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/config@1.0.7 + - @ballerine/ui@0.3.30 + ## 1.0.14 ### Patch Changes diff --git a/packages/react-pdf-toolkit/package.json b/packages/react-pdf-toolkit/package.json index 6a519a1cde..493c6e0cfa 100644 --- a/packages/react-pdf-toolkit/package.json +++ b/packages/react-pdf-toolkit/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/react-pdf-toolkit", "private": false, - "version": "1.0.14", + "version": "1.0.15", "types": "./dist/build.d.ts", "main": "./dist/react-pdf-toolkit.js", "module": "./dist/react-pdf-toolkit.mjs", @@ -24,8 +24,8 @@ "preview": "vite preview" }, "dependencies": { - "@ballerine/config": "^1.0.6", - "@ballerine/ui": "0.3.29", + "@ballerine/config": "^1.0.7", + "@ballerine/ui": "0.3.30", "@react-pdf/renderer": "^3.1.14", "@sinclair/typebox": "^0.31.7", "class-variance-authority": "^0.7.0", diff --git a/packages/rules-engine/CHANGELOG.md b/packages/rules-engine/CHANGELOG.md index 7c2b650da8..9d4451c954 100644 --- a/packages/rules-engine/CHANGELOG.md +++ b/packages/rules-engine/CHANGELOG.md @@ -1,5 +1,11 @@ # @ballerine/rules-engine-lib +## 0.4.27 + +### Patch Changes + +- version bump + ## 0.4.26 ### Patch Changes diff --git a/packages/rules-engine/package.json b/packages/rules-engine/package.json index 2a06a2aed9..34a7f5d6d5 100644 --- a/packages/rules-engine/package.json +++ b/packages/rules-engine/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/rules-engine-lib", "author": "Ballerine ", - "version": "0.4.26", + "version": "0.4.27", "description": "rules-engine-lib", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", @@ -34,9 +34,9 @@ "@babel/core": "7.17.9", "@babel/preset-env": "7.16.11", "@babel/preset-typescript": "7.16.7", - "@ballerine/config": "^1.0.6", + "@ballerine/config": "^1.0.7", "@cspell/cspell-types": "^6.31.1", - "@ballerine/eslint-config": "^1.0.6", + "@ballerine/eslint-config": "^1.0.7", "@rollup/plugin-babel": "5.3.1", "@rollup/plugin-commonjs": "^24.0.1", "@rollup/plugin-node-resolve": "13.2.1", diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index c0c0713419..63372fc173 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -1,5 +1,13 @@ # @ballerine/ui +## 0.3.30 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/common@0.7.46 + ## 0.3.29 ### Patch Changes diff --git a/packages/ui/package.json b/packages/ui/package.json index 1d91f1c68e..74f3467629 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/ui", "private": false, - "version": "0.3.29", + "version": "0.3.30", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -26,7 +26,7 @@ "test": "vitest run" }, "dependencies": { - "@ballerine/common": "^0.7.43", + "@ballerine/common": "^0.7.46", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@mui/material": "^5.14.2", @@ -58,8 +58,8 @@ "tailwind-merge": "^1.10.0" }, "devDependencies": { - "@ballerine/config": "^1.0.6", - "@ballerine/eslint-config-react": "^1.0.7", + "@ballerine/config": "^1.0.7", + "@ballerine/eslint-config-react": "^1.0.8", "@cspell/cspell-types": "^6.31.1", "@storybook/addon-essentials": "^7.0.26", "@storybook/addon-interactions": "^7.0.26", diff --git a/packages/workflow-core/CHANGELOG.md b/packages/workflow-core/CHANGELOG.md index ea7a2a707d..b77dd753cb 100644 --- a/packages/workflow-core/CHANGELOG.md +++ b/packages/workflow-core/CHANGELOG.md @@ -1,5 +1,13 @@ # @ballerine/workflow-core +## 0.5.46 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/common@0.7.46 + ## 0.5.45 ### Patch Changes diff --git a/packages/workflow-core/package.json b/packages/workflow-core/package.json index 34317b274d..04e4d86857 100644 --- a/packages/workflow-core/package.json +++ b/packages/workflow-core/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/workflow-core", "author": "Ballerine ", - "version": "0.5.45", + "version": "0.5.46", "description": "workflow-core", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", @@ -31,7 +31,7 @@ "node": ">=12" }, "dependencies": { - "@ballerine/common": "0.7.45", + "@ballerine/common": "0.7.46", "ajv": "^8.12.0", "i18n-iso-countries": "^7.6.0", "jmespath": "^0.16.0", @@ -42,8 +42,8 @@ "@babel/core": "7.17.9", "@babel/preset-env": "7.16.11", "@babel/preset-typescript": "7.16.7", - "@ballerine/config": "^1.0.6", - "@ballerine/eslint-config": "^1.0.6", + "@ballerine/config": "^1.0.7", + "@ballerine/eslint-config": "^1.0.7", "@cspell/cspell-types": "^6.31.1", "@rollup/plugin-babel": "5.3.1", "@rollup/plugin-commonjs": "^24.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68f57fd16d..aec434e279 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,19 +61,19 @@ importers: apps/backoffice-v2: dependencies: '@ballerine/blocks': - specifier: 0.1.27 + specifier: 0.1.28 version: link:../../packages/blocks '@ballerine/common': - specifier: 0.7.45 + specifier: 0.7.46 version: link:../../packages/common '@ballerine/ui': - specifier: ^0.3.29 + specifier: ^0.3.30 version: link:../../packages/ui '@ballerine/workflow-browser-sdk': - specifier: 0.5.45 + specifier: 0.5.46 version: link:../../sdks/workflow-browser-sdk '@ballerine/workflow-node-sdk': - specifier: 0.5.45 + specifier: 0.5.46 version: link:../../sdks/workflow-node-sdk '@fontsource/inter': specifier: ^4.5.15 @@ -233,10 +233,10 @@ importers: version: 3.22.4 devDependencies: '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../../packages/config '@ballerine/eslint-config-react': - specifier: ^1.0.7 + specifier: ^1.0.8 version: link:../../packages/eslint-config-react '@cspell/cspell-types': specifier: ^6.31.1 @@ -374,16 +374,16 @@ importers: apps/kyb-app: dependencies: '@ballerine/blocks': - specifier: 0.1.27 + specifier: 0.1.28 version: link:../../packages/blocks '@ballerine/common': - specifier: ^0.7.45 + specifier: ^0.7.46 version: link:../../packages/common '@ballerine/ui': - specifier: 0.3.29 + specifier: 0.3.30 version: link:../../packages/ui '@ballerine/workflow-browser-sdk': - specifier: 0.5.45 + specifier: 0.5.46 version: link:../../sdks/workflow-browser-sdk '@lukemorales/query-key-factory': specifier: ^1.0.3 @@ -501,10 +501,10 @@ importers: version: 3.22.4 devDependencies: '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../../packages/config '@ballerine/eslint-config-react': - specifier: ^1.0.7 + specifier: ^1.0.8 version: link:../../packages/eslint-config-react '@jest/globals': specifier: ^29.7.0 @@ -715,10 +715,10 @@ importers: version: 3.22.4 devDependencies: '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../../packages/config '@ballerine/eslint-config-react': - specifier: ^1.0.7 + specifier: ^1.0.8 version: link:../../packages/eslint-config-react '@cspell/cspell-types': specifier: ^6.31.1 @@ -805,10 +805,10 @@ importers: examples/headless-example: dependencies: '@ballerine/common': - specifier: 0.7.45 + specifier: 0.7.46 version: link:../../packages/common '@ballerine/workflow-browser-sdk': - specifier: 0.5.45 + specifier: 0.5.46 version: link:../../sdks/workflow-browser-sdk '@felte/reporter-svelte': specifier: ^1.1.5 @@ -902,7 +902,7 @@ importers: examples/report-generation-example: dependencies: '@ballerine/react-pdf-toolkit': - specifier: ^1.0.14 + specifier: ^1.0.15 version: link:../../packages/react-pdf-toolkit react: specifier: ^18.2.0 @@ -945,7 +945,7 @@ importers: packages/blocks: dependencies: '@ballerine/common': - specifier: ^0.7.43 + specifier: ^0.7.46 version: link:../common devDependencies: '@babel/core': @@ -961,10 +961,10 @@ importers: specifier: 7.16.7 version: 7.16.7(@babel/core@7.17.9) '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../config '@ballerine/eslint-config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../eslint-config '@rollup/plugin-babel': specifier: 5.3.1 @@ -1121,10 +1121,10 @@ importers: specifier: 7.16.7 version: 7.16.7(@babel/core@7.17.9) '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../config '@ballerine/eslint-config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../eslint-config '@cspell/cspell-types': specifier: ^6.31.1 @@ -1261,7 +1261,7 @@ importers: packages/eslint-config-react: dependencies: '@ballerine/eslint-config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../eslint-config eslint-plugin-react: specifier: ^7.33.2 @@ -1273,10 +1273,10 @@ importers: packages/react-pdf-toolkit: dependencies: '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../config '@ballerine/ui': - specifier: 0.3.29 + specifier: 0.3.30 version: link:../ui '@react-pdf/renderer': specifier: ^3.1.14 @@ -1356,10 +1356,10 @@ importers: specifier: 7.16.7 version: 7.16.7(@babel/core@7.17.9) '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../config '@ballerine/eslint-config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../eslint-config '@cspell/cspell-types': specifier: ^6.31.1 @@ -1461,7 +1461,7 @@ importers: packages/ui: dependencies: '@ballerine/common': - specifier: ^0.7.43 + specifier: ^0.7.46 version: link:../common '@emotion/react': specifier: ^11.11.1 @@ -1552,10 +1552,10 @@ importers: version: 1.14.0 devDependencies: '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../config '@ballerine/eslint-config-react': - specifier: ^1.0.7 + specifier: ^1.0.8 version: link:../eslint-config-react '@cspell/cspell-types': specifier: ^6.31.1 @@ -1660,7 +1660,7 @@ importers: packages/workflow-core: dependencies: '@ballerine/common': - specifier: 0.7.45 + specifier: 0.7.46 version: link:../common ajv: specifier: ^8.12.0 @@ -1688,10 +1688,10 @@ importers: specifier: 7.16.7 version: 7.16.7(@babel/core@7.17.9) '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../config '@ballerine/eslint-config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../eslint-config '@cspell/cspell-types': specifier: ^6.31.1 @@ -1823,7 +1823,7 @@ importers: sdks/web-ui-sdk: dependencies: '@ballerine/common': - specifier: 0.7.45 + specifier: 0.7.46 version: link:../../packages/common '@zerodevx/svelte-toast': specifier: ^0.8.0 @@ -1950,10 +1950,10 @@ importers: sdks/workflow-browser-sdk: dependencies: '@ballerine/common': - specifier: 0.7.45 + specifier: 0.7.46 version: link:../../packages/common '@ballerine/workflow-core': - specifier: 0.5.45 + specifier: 0.5.46 version: link:../../packages/workflow-core xstate: specifier: ^4.37.0 @@ -1969,10 +1969,10 @@ importers: specifier: 7.16.7 version: 7.16.7(@babel/core@7.17.9) '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../../packages/config '@ballerine/eslint-config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../../packages/eslint-config '@cspell/cspell-types': specifier: ^6.31.1 @@ -2092,7 +2092,7 @@ importers: sdks/workflow-node-sdk: dependencies: '@ballerine/workflow-core': - specifier: 0.5.45 + specifier: 0.5.46 version: link:../../packages/workflow-core json-logic-js: specifier: ^2.0.2 @@ -2111,10 +2111,10 @@ importers: specifier: 7.16.7 version: 7.16.7(@babel/core@7.17.9) '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../../packages/config '@ballerine/eslint-config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../../packages/eslint-config '@cspell/cspell-types': specifier: ^6.31.1 @@ -2334,13 +2334,13 @@ importers: specifier: 3.347.1 version: 3.347.1 '@ballerine/common': - specifier: 0.7.45 + specifier: 0.7.46 version: link:../../packages/common '@ballerine/workflow-core': - specifier: 0.5.45 + specifier: 0.5.46 version: link:../../packages/workflow-core '@ballerine/workflow-node-sdk': - specifier: 0.5.45 + specifier: 0.5.46 version: link:../../sdks/workflow-node-sdk '@faker-js/faker': specifier: ^7.6.0 @@ -2509,10 +2509,10 @@ importers: version: 3.22.4 devDependencies: '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../../packages/config '@ballerine/eslint-config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../../packages/eslint-config '@cspell/cspell-types': specifier: ^6.31.1 @@ -2653,7 +2653,7 @@ importers: specifier: ^4.0.0 version: 4.0.0(astro@3.3.3)(tailwindcss@3.3.5)(ts-node@10.9.1) '@ballerine/common': - specifier: ^0.7.45 + specifier: ^0.7.46 version: link:../../packages/common astro: specifier: 3.3.3 @@ -2666,10 +2666,10 @@ importers: version: 0.14.5 devDependencies: '@ballerine/config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../../packages/config '@ballerine/eslint-config': - specifier: ^1.0.6 + specifier: ^1.0.7 version: link:../../packages/eslint-config eslint: specifier: ^8.46.0 diff --git a/sdks/web-ui-sdk/CHANGELOG.md b/sdks/web-ui-sdk/CHANGELOG.md index e85dc860d5..eab96566b5 100644 --- a/sdks/web-ui-sdk/CHANGELOG.md +++ b/sdks/web-ui-sdk/CHANGELOG.md @@ -1,5 +1,13 @@ # web-ui-sdk +## 1.4.44 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/common@0.7.46 + ## 1.4.43 ### Patch Changes diff --git a/sdks/web-ui-sdk/package.json b/sdks/web-ui-sdk/package.json index 8d49584d37..6c40164f85 100644 --- a/sdks/web-ui-sdk/package.json +++ b/sdks/web-ui-sdk/package.json @@ -21,7 +21,7 @@ "types": "dist/index.d.ts", "name": "@ballerine/web-ui-sdk", "private": false, - "version": "1.4.43", + "version": "1.4.44", "type": "module", "files": [ "dist" @@ -96,7 +96,7 @@ "vitest": "^0.24.5" }, "dependencies": { - "@ballerine/common": "0.7.45", + "@ballerine/common": "0.7.46", "@zerodevx/svelte-toast": "^0.8.0", "compressorjs": "^1.1.1", "deepmerge": "^4.3.0", diff --git a/sdks/workflow-browser-sdk/CHANGELOG.md b/sdks/workflow-browser-sdk/CHANGELOG.md index 6cc725e960..6693dd71f2 100644 --- a/sdks/workflow-browser-sdk/CHANGELOG.md +++ b/sdks/workflow-browser-sdk/CHANGELOG.md @@ -1,5 +1,14 @@ # @ballerine/workflow-browser-sdk +## 0.5.46 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/common@0.7.46 + - @ballerine/workflow-core@0.5.46 + ## 0.5.45 ### Patch Changes diff --git a/sdks/workflow-browser-sdk/package.json b/sdks/workflow-browser-sdk/package.json index bd1d9cab10..a40c1d160d 100644 --- a/sdks/workflow-browser-sdk/package.json +++ b/sdks/workflow-browser-sdk/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/workflow-browser-sdk", "author": "Ballerine ", - "version": "0.5.45", + "version": "0.5.46", "description": "workflow-browser-sdk", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", @@ -33,17 +33,17 @@ "node": ">=12" }, "dependencies": { - "@ballerine/common": "0.7.45", - "@ballerine/workflow-core": "0.5.45", + "@ballerine/common": "0.7.46", + "@ballerine/workflow-core": "0.5.46", "xstate": "^4.37.0" }, "devDependencies": { "@babel/core": "7.17.9", "@babel/preset-env": "7.16.11", "@babel/preset-typescript": "7.16.7", - "@ballerine/config": "^1.0.6", + "@ballerine/config": "^1.0.7", "@cspell/cspell-types": "^6.31.1", - "@ballerine/eslint-config": "^1.0.6", + "@ballerine/eslint-config": "^1.0.7", "@rollup/plugin-babel": "5.3.1", "@rollup/plugin-commonjs": "^24.0.1", "@rollup/plugin-json": "^6.0.0", diff --git a/sdks/workflow-node-sdk/CHANGELOG.md b/sdks/workflow-node-sdk/CHANGELOG.md index 8ed55b83e3..16ff75b7cc 100644 --- a/sdks/workflow-node-sdk/CHANGELOG.md +++ b/sdks/workflow-node-sdk/CHANGELOG.md @@ -1,5 +1,13 @@ # @ballerine/workflow-node-sdk +## 0.5.46 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/workflow-core@0.5.46 + ## 0.5.45 ### Patch Changes diff --git a/sdks/workflow-node-sdk/package.json b/sdks/workflow-node-sdk/package.json index 3fa209ab68..cb944fd7bf 100644 --- a/sdks/workflow-node-sdk/package.json +++ b/sdks/workflow-node-sdk/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/workflow-node-sdk", "author": "Ballerine ", - "version": "0.5.45", + "version": "0.5.46", "description": "workflow-node-sdk", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", @@ -28,7 +28,7 @@ "node": ">=12" }, "dependencies": { - "@ballerine/workflow-core": "0.5.45", + "@ballerine/workflow-core": "0.5.46", "json-logic-js": "^2.0.2", "xstate": "^4.36.0" }, @@ -36,9 +36,9 @@ "@babel/core": "7.17.9", "@babel/preset-env": "7.16.11", "@babel/preset-typescript": "7.16.7", - "@ballerine/config": "^1.0.6", + "@ballerine/config": "^1.0.7", "@cspell/cspell-types": "^6.31.1", - "@ballerine/eslint-config": "^1.0.6", + "@ballerine/eslint-config": "^1.0.7", "@rollup/plugin-babel": "5.3.1", "@rollup/plugin-commonjs": "^24.0.1", "@rollup/plugin-json": "^6.0.0", diff --git a/services/websocket-service/CHANGELOG.md b/services/websocket-service/CHANGELOG.md index 09f261afec..6cd0cdb723 100644 --- a/services/websocket-service/CHANGELOG.md +++ b/services/websocket-service/CHANGELOG.md @@ -1,5 +1,11 @@ # @ballerine/websocket-service +## 0.0.19 + +### Patch Changes + +- version bump + ## 0.0.18 ### Patch Changes diff --git a/services/websocket-service/package.json b/services/websocket-service/package.json index 168d564916..c362ed93b1 100644 --- a/services/websocket-service/package.json +++ b/services/websocket-service/package.json @@ -1,6 +1,6 @@ { "name": "@ballerine/websocket-service", - "version": "0.0.18", + "version": "0.0.19", "description": "websocket-service", "private": false, "scripts": { diff --git a/services/workflows-service/CHANGELOG.md b/services/workflows-service/CHANGELOG.md index 9c7e8fde3d..d79c15ba4f 100644 --- a/services/workflows-service/CHANGELOG.md +++ b/services/workflows-service/CHANGELOG.md @@ -1,5 +1,15 @@ # @ballerine/workflows-service +## 0.5.46 + +### Patch Changes + +- version bump +- Updated dependencies + - @ballerine/common@0.7.46 + - @ballerine/workflow-core@0.5.46 + - @ballerine/workflow-node-sdk@0.5.46 + ## 0.5.45 ### Patch Changes diff --git a/services/workflows-service/package.json b/services/workflows-service/package.json index e4b331fc4b..b271083d36 100644 --- a/services/workflows-service/package.json +++ b/services/workflows-service/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/workflows-service", "private": false, - "version": "0.5.45", + "version": "0.5.46", "description": "workflow-service", "scripts": { "spellcheck": "cspell \"*\"", @@ -42,9 +42,9 @@ "@aws-sdk/client-s3": "3.347.1", "@aws-sdk/lib-storage": "3.347.1", "@aws-sdk/s3-request-presigner": "3.347.1", - "@ballerine/common": "0.7.45", - "@ballerine/workflow-core": "0.5.45", - "@ballerine/workflow-node-sdk": "0.5.45", + "@ballerine/common": "0.7.46", + "@ballerine/workflow-core": "0.5.46", + "@ballerine/workflow-node-sdk": "0.5.46", "@faker-js/faker": "^7.6.0", "@nestjs/axios": "^2.0.0", "@nestjs/common": "^9.3.12", @@ -102,8 +102,8 @@ "zod": "^3.22.3" }, "devDependencies": { - "@ballerine/config": "^1.0.6", - "@ballerine/eslint-config": "^1.0.6", + "@ballerine/config": "^1.0.7", + "@ballerine/eslint-config": "^1.0.7", "@cspell/cspell-types": "^6.31.1", "@nestjs/cli": "9.3.0", "@nestjs/swagger": "6.2.1", diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index aeecc3cd52..fd57f93276 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit aeecc3cd5291c2d447c1d5e0bb9c14518d9dc3c4 +Subproject commit fd57f932761ddb1f87f8fd5ed4a559deab1dc06d diff --git a/websites/docs/package.json b/websites/docs/package.json index 2099532e2b..c8b49fa9f6 100644 --- a/websites/docs/package.json +++ b/websites/docs/package.json @@ -17,14 +17,14 @@ "dependencies": { "@astrojs/starlight": "0.11.1", "@astrojs/tailwind": "^4.0.0", - "@ballerine/common": "^0.7.45", + "@ballerine/common": "^0.7.46", "astro": "3.3.3", "sharp": "^0.32.4", "shiki": "^0.14.3" }, "devDependencies": { - "@ballerine/config": "^1.0.6", - "@ballerine/eslint-config": "^1.0.6", + "@ballerine/config": "^1.0.7", + "@ballerine/eslint-config": "^1.0.7", "eslint": "^8.46.0", "eslint-config-prettier": "^9.0.0", "eslint-config-standard-with-typescript": "^37.0.0",