-
Notifications
You must be signed in to change notification settings - Fork 199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Signup page leftovers #2849
base: dev
Are you sure you want to change the base?
Signup page leftovers #2849
Changes from 7 commits
60e8f29
2e96012
4d7664c
9bfca66
9426d95
01de2c2
0982228
dd2f683
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,94 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { WorkflowTokenRepository } from '@/auth/workflow-token/workflow-token.repository'; | ||
import type { PrismaTransaction, TProjectId } from '@/types'; | ||
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'; | ||
|
||
@Injectable() | ||
export class WorkflowTokenService { | ||
constructor(private readonly workflowTokenRepository: WorkflowTokenRepository) {} | ||
constructor( | ||
private readonly customerService: CustomerService, | ||
private readonly uiDefinitionService: UiDefinitionService, | ||
private readonly workflowTokenRepository: WorkflowTokenRepository, | ||
private readonly workflowRuntimeDataRepository: WorkflowRuntimeDataRepository, | ||
) {} | ||
|
||
async create( | ||
projectId: TProjectId, | ||
data: Parameters<typeof this.workflowTokenRepository.create>[1], | ||
transaction?: PrismaTransaction, | ||
) { | ||
const { workflowRuntimeDataId } = data; | ||
|
||
const existingTokensForWorkflowRuntime = await this.count( | ||
{ where: { workflowRuntimeDataId } }, | ||
projectId, | ||
); | ||
|
||
Comment on lines
+28
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential race condition when checking existing tokens The check for Consider implementing a mutex, database-level constraint, or transaction to ensure that only one token is created per |
||
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<string, unknown> | ||
>, | ||
{ 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); | ||
} | ||
|
||
|
@@ -26,6 +104,13 @@ export class WorkflowTokenService { | |
return await this.workflowTokenRepository.findByTokenWithExpiredUnscoped(token); | ||
} | ||
|
||
async count( | ||
args: Parameters<typeof this.workflowTokenRepository.count>[0], | ||
projectId: TProjectId, | ||
) { | ||
return await this.workflowTokenRepository.count(args, projectId); | ||
} | ||
|
||
async deleteByToken(token: string) { | ||
return await this.workflowTokenRepository.deleteByTokenUnscoped(token); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,12 +37,16 @@ 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; | ||
|
@@ -55,8 +59,6 @@ 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 }, | ||
|
@@ -72,6 +74,11 @@ describe('CollectionFlowSignupController', () => { | |
{ provide: WorkflowEventEmitterService, useValue: { emit: noop } }, | ||
WorkflowService, | ||
EndUserService, | ||
UiDefinitionService, | ||
UiDefinitionRepository, | ||
CustomerService, | ||
ApiKeyService, | ||
ApiKeyRepository, | ||
BusinessReportService, | ||
BusinessRepository, | ||
EntityRepository, | ||
|
@@ -95,6 +102,7 @@ describe('CollectionFlowSignupController', () => { | |
workflowDefinitionRepository = module.get<WorkflowDefinitionRepository>( | ||
WorkflowDefinitionRepository, | ||
); | ||
uiDefinitionRepository = module.get<UiDefinitionRepository>(UiDefinitionRepository); | ||
workflowRuntimeDataRepository = module.get<WorkflowRuntimeDataRepository>( | ||
WorkflowRuntimeDataRepository, | ||
); | ||
|
@@ -124,6 +132,16 @@ describe('CollectionFlowSignupController', () => { | |
}, | ||
}); | ||
|
||
await uiDefinitionRepository.create({ | ||
data: { | ||
uiSchema: {}, | ||
projectId: project.id, | ||
uiContext: 'collection_flow', | ||
name: 'signup-test-ui-definition', | ||
workflowDefinitionId: workflowDefinition.id, | ||
}, | ||
}); | ||
Comment on lines
+135
to
+143
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification UI definition test coverage is incomplete The test file creates a UI definition with an empty schema but doesn't verify its integration with the workflow. This is concerning because:
Suggested improvements:
🔗 Analysis chainEnhance test coverage for UI definition integration While the UI definition setup is added, consider the following improvements:
Let's check if there are any existing UI definition test cases: Example test case to add: it('should include UI definition in the workflow context', async () => {
const response = await request(app.getHttpServer())
.get('/collection-flow/context')
.set('authorization', `Bearer ${workflowRuntimeDataToken.token}`);
expect(response.body.context.uiDefinition).toBeDefined();
expect(response.body.context.uiDefinition.uiSchema).toEqual({});
}); 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Search for UI definition related test cases
rg -l "uiDefinition.*test" --type ts
Length of output: 38 Script: #!/bin/bash
# Let's try a broader search for UI definition tests and related code
rg -l "uiDefinition" --type ts
# Also search for specific UI definition test patterns
rg "describe.*['\"].*UI.*definition" --type ts -A 5
# Search for UI schema related tests
rg "uiSchema.*test" --type ts -A 5
Length of output: 5914 Script: #!/bin/bash
# Let's check the collection flow controller test file for context
rg "uiSchema|uiDefinition" services/workflows-service/src/collection-flow/controllers/collection-flow.no-user.controller.intg.test.ts -B 2 -A 2
# Let's also check if there are any UI definition assertions in the test file
rg "expect.*uiDefinition|expect.*uiSchema" services/workflows-service/src/collection-flow/controllers/collection-flow.no-user.controller.intg.test.ts
# Check the actual controller implementation to understand UI definition usage
rg "uiDefinition|uiSchema" services/workflows-service/src/collection-flow/controllers/collection-flow.no-user.controller.ts -B 2 -A 2
Length of output: 1272 |
||
|
||
const { id: workflowRuntimeDataId } = await workflowRuntimeDataRepository.create({ | ||
data: { | ||
workflowDefinitionId: workflowDefinition.id, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,31 @@ | ||
import { WorkflowTokenRepository } from '@/auth/workflow-token/workflow-token.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'; | ||
|
||
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'; | ||
|
||
@Module({ | ||
providers: [WorkflowTokenRepository, WorkflowTokenService, TokenAuthGuard], | ||
providers: [ | ||
WorkflowTokenRepository, | ||
WorkflowTokenService, | ||
TokenAuthGuard, | ||
CustomerService, | ||
CustomerRepository, | ||
UiDefinitionService, | ||
ProjectScopeService, | ||
UiDefinitionRepository, | ||
WorkflowRuntimeDataRepository, | ||
ApiKeyService, | ||
ApiKeyRepository, | ||
], | ||
exports: [WorkflowTokenRepository, WorkflowTokenService, TokenAuthGuard], | ||
}) | ||
export class TokenAuthModule {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Consider extracting authentication-related functionality into a separate module
Based on the analysis of the codebase, here's how to resolve the circular dependency:
Create a new
AuthenticationModule
that will contain:PasswordService
(it's self-contained and only handles password operations)Restructure the current modules:
AuthModule
should focus on authorization and depend onUserModule
andAuthenticationModule
UserModule
should only depend onAuthenticationModule
for password operationsforwardRef
usageThis separation follows the single responsibility principle and creates a cleaner dependency graph:
🔗 Analysis chain
Consider resolving the circular dependency with UserModule.
While
forwardRef
works as a temporary solution, circular dependencies can lead to maintenance issues and potential bugs. Consider:Let's analyze the dependency cycle:
Let me gather more information about the services and dependencies to better understand the circular dependency structure.
Let me check the specific services and their dependencies to understand what can be extracted.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 4294
Script:
Length of output: 2148
Script:
Length of output: 10268