From fb5e52b8015a1bcd924fbe41ffefdefd1197dde4 Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Sun, 1 Dec 2024 11:30:23 +0200 Subject: [PATCH] Revert "Signup page leftovers (#2849)" This reverts commit 0f7a4d261129b40a5f2fd1809bcf7c856063659d. --- .../documents/workflow/config-schema.ts | 1 + .../build-collection-flow-state.ts | 4 +- .../scripts/workflows/dynamic-ui-workflow.ts | 1 + .../workflows/kyb-kyc-workflow-definition.ts | 1 + ...ose-child-associated-company-definition.ts | 1 + ...yb-with-associated-companies-definition.ts | 1 + .../compose-kyc-child-workflow-definition.ts | 1 + .../workflows-service/src/auth/auth.module.ts | 2 +- .../workflow-token.repository.ts | 12 +- .../workflow-token/workflow-token.service.ts | 89 +-------------- .../collection-flow.service.intg.test.ts | 36 +----- ...ction-flow.no-user.controller.intg.test.ts | 22 +--- .../guards/token-guard/token-auth.module.ts | 29 +---- .../src/workflow/schemas/zod-schemas.ts | 4 + .../workflow-runtime-data.repository.ts | 31 ++--- .../workflow/workflow.controller.external.ts | 59 ++++------ .../src/workflow/workflow.service.ts | 107 ++++++++++++++++++ .../workflow/workflow.service.unit.test.ts | 3 + 18 files changed, 175 insertions(+), 229 deletions(-) diff --git a/packages/common/src/schemas/documents/workflow/config-schema.ts b/packages/common/src/schemas/documents/workflow/config-schema.ts index eb253cdd93..e3f6464bed 100644 --- a/packages/common/src/schemas/documents/workflow/config-schema.ts +++ b/packages/common/src/schemas/documents/workflow/config-schema.ts @@ -72,6 +72,7 @@ export const WorkflowConfigSchema = Type.Object({ availableDocuments: Type.Optional(Type.Array(AvailableDocumentSchema)), callbackResult: Type.Optional(CallbackResultSchema), childCallbackResults: Type.Optional(Type.Array(ChildCallbackResultSchema)), + createCollectionFlowToken: Type.Optional(Type.Boolean()), mainRepresentative: Type.Optional(MainRepresentativeSchema), customerName: Type.Optional(Type.String()), enableManualCreation: Type.Optional(Type.Boolean()), diff --git a/packages/common/src/utils/collection-flow/build-collection-flow-state.ts b/packages/common/src/utils/collection-flow/build-collection-flow-state.ts index c894ed686b..f5ef3a140d 100644 --- a/packages/common/src/utils/collection-flow/build-collection-flow-state.ts +++ b/packages/common/src/utils/collection-flow/build-collection-flow-state.ts @@ -36,9 +36,11 @@ export const buildCollectionFlowState = (inputConfig: TCollectionFlowConfig): TC const config: TCollectionFlow['config'] = initializeConfig(inputConfig); const state: TCollectionFlow['state'] = initializeState(inputConfig); - return { + const collectionFlow: TCollectionFlow = { config, state, additionalInformation: inputConfig.additionalInformation || {}, }; + + return collectionFlow; }; diff --git a/services/workflows-service/scripts/workflows/dynamic-ui-workflow.ts b/services/workflows-service/scripts/workflows/dynamic-ui-workflow.ts index d8c56199e5..fb07c03de1 100644 --- a/services/workflows-service/scripts/workflows/dynamic-ui-workflow.ts +++ b/services/workflows-service/scripts/workflows/dynamic-ui-workflow.ts @@ -229,6 +229,7 @@ export const dynamicUiWorkflowDefinition = { language: 'en', supportedLanguages: ['en', 'cn'], initialEvent: 'START', + createCollectionFlowToken: true, childCallbackResults: [ { definitionId: kycEmailSessionDefinition.name, diff --git a/services/workflows-service/scripts/workflows/kyb-kyc-workflow-definition.ts b/services/workflows-service/scripts/workflows/kyb-kyc-workflow-definition.ts index b149104b96..eaa4d9d6e2 100644 --- a/services/workflows-service/scripts/workflows/kyb-kyc-workflow-definition.ts +++ b/services/workflows-service/scripts/workflows/kyb-kyc-workflow-definition.ts @@ -229,6 +229,7 @@ export const kybKycWorkflowDefinition = { deliverEvent: 'KYC_RESPONDED', }, ], + createCollectionFlowToken: true, }, contextSchema: { type: 'json-schema', diff --git a/services/workflows-service/scripts/workflows/ui-definition/kyb-with-associated-companies/definition/compose-child-associated-company-definition.ts b/services/workflows-service/scripts/workflows/ui-definition/kyb-with-associated-companies/definition/compose-child-associated-company-definition.ts index d87b9dbb9d..5505de7a57 100644 --- a/services/workflows-service/scripts/workflows/ui-definition/kyb-with-associated-companies/definition/compose-child-associated-company-definition.ts +++ b/services/workflows-service/scripts/workflows/ui-definition/kyb-with-associated-companies/definition/compose-child-associated-company-definition.ts @@ -57,6 +57,7 @@ export const composeChildAssociatedCompanyDefinition = ({ apiPlugins: [], }, config: { + createCollectionFlowToken: true, workflowLevelResolution: true, }, projectId: projectId, diff --git a/services/workflows-service/scripts/workflows/ui-definition/kyb-with-associated-companies/definition/compose-kyb-with-associated-companies-definition.ts b/services/workflows-service/scripts/workflows/ui-definition/kyb-with-associated-companies/definition/compose-kyb-with-associated-companies-definition.ts index e6af502529..824031ae18 100644 --- a/services/workflows-service/scripts/workflows/ui-definition/kyb-with-associated-companies/definition/compose-kyb-with-associated-companies-definition.ts +++ b/services/workflows-service/scripts/workflows/ui-definition/kyb-with-associated-companies/definition/compose-kyb-with-associated-companies-definition.ts @@ -188,6 +188,7 @@ export const composeKybWithAssociatedCompaniesDefinition = ({ }, config: { isExample: true, + createCollectionFlowToken: true, workflowLevelResolution: true, childCallbackResults: [ { diff --git a/services/workflows-service/scripts/workflows/ui-definition/kyb-with-associated-companies/definition/compose-kyc-child-workflow-definition.ts b/services/workflows-service/scripts/workflows/ui-definition/kyb-with-associated-companies/definition/compose-kyc-child-workflow-definition.ts index 8eb8d962c7..3f0c5652a2 100644 --- a/services/workflows-service/scripts/workflows/ui-definition/kyb-with-associated-companies/definition/compose-kyc-child-workflow-definition.ts +++ b/services/workflows-service/scripts/workflows/ui-definition/kyb-with-associated-companies/definition/compose-kyc-child-workflow-definition.ts @@ -57,6 +57,7 @@ export const composeKycChildWorkflowDefinition = ({ apiPlugins: [], }, config: { + createCollectionFlowToken: true, workflowLevelResolution: true, isCaseOverviewEnabled: true, }, diff --git a/services/workflows-service/src/auth/auth.module.ts b/services/workflows-service/src/auth/auth.module.ts index 00683203e9..9e32eef26a 100644 --- a/services/workflows-service/src/auth/auth.module.ts +++ b/services/workflows-service/src/auth/auth.module.ts @@ -4,7 +4,7 @@ import { AuthService } from './auth.service'; import { BasicStrategy } from './basic/basic.strategy'; import { PasswordService } from './password/password.service'; // eslint-disable-next-line import/no-cycle -import { UserModule } from '@/user/user.module'; +import { UserModule } from '../user/user.module'; import { LocalStrategy } from '@/auth/local/local.strategy'; import { SessionSerializer } from '@/auth/session-serializer'; import { UserService } from '@/user/user.service'; diff --git a/services/workflows-service/src/auth/workflow-token/workflow-token.repository.ts b/services/workflows-service/src/auth/workflow-token/workflow-token.repository.ts index 9c5c220dcf..07a708a6ac 100644 --- a/services/workflows-service/src/auth/workflow-token/workflow-token.repository.ts +++ b/services/workflows-service/src/auth/workflow-token/workflow-token.repository.ts @@ -1,8 +1,7 @@ -import { Injectable } from '@nestjs/common'; import { Prisma, PrismaClient } from '@prisma/client'; - -import { PrismaService } from '@/prisma/prisma.service'; +import { Injectable } from '@nestjs/common'; import type { PrismaTransaction, TProjectId } from '@/types'; +import { PrismaService } from '@/prisma/prisma.service'; @Injectable() export class WorkflowTokenRepository { @@ -24,13 +23,6 @@ export class WorkflowTokenRepository { }); } - async count(args: Prisma.WorkflowRuntimeDataTokenCountArgs, projectId: TProjectId) { - return await this.prismaService.workflowRuntimeDataToken.count({ - ...args, - where: { ...args.where, projectId }, - }); - } - async findFirstByWorkflowruntimeDataIdUnscoped(workflowRuntimeDataId: string) { return await this.prismaService.workflowRuntimeDataToken.findFirst({ select: { diff --git a/services/workflows-service/src/auth/workflow-token/workflow-token.service.ts b/services/workflows-service/src/auth/workflow-token/workflow-token.service.ts index 6146063c99..4c07dd4ef9 100644 --- a/services/workflows-service/src/auth/workflow-token/workflow-token.service.ts +++ b/services/workflows-service/src/auth/workflow-token/workflow-token.service.ts @@ -1,94 +1,16 @@ import { Injectable } from '@nestjs/common'; import { WorkflowTokenRepository } from '@/auth/workflow-token/workflow-token.repository'; -import type { InputJsonValue, PrismaTransaction, TProjectId } from '@/types'; -import { Prisma, UiDefinitionContext } from '@prisma/client'; -import { buildCollectionFlowState, getOrderedSteps } from '@ballerine/common'; -import { env } from '@/env'; -import { WORKFLOW_FINAL_STATES } from '@/workflow/consts'; -import { UiDefinitionService } from '@/ui-definition/ui-definition.service'; -import { WorkflowRuntimeDataRepository } from '@/workflow/workflow-runtime-data.repository'; -import { CustomerService } from '@/customer/customer.service'; +import type { PrismaTransaction, TProjectId } from '@/types'; @Injectable() export class WorkflowTokenService { - constructor( - private readonly customerService: CustomerService, - private readonly uiDefinitionService: UiDefinitionService, - private readonly workflowTokenRepository: WorkflowTokenRepository, - private readonly workflowRuntimeDataRepository: WorkflowRuntimeDataRepository, - ) {} + constructor(private readonly workflowTokenRepository: WorkflowTokenRepository) {} async create( projectId: TProjectId, data: Parameters[1], transaction?: PrismaTransaction, ) { - const { workflowRuntimeDataId } = data; - - const existingTokensForWorkflowRuntime = await this.count( - { where: { workflowRuntimeDataId } }, - projectId, - ); - - if (existingTokensForWorkflowRuntime === 0) { - const { workflowDefinitionId, context } = await this.workflowRuntimeDataRepository.findById( - workflowRuntimeDataId, - { select: { workflowDefinitionId: true, context: true } }, - [projectId], - ); - - const [uiDefinition, customer] = await Promise.all([ - this.uiDefinitionService.getByWorkflowDefinitionId( - workflowDefinitionId, - UiDefinitionContext.collection_flow, - [projectId], - ), - this.customerService.getByProjectId(projectId), - ]); - - const collectionFlow = buildCollectionFlowState({ - apiUrl: env.APP_API_URL, - steps: uiDefinition?.definition - ? getOrderedSteps( - (uiDefinition?.definition as Prisma.JsonObject)?.definition as Record< - string, - Record - >, - { finalStates: [...WORKFLOW_FINAL_STATES] }, - ).map(stepName => ({ - stateName: stepName, - })) - : [], - additionalInformation: { - customerCompany: customer.displayName, - }, - }); - - const workflowToken = await this.workflowTokenRepository.create(projectId, data, transaction); - - await this.workflowRuntimeDataRepository.updateStateById( - workflowRuntimeDataId, - { - data: { - context: { - ...context, - collectionFlow, - metadata: { - ...(context.metadata ?? {}), - token: workflowToken.token, - collectionFlowUrl: env.COLLECTION_FLOW_URL, - webUiSDKUrl: env.WEB_UI_SDK_URL, - }, - } as InputJsonValue, - projectId, - }, - }, - transaction, - ); - - return workflowToken; - } - return await this.workflowTokenRepository.create(projectId, data, transaction); } @@ -104,13 +26,6 @@ export class WorkflowTokenService { return await this.workflowTokenRepository.findByTokenWithExpiredUnscoped(token); } - async count( - args: Parameters[0], - projectId: TProjectId, - ) { - return await this.workflowTokenRepository.count(args, projectId); - } - async deleteByToken(token: string) { return await this.workflowTokenRepository.deleteByTokenUnscoped(token); } diff --git a/services/workflows-service/src/collection-flow/collection-flow.service.intg.test.ts b/services/workflows-service/src/collection-flow/collection-flow.service.intg.test.ts index 9e66afda41..d29e1e5148 100644 --- a/services/workflows-service/src/collection-flow/collection-flow.service.intg.test.ts +++ b/services/workflows-service/src/collection-flow/collection-flow.service.intg.test.ts @@ -37,7 +37,6 @@ import { Customer, EndUser, PrismaClient, Project } from '@prisma/client'; import { noop } from 'lodash'; import { CollectionFlowService } from './collection-flow.service'; import { MerchantMonitoringClient } from '@/business-report/merchant-monitoring-client'; -import { env } from '@/env'; const deps: Provider[] = [ { @@ -122,7 +121,6 @@ describe('CollectionFlowService', () => { let workflowRuntimeDataRepository: WorkflowRuntimeDataRepository; let customerRepository: CustomerRepository; let endUserRepository: EndUserRepository; - let uiDefinitionRepository: UiDefinitionRepository; let customer: Customer; let project: Project; @@ -161,7 +159,6 @@ describe('CollectionFlowService', () => { ); customerRepository = module.get(CustomerRepository); endUserRepository = module.get(EndUserRepository); - uiDefinitionRepository = module.get(UiDefinitionRepository); }); beforeEach(async () => { @@ -205,16 +202,6 @@ describe('CollectionFlowService', () => { }, }); - await uiDefinitionRepository.create({ - data: { - uiSchema: {}, - projectId: project.id, - name: 'test-ui-definition', - uiContext: 'collection_flow', - workflowDefinitionId: workflowDefinition.id, - }, - }); - const workflowRuntimeData = await workflowRuntimeDataRepository.create({ data: { workflowDefinitionId: workflowDefinition.id, @@ -233,28 +220,7 @@ describe('CollectionFlowService', () => { const context = await collectionFlowService.getCollectionFlowContext(token); - const expectedContext = { - metadata: { - token: token.token, - webUiSDKUrl: env.WEB_UI_SDK_URL, - collectionFlowUrl: env.COLLECTION_FLOW_URL, - }, - collectionFlow: { - state: { - steps: [], - status: 'pending', - currentStep: '', - }, - config: { - apiUrl: env.APP_API_URL, - }, - additionalInformation: { - customerCompany: customer.displayName, - }, - }, - }; - - expect(context.context).toEqual(expectedContext); + expect(context.context).toEqual(workflowContext); expect(context.config).toEqual(workflowConfig); }); }); diff --git a/services/workflows-service/src/collection-flow/controllers/collection-flow.no-user.controller.intg.test.ts b/services/workflows-service/src/collection-flow/controllers/collection-flow.no-user.controller.intg.test.ts index 8ddf1c26a9..04e627d114 100644 --- a/services/workflows-service/src/collection-flow/controllers/collection-flow.no-user.controller.intg.test.ts +++ b/services/workflows-service/src/collection-flow/controllers/collection-flow.no-user.controller.intg.test.ts @@ -37,16 +37,12 @@ import { WorkflowEventEmitterService } from '@/workflow/workflow-event-emitter.s import { WorkflowRuntimeDataRepository } from '@/workflow/workflow-runtime-data.repository'; import { WorkflowService } from '@/workflow/workflow.service'; import { CollectionFlowNoUserController } from './collection-flow.no-user.controller'; -import { UiDefinitionRepository } from '@/ui-definition/ui-definition.repository'; -import { ApiKeyService } from '@/customer/api-key/api-key.service'; -import { ApiKeyRepository } from '@/customer/api-key/api-key.repository'; describe('CollectionFlowSignupController', () => { let app: INestApplication; let prismaClient: PrismaService; let workflowTokenService: WorkflowTokenService; let workflowDefinitionRepository: WorkflowDefinitionRepository; - let uiDefinitionRepository: UiDefinitionRepository; let workflowRuntimeDataRepository: WorkflowRuntimeDataRepository; let customerRepository: CustomerRepository; let endUserRepository: EndUserRepository; @@ -59,6 +55,8 @@ describe('CollectionFlowSignupController', () => { controllers: [CollectionFlowNoUserController], providers: [ { provide: BusinessService, useValue: noop }, + { provide: UiDefinitionService, useValue: noop }, + { provide: CustomerService, useValue: noop }, { provide: FileService, useValue: noop }, { provide: SalesforceService, useValue: noop }, { provide: RiskRuleService, useValue: noop }, @@ -74,11 +72,6 @@ describe('CollectionFlowSignupController', () => { { provide: WorkflowEventEmitterService, useValue: { emit: noop } }, WorkflowService, EndUserService, - UiDefinitionService, - UiDefinitionRepository, - CustomerService, - ApiKeyService, - ApiKeyRepository, BusinessReportService, BusinessRepository, EntityRepository, @@ -102,7 +95,6 @@ describe('CollectionFlowSignupController', () => { workflowDefinitionRepository = module.get( WorkflowDefinitionRepository, ); - uiDefinitionRepository = module.get(UiDefinitionRepository); workflowRuntimeDataRepository = module.get( WorkflowRuntimeDataRepository, ); @@ -132,16 +124,6 @@ describe('CollectionFlowSignupController', () => { }, }); - await uiDefinitionRepository.create({ - data: { - uiSchema: {}, - projectId: project.id, - uiContext: 'collection_flow', - name: 'signup-test-ui-definition', - workflowDefinitionId: workflowDefinition.id, - }, - }); - const { id: workflowRuntimeDataId } = await workflowRuntimeDataRepository.create({ data: { workflowDefinitionId: workflowDefinition.id, diff --git a/services/workflows-service/src/common/guards/token-guard/token-auth.module.ts b/services/workflows-service/src/common/guards/token-guard/token-auth.module.ts index 0816dc5207..3cbe3caa13 100644 --- a/services/workflows-service/src/common/guards/token-guard/token-auth.module.ts +++ b/services/workflows-service/src/common/guards/token-guard/token-auth.module.ts @@ -1,31 +1,10 @@ -import { Module } from '@nestjs/common'; - -import { CustomerService } from '@/customer/customer.service'; -import { ApiKeyService } from '@/customer/api-key/api-key.service'; -import { CustomerRepository } from '@/customer/customer.repository'; -import { ProjectScopeService } from '@/project/project-scope.service'; -import { ApiKeyRepository } from '@/customer/api-key/api-key.repository'; -import { UiDefinitionService } from '@/ui-definition/ui-definition.service'; -import { TokenAuthGuard } from '@/common/guards/token-guard/token-auth.guard'; -import { UiDefinitionRepository } from '@/ui-definition/ui-definition.repository'; -import { WorkflowTokenService } from '@/auth/workflow-token/workflow-token.service'; import { WorkflowTokenRepository } from '@/auth/workflow-token/workflow-token.repository'; -import { WorkflowRuntimeDataRepository } from '@/workflow/workflow-runtime-data.repository'; +import { WorkflowTokenService } from '@/auth/workflow-token/workflow-token.service'; +import { TokenAuthGuard } from '@/common/guards/token-guard/token-auth.guard'; +import { Module } from '@nestjs/common'; @Module({ - providers: [ - WorkflowTokenRepository, - WorkflowTokenService, - TokenAuthGuard, - CustomerService, - CustomerRepository, - UiDefinitionService, - ProjectScopeService, - UiDefinitionRepository, - WorkflowRuntimeDataRepository, - ApiKeyService, - ApiKeyRepository, - ], + providers: [WorkflowTokenRepository, WorkflowTokenService, TokenAuthGuard], exports: [WorkflowTokenRepository, WorkflowTokenService, TokenAuthGuard], }) export class TokenAuthModule {} diff --git a/services/workflows-service/src/workflow/schemas/zod-schemas.ts b/services/workflows-service/src/workflow/schemas/zod-schemas.ts index 0fc2fdbc79..c47d641554 100644 --- a/services/workflows-service/src/workflow/schemas/zod-schemas.ts +++ b/services/workflows-service/src/workflow/schemas/zod-schemas.ts @@ -40,6 +40,10 @@ export const ConfigSchema = z .optional(), ) .optional(), + createCollectionFlowToken: z + .boolean() + .optional() + .describe('Whether to create a collection flow token as part of the workflow'), mainRepresentative: z .object({ fullName: z.string(), diff --git a/services/workflows-service/src/workflow/workflow-runtime-data.repository.ts b/services/workflows-service/src/workflow/workflow-runtime-data.repository.ts index e4b3d83ee1..2c6915a4b0 100644 --- a/services/workflows-service/src/workflow/workflow-runtime-data.repository.ts +++ b/services/workflows-service/src/workflow/workflow-runtime-data.repository.ts @@ -23,13 +23,14 @@ type StateRelatedColumns = 'state' | 'status' | 'context' | 'tags'; @Injectable() export class WorkflowRuntimeDataRepository { constructor( - protected readonly prismaService: PrismaService, + protected readonly prisma: PrismaService, + protected readonly projectScopeService: ProjectScopeService, protected readonly scopeService: ProjectScopeService, ) {} async create( args: Prisma.SelectSubset, - transaction: PrismaTransaction | PrismaClient = this.prismaService, + transaction: PrismaTransaction | PrismaClient = this.prisma, ): Promise { return await transaction.workflowRuntimeData.create({ ...args, @@ -47,7 +48,7 @@ export class WorkflowRuntimeDataRepository { args: Prisma.SelectSubset, projectIds: TProjectIds, ) { - return await this.prismaService.workflowRuntimeData.findMany( + return await this.prisma.workflowRuntimeData.findMany( this.scopeService.scopeFindMany(args, projectIds), ); } @@ -55,13 +56,13 @@ export class WorkflowRuntimeDataRepository { async findManyUnscoped( args: Prisma.SelectSubset, ) { - return await this.prismaService.workflowRuntimeData.findMany(args); + return await this.prisma.workflowRuntimeData.findMany(args); } async findOne( args: Prisma.SelectSubset, projectIds: TProjectIds, - transaction: PrismaTransaction | PrismaClient = this.prismaService, + transaction: PrismaTransaction | PrismaClient = this.prisma, ): Promise { return await transaction.workflowRuntimeData.findFirst( this.scopeService.scopeFindOne(args, projectIds), @@ -85,7 +86,7 @@ export class WorkflowRuntimeDataRepository { id: string, args: Prisma.SelectSubset>, projectIds: TProjectIds, - transaction: PrismaTransaction | PrismaClient = this.prismaService, + transaction: PrismaTransaction | PrismaClient = this.prisma, ): Promise { return await transaction.workflowRuntimeData.findFirstOrThrow( this.scopeService.scopeFindOne(merge(args, { where: { id } }), projectIds), @@ -133,7 +134,7 @@ export class WorkflowRuntimeDataRepository { async findByIdAndLockUnscoped({ id, - transaction = this.prismaService, + transaction = this.prisma, }: { id: string; transaction: PrismaTransaction | PrismaClient; @@ -148,7 +149,7 @@ export class WorkflowRuntimeDataRepository { args: { data: Omit; }, - transaction: PrismaTransaction | PrismaService = this.prismaService, + transaction: PrismaTransaction | PrismaService = this.prisma, ): Promise { return await transaction.workflowRuntimeData.update({ where: { id }, @@ -165,7 +166,7 @@ export class WorkflowRuntimeDataRepository { data: Prisma.WorkflowRuntimeDataUncheckedUpdateInput; include?: Prisma.WorkflowRuntimeDataInclude; }, - transaction: PrismaTransaction = this.prismaService, + transaction: PrismaTransaction, ) { return await transaction.workflowRuntimeData.update({ where: { id }, @@ -181,7 +182,7 @@ export class WorkflowRuntimeDataRepository { projectIds: TProjectIds, ): Promise { const stringifiedConfig = JSON.stringify(newConfig); - const affectedRows = await this.prismaService + const affectedRows = await this.prisma .$executeRaw`UPDATE "WorkflowRuntimeData" SET "config" = jsonb_deep_merge_with_options("config", ${stringifiedConfig}::jsonb, ${arrayMergeOption}) WHERE "id" = ${id} AND "projectId" in (${projectIds?.join( ',', )})`; @@ -199,7 +200,7 @@ export class WorkflowRuntimeDataRepository { args: Prisma.SelectSubset>, projectIds: TProjectIds, ): Promise { - return await this.prismaService.workflowRuntimeData.delete( + return await this.prisma.workflowRuntimeData.delete( this.scopeService.scopeDelete( { where: { id }, @@ -258,7 +259,7 @@ export class WorkflowRuntimeDataRepository { async findContext(id: string, projectIds: TProjectIds) { return ( - await this.prismaService.workflowRuntimeData.findFirstOrThrow( + await this.prisma.workflowRuntimeData.findFirstOrThrow( this.scopeService.scopeFindOne( { where: { id }, @@ -276,7 +277,7 @@ export class WorkflowRuntimeDataRepository { args: Prisma.SelectSubset, projectIds: TProjectIds, ): Promise { - return await this.prismaService.workflowRuntimeData.count( + return await this.prisma.workflowRuntimeData.count( this.scopeService.scopeFindMany(args, projectIds) as any, ); } @@ -285,7 +286,7 @@ export class WorkflowRuntimeDataRepository { args: Prisma.SubsetIntersection, projectIds: TProjectIds, ) { - return await this.prismaService.workflowRuntimeData.groupBy( + return await this.prisma.workflowRuntimeData.groupBy( this.scopeService.scopeGroupBy(args, projectIds), ); } @@ -395,6 +396,6 @@ export class WorkflowRuntimeDataRepository { LIMIT ${take} OFFSET ${skip} `; - return (await this.prismaService.$queryRaw(sql)) as WorkflowRuntimeData[]; + return (await this.prisma.$queryRaw(sql)) as WorkflowRuntimeData[]; } } diff --git a/services/workflows-service/src/workflow/workflow.controller.external.ts b/services/workflows-service/src/workflow/workflow.controller.external.ts index 91a0221221..313e3ba4ec 100644 --- a/services/workflows-service/src/workflow/workflow.controller.external.ts +++ b/services/workflows-service/src/workflow/workflow.controller.external.ts @@ -52,7 +52,7 @@ export const WORKFLOW_TAG = 'Workflows'; @common.Controller('external/workflows') export class WorkflowControllerExternal { constructor( - protected readonly workflowService: WorkflowService, + protected readonly service: WorkflowService, protected readonly normalizeService: HookCallbackHandlerService, private readonly workflowTokenService: WorkflowTokenService, private readonly workflowDefinitionService: WorkflowDefinitionService, @@ -68,7 +68,7 @@ export class WorkflowControllerExternal { @Query() query: GetWorkflowsRuntimeInputDto, @ProjectIds() projectIds: TProjectIds, ): Promise { - const results = await this.workflowService.listRuntimeData( + const results = await this.service.listRuntimeData( { page: query.page, size: query.limit, @@ -89,7 +89,7 @@ export class WorkflowControllerExternal { @common.Param() params: WorkflowDefinitionWhereUniqueInput, @ProjectIds() projectIds: TProjectIds, ) { - return await this.workflowService.getWorkflowDefinitionById( + return await this.service.getWorkflowDefinitionById( params.id, { include: { @@ -186,7 +186,7 @@ export class WorkflowControllerExternal { @common.Param() params: WorkflowDefinitionWhereUniqueInput, @ProjectIds() projectIds: TProjectIds, ): Promise { - const workflowRuntimeData = await this.workflowService.getWorkflowRuntimeDataById( + const workflowRuntimeData = await this.service.getWorkflowRuntimeDataById( params.id, {}, projectIds, @@ -210,11 +210,7 @@ export class WorkflowControllerExternal { @CurrentProject() currentProjectId: TProjectId, ): Promise { try { - return await this.workflowService.updateWorkflowRuntimeData( - params.id, - data, - currentProjectId, - ); + return await this.service.updateWorkflowRuntimeData(params.id, data, currentProjectId); } catch (error) { if (isRecordNotFoundError(error)) { throw new errors.NotFoundException(`No resource was found for ${JSON.stringify(params)}`); @@ -356,7 +352,7 @@ export class WorkflowControllerExternal { projectIds, ); - const actionResult = await this.workflowService.createOrUpdateWorkflowRuntime({ + const actionResult = await this.service.createOrUpdateWorkflowRuntime({ workflowDefinitionId: latestDefinitionVersion.id, context, config, @@ -372,6 +368,7 @@ export class WorkflowControllerExternal { workflowDefinitionId: actionResult[0]?.workflowDefinition.id, workflowRuntimeId: actionResult[0]?.workflowRuntimeData.id, ballerineEntityId: actionResult[0]?.ballerineEntityId, + entities: actionResult[0]?.entities, }); } @@ -381,18 +378,21 @@ export class WorkflowControllerExternal { @common.HttpCode(200) @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) async createCollectionFlowUrl( - @common.Body() { workflowRuntimeDataId }: CreateCollectionFlowUrlDto, + @common.Body() + { workflowRuntimeDataId }: CreateCollectionFlowUrlDto, ) { - const token = await this.workflowTokenService.findFirstByWorkflowruntimeDataIdUnscoped( + const result = await this.workflowTokenService.findFirstByWorkflowruntimeDataIdUnscoped( workflowRuntimeDataId, ); - if (!token) { - throw new NotFoundException(`No token was found for ${workflowRuntimeDataId}`); + if (!result) { + throw new NotFoundException( + `No WorkflowRuntimeDataId was found for ${JSON.stringify(workflowRuntimeDataId)}`, + ); } return { - collectionFlowUrl: `${env.COLLECTION_FLOW_URL}?token=${token.token}`, + collectionFlowUrl: `${env.COLLECTION_FLOW_URL}?token=${result.token}`, }; } @@ -405,16 +405,6 @@ export class WorkflowControllerExternal { @common.Body() { expiry, workflowRuntimeDataId, endUserId }: CreateTokenDto, @CurrentProject() currentProjectId: TProjectId, ) { - try { - await this.workflowService.getWorkflowRuntimeDataById(workflowRuntimeDataId, {}, [ - currentProjectId, - ]); - } catch (e) { - throw new common.BadRequestException( - `No WorkflowRuntimeData was found for ${workflowRuntimeDataId}`, - ); - } - const expiresAt = new Date(Date.now() + (expiry || 30) * 24 * 60 * 60 * 1000); const { token } = await this.workflowTokenService.create(currentProjectId, { @@ -439,7 +429,7 @@ export class WorkflowControllerExternal { @ProjectIds() projectIds: TProjectIds, @CurrentProject() currentProjectId: TProjectId, ): Promise { - return await this.workflowService.event( + return await this.service.event( { ...data, id, @@ -461,7 +451,7 @@ export class WorkflowControllerExternal { @ProjectIds() projectIds: TProjectIds, @CurrentProject() currentProjectId: TProjectId, ) { - return await this.workflowService.event( + return await this.service.event( { ...data, id, @@ -489,7 +479,7 @@ export class WorkflowControllerExternal { @ProjectIds() projectIds: TProjectIds, ) { try { - const context = await this.workflowService.getWorkflowRuntimeDataContext(id, projectIds); + const context = await this.service.getWorkflowRuntimeDataContext(id, projectIds); return { context }; } catch (err) { @@ -514,11 +504,10 @@ export class WorkflowControllerExternal { ): Promise { try { await this.prismaService.$transaction(async transaction => { - const workflowRuntime = - await this.workflowService.getWorkflowRuntimeDataByIdAndLockUnscoped({ - id: params.id, - transaction, - }); + const workflowRuntime = await this.service.getWorkflowRuntimeDataByIdAndLockUnscoped({ + id: params.id, + transaction, + }); const context = await this.normalizeService.handleHookResponse({ workflowRuntime, @@ -529,7 +518,7 @@ export class WorkflowControllerExternal { currentProjectId: workflowRuntime.projectId, }); - await this.workflowService.event( + await this.service.event( { id: params.id, name: BUILT_IN_EVENT.DEEP_MERGE_CONTEXT, @@ -543,7 +532,7 @@ export class WorkflowControllerExternal { transaction, ); - await this.workflowService.event( + await this.service.event( { id: params.id, name: params.event, diff --git a/services/workflows-service/src/workflow/workflow.service.ts b/services/workflows-service/src/workflow/workflow.service.ts index d5b1b686f2..10b0ec69ca 100644 --- a/services/workflows-service/src/workflow/workflow.service.ts +++ b/services/workflows-service/src/workflow/workflow.service.ts @@ -1,4 +1,5 @@ import { WorkflowTokenService } from '@/auth/workflow-token/workflow-token.service'; +import { BusinessReportService } from '@/business-report/business-report.service'; import { BusinessRepository } from '@/business/business.repository'; import { BusinessService } from '@/business/business.service'; import { ajv } from '@/common/ajv/ajv.validator'; @@ -51,9 +52,11 @@ import { } from '@/workflow/workflow-runtime-list-item.model'; import { AnyRecord, + buildCollectionFlowState, CollectionFlowStatusesEnum, DefaultContextSchema, getDocumentId, + getOrderedSteps, isErrorWithMessage, isObject, ProcessStatus, @@ -86,6 +89,7 @@ import { EndUser, Prisma, PrismaClient, + UiDefinitionContext, User, WorkflowDefinition, WorkflowRuntimeData, @@ -96,6 +100,7 @@ import { plainToClass } from 'class-transformer'; import dayjs from 'dayjs'; import { isEqual, merge } from 'lodash'; import mime from 'mime'; +import { WORKFLOW_FINAL_STATES } from './consts'; import { WorkflowDefinitionCreateDto } from './dtos/workflow-definition-create'; import { WorkflowDefinitionFindManyArgs } from './dtos/workflow-definition-find-many-args'; import { WorkflowDefinitionUpdateInput } from './dtos/workflow-definition-update-input'; @@ -135,6 +140,7 @@ export class WorkflowService { protected readonly workflowRuntimeDataRepository: WorkflowRuntimeDataRepository, protected readonly endUserRepository: EndUserRepository, protected readonly endUserService: EndUserService, + protected readonly businessReportService: BusinessReportService, protected readonly businessRepository: BusinessRepository, protected readonly businessService: BusinessService, protected readonly entityRepository: EntityRepository, @@ -1438,6 +1444,12 @@ export class WorkflowService { validatedConfig || {}, ) as InputJsonValue; + const entities: Array<{ + id: string; + type: 'individual' | 'business'; + tags?: Array<'mainRepresentative' | 'UBO'>; + }> = []; + // Creating new workflow if ( !existingWorkflowRuntimeData || @@ -1454,6 +1466,19 @@ export class WorkflowService { currentProjectId, customer.name, ); + let uiDefinition; + + try { + uiDefinition = await this.uiDefinitionService.getByWorkflowDefinitionId( + workflowDefinitionId, + UiDefinitionContext.collection_flow, + projectIds, + ); + } catch (err) { + if (isErrorWithMessage(err)) { + this.logger.warn(err.message); + } + } workflowRuntimeData = await this.workflowRuntimeDataRepository.create( { @@ -1494,6 +1519,87 @@ export class WorkflowService { workflowRuntimeData, }); + let endUserId: string | null = null; + const entityData = + workflowRuntimeData.context.entity?.data?.additionalInfo?.mainRepresentative; + + if (mergedConfig.createCollectionFlowToken) { + if (entityType === 'endUser') { + endUserId = entityId; + entities.push({ type: 'individual', id: entityId }); + } else if (entityData) { + endUserId = await this.__generateEndUserWithBusiness({ + entityType, + workflowRuntimeData, + entityData: entityData, + currentProjectId, + entityId, + position: BusinessPosition.representative, + }); + + entities.push({ + type: 'individual', + id: endUserId, + }); + + entities.push({ type: 'business', id: entityId }); + + if (entityData) { + workflowRuntimeData.context.entity.data.additionalInfo.mainRepresentative.ballerineEntityId = + endUserId; + } + } + + const nowPlus30Days = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000); + const workflowToken = await this.workflowTokenService.create( + currentProjectId, + { + workflowRuntimeDataId: workflowRuntimeData.id, + endUserId: endUserId ?? null, + expiresAt: nowPlus30Days, + }, + transaction, + ); + + const collectionFlow = buildCollectionFlowState({ + apiUrl: env.APP_API_URL, + steps: uiDefinition?.definition + ? getOrderedSteps( + (uiDefinition?.definition as Prisma.JsonObject)?.definition as Record< + string, + Record + >, + { finalStates: [...WORKFLOW_FINAL_STATES] }, + ).map(stepName => ({ + stateName: stepName, + })) + : [], + additionalInformation: { + customerCompany: customer.displayName, + }, + }); + + workflowRuntimeData = await this.workflowRuntimeDataRepository.updateStateById( + workflowRuntimeData.id, + { + data: { + context: { + ...workflowRuntimeData.context, + collectionFlow, + metadata: { + ...(workflowRuntimeData.context.metadata ?? {}), + token: workflowToken.token, + collectionFlowUrl: env.COLLECTION_FLOW_URL, + webUiSDKUrl: env.WEB_UI_SDK_URL, + }, + } as InputJsonValue, + projectId: currentProjectId, + }, + }, + transaction, + ); + } + if (mergedConfig?.initialEvent) { workflowRuntimeData = await this.event( { @@ -1574,6 +1680,7 @@ export class WorkflowService { workflowDefinition, workflowRuntimeData, ballerineEntityId: entityId, + entities, }, ] as const; }); diff --git a/services/workflows-service/src/workflow/workflow.service.unit.test.ts b/services/workflows-service/src/workflow/workflow.service.unit.test.ts index ac70009bb4..17b19e3914 100644 --- a/services/workflows-service/src/workflow/workflow.service.unit.test.ts +++ b/services/workflows-service/src/workflow/workflow.service.unit.test.ts @@ -102,6 +102,7 @@ describe('WorkflowService', () => { let projectScopeService; let businessRepo; let businessService; + let businessReportService; let customerService; let endUserRepo; let entityRepo; @@ -134,6 +135,7 @@ describe('WorkflowService', () => { workflowRuntimeDataRepo = new FakeWorkflowRuntimeDataRepo(); businessRepo = new FakeBusinessRepo(); businessService = new FakeBusinessRepo(); + businessReportService = new FakeBusinessRepo(); endUserRepo = new FakeEndUserRepo(); entityRepo = new FakeEntityRepo(); customerService = new FakeCustomerRepo(); @@ -186,6 +188,7 @@ describe('WorkflowService', () => { workflowDefinitionRepo as any, workflowRuntimeDataRepo, endUserRepo, + businessReportService, {} as any, businessRepo, businessService,