Skip to content

Commit 3af904d

Browse files
MatanYadaevalonp99
andauthored
Expose MM endpoints in the WF service (#2820)
* fix: swagger * refactor(workflow): update type definitions for collection flow - Change type from 'any' to 'unknown' for better type safety - Improve type definition for nested records in workflow steps (Your type definitions are so loose, they could qualify as a pair of sweatpants at a buffet) --------- Co-authored-by: Alon Peretz <[email protected]>
1 parent e548a6b commit 3af904d

File tree

8 files changed

+188
-180
lines changed

8 files changed

+188
-180
lines changed

services/workflows-service/scripts/workflows/runtime/generate-initial-collection-flow-example.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const generateInitialCollectionFlowExample = async (
2727
const collectionFlow = buildCollectionFlowState({
2828
apiUrl: env.APP_API_URL,
2929
steps: getOrderedSteps(
30-
(uiDefinition?.definition as Prisma.JsonObject)?.definition as Record<string, any>,
30+
(uiDefinition?.definition as Prisma.JsonObject)?.definition as Record<string, unknown>,
3131
{ finalStates: [...WORKFLOW_FINAL_STATES] },
3232
).map(stepName => ({
3333
stateName: stepName,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
import { IsOptional, IsString } from 'class-validator';
3+
import { PageDto } from '@/common/dto';
4+
import { z } from 'zod';
5+
import { BusinessReportDto } from '@/business-report/business-report.dto';
6+
7+
export class BusinessReportListRequestParamDto {
8+
@IsOptional()
9+
@IsString()
10+
businessId?: string;
11+
12+
@IsOptional()
13+
@ApiProperty({ type: String, required: false })
14+
search?: string;
15+
16+
@ApiProperty({ type: PageDto })
17+
page!: PageDto;
18+
}
19+
20+
export const ListBusinessReportsSchema = z.object({
21+
search: z.string().optional(),
22+
page: z.object({
23+
number: z.coerce.number().int().positive(),
24+
size: z.coerce.number().int().positive().max(100),
25+
}),
26+
});
27+
28+
export class BusinessReportListResponseDto {
29+
@ApiProperty({ type: Number, example: 20 })
30+
totalItems!: number;
31+
32+
@ApiProperty({ type: Number, example: 1 })
33+
totalPages!: number;
34+
35+
@ApiProperty({ type: [BusinessReportDto] })
36+
data!: BusinessReportDto[];
37+
}

services/workflows-service/src/business-report/business-report.controller.internal.ts

Lines changed: 76 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,15 @@ import { CurrentProject } from '@/common/decorators/current-project.decorator';
1717
import { BusinessReportService } from '@/business-report/business-report.service';
1818
import { GetLatestBusinessReportDto } from '@/business-report/get-latest-business-report.dto';
1919
import {
20-
ListBusinessReportsDto,
20+
BusinessReportListRequestParamDto,
21+
BusinessReportListResponseDto,
2122
ListBusinessReportsSchema,
22-
} from '@/business-report/list-business-reports.dto';
23+
} from '@/business-report/business-report-list.dto';
2324
import { Business } from '@prisma/client';
2425
import { ZodValidationPipe } from '@/common/pipes/zod.pipe';
2526
import { CreateBusinessReportDto } from '@/business-report/dto/create-business-report.dto';
26-
import { HookCallbackHandlerService } from '@/workflow/hook-callback-handler.service';
2727
import { CustomerService } from '@/customer/customer.service';
2828
import { BusinessService } from '@/business/business.service';
29-
import { AlertService } from '@/alert/alert.service';
3029
import { Public } from '@/common/decorators/public.decorator';
3130
import { VerifyUnifiedApiSignatureDecorator } from '@/common/decorators/verify-unified-api-signature.decorator';
3231
import { BusinessReportHookBodyDto } from '@/business-report/dtos/business-report-hook-body.dto';
@@ -37,90 +36,23 @@ import { fileFilter } from '@/storage/file-filter';
3736
import { RemoveTempFileInterceptor } from '@/common/interceptors/remove-temp-file.interceptor';
3837
import { CreateBusinessReportBatchBodyDto } from '@/business-report/dto/create-business-report-batch-body.dto';
3938
import type { Response } from 'express';
39+
import { BusinessReportDto } from '@/business-report/business-report.dto';
4040

4141
@ApiBearerAuth()
4242
@swagger.ApiTags('Business Reports')
4343
@common.Controller('internal/business-reports')
44-
@swagger.ApiExcludeController()
4544
export class BusinessReportControllerInternal {
4645
constructor(
4746
protected readonly businessReportService: BusinessReportService,
4847
protected readonly logger: AppLoggerService,
4948
protected readonly customerService: CustomerService,
5049
protected readonly businessService: BusinessService,
51-
protected readonly alertService: AlertService,
52-
protected readonly hookCallbackService: HookCallbackHandlerService,
5350
) {}
5451

55-
@common.Post()
56-
@swagger.ApiOkResponse({ type: [String] })
57-
@swagger.ApiForbiddenResponse({ type: errors.ForbiddenException })
58-
async createBusinessReport(
59-
@Body()
60-
{
61-
websiteUrl,
62-
countryCode,
63-
merchantName,
64-
businessCorrelationId,
65-
reportType,
66-
workflowVersion,
67-
}: CreateBusinessReportDto,
68-
@CurrentProject() currentProjectId: TProjectId,
69-
) {
70-
const { id: customerId, config } = await this.customerService.getByProjectId(currentProjectId);
71-
72-
const { maxBusinessReports, withQualityControl } = config || {};
73-
await this.businessReportService.checkBusinessReportsLimit(maxBusinessReports, customerId);
74-
75-
let business: Pick<Business, 'id' | 'correlationId'> | undefined;
76-
const merchantNameWithDefault = merchantName || 'Not detected';
77-
78-
if (!businessCorrelationId) {
79-
business = await this.businessService.create({
80-
data: {
81-
companyName: merchantNameWithDefault,
82-
country: countryCode,
83-
website: websiteUrl,
84-
projectId: currentProjectId,
85-
},
86-
select: {
87-
id: true,
88-
correlationId: true,
89-
},
90-
});
91-
}
92-
93-
if (businessCorrelationId) {
94-
business =
95-
(await this.businessService.getByCorrelationId(businessCorrelationId, [currentProjectId], {
96-
select: {
97-
id: true,
98-
correlationId: true,
99-
},
100-
})) ?? undefined;
101-
}
102-
103-
if (!business) {
104-
throw new BadRequestException(
105-
`Business with an id of ${businessCorrelationId} was not found`,
106-
);
107-
}
108-
109-
await this.businessReportService.createBusinessReportAndTriggerReportCreation({
110-
reportType,
111-
business,
112-
websiteUrl,
113-
countryCode,
114-
merchantName: merchantNameWithDefault,
115-
workflowVersion,
116-
withQualityControl,
117-
customerId,
118-
});
119-
}
120-
12152
@common.Post('/hook')
12253
@swagger.ApiOkResponse({ type: [String] })
12354
@swagger.ApiForbiddenResponse({ type: errors.ForbiddenException })
55+
@swagger.ApiExcludeEndpoint()
12456
@Public()
12557
@VerifyUnifiedApiSignatureDecorator()
12658
async createBusinessReportCallback(
@@ -156,6 +88,7 @@ export class BusinessReportControllerInternal {
15688
@common.Get('/latest')
15789
@swagger.ApiOkResponse({ type: [String] })
15890
@swagger.ApiForbiddenResponse({ type: errors.ForbiddenException })
91+
@swagger.ApiExcludeEndpoint()
15992
async getLatestBusinessReport(
16093
@CurrentProject() currentProjectId: TProjectId,
16194
@Query() { businessId, type }: GetLatestBusinessReportDto,
@@ -172,12 +105,12 @@ export class BusinessReportControllerInternal {
172105
}
173106

174107
@common.Get()
175-
@swagger.ApiOkResponse({ type: [String] })
108+
@swagger.ApiOkResponse({ type: BusinessReportListResponseDto })
176109
@swagger.ApiForbiddenResponse({ type: errors.ForbiddenException })
177110
@common.UsePipes(new ZodValidationPipe(ListBusinessReportsSchema, 'query'))
178111
async listBusinessReports(
179112
@CurrentProject() currentProjectId: TProjectId,
180-
@Query() { businessId, page, search, orderBy }: ListBusinessReportsDto,
113+
@Query() { businessId, page, search }: BusinessReportListRequestParamDto,
181114
) {
182115
const { id: customerId } = await this.customerService.getByProjectId(currentProjectId);
183116

@@ -191,8 +124,74 @@ export class BusinessReportControllerInternal {
191124
});
192125
}
193126

127+
@common.Post()
128+
@swagger.ApiOkResponse({})
129+
@swagger.ApiForbiddenResponse({ type: errors.ForbiddenException })
130+
async createBusinessReport(
131+
@Body()
132+
{
133+
websiteUrl,
134+
countryCode,
135+
merchantName,
136+
businessCorrelationId,
137+
reportType,
138+
workflowVersion,
139+
}: CreateBusinessReportDto,
140+
@CurrentProject() currentProjectId: TProjectId,
141+
) {
142+
const { id: customerId, config } = await this.customerService.getByProjectId(currentProjectId);
143+
144+
const { maxBusinessReports, withQualityControl } = config || {};
145+
await this.businessReportService.checkBusinessReportsLimit(maxBusinessReports, customerId);
146+
147+
let business: Pick<Business, 'id' | 'correlationId'> | undefined;
148+
const merchantNameWithDefault = merchantName || 'Not detected';
149+
150+
if (!businessCorrelationId) {
151+
business = await this.businessService.create({
152+
data: {
153+
companyName: merchantNameWithDefault,
154+
country: countryCode,
155+
website: websiteUrl,
156+
projectId: currentProjectId,
157+
},
158+
select: {
159+
id: true,
160+
correlationId: true,
161+
},
162+
});
163+
}
164+
165+
if (businessCorrelationId) {
166+
business =
167+
(await this.businessService.getByCorrelationId(businessCorrelationId, [currentProjectId], {
168+
select: {
169+
id: true,
170+
correlationId: true,
171+
},
172+
})) ?? undefined;
173+
}
174+
175+
if (!business) {
176+
throw new BadRequestException(
177+
`Business with an id of ${businessCorrelationId} was not found`,
178+
);
179+
}
180+
181+
await this.businessReportService.createBusinessReportAndTriggerReportCreation({
182+
reportType,
183+
business,
184+
websiteUrl,
185+
countryCode,
186+
merchantName: merchantNameWithDefault,
187+
workflowVersion,
188+
withQualityControl,
189+
customerId,
190+
});
191+
}
192+
194193
@common.Get(':id')
195-
@swagger.ApiOkResponse({ type: [String] })
194+
@swagger.ApiOkResponse({ type: BusinessReportDto })
196195
@swagger.ApiForbiddenResponse({ type: errors.ForbiddenException })
197196
@common.UsePipes(new ZodValidationPipe(ListBusinessReportsSchema, 'query'))
198197
async getBusinessReportById(
@@ -204,6 +203,7 @@ export class BusinessReportControllerInternal {
204203
return await this.businessReportService.findById({ id, customerId });
205204
}
206205

206+
@swagger.ApiExcludeEndpoint()
207207
@common.Post('/upload-batch')
208208
@swagger.ApiForbiddenResponse({ type: errors.ForbiddenException })
209209
@ApiConsumes('multipart/form-data')
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
import {
3+
MERCHANT_REPORT_STATUSES,
4+
MERCHANT_REPORT_TYPES,
5+
MERCHANT_REPORT_VERSIONS,
6+
type MerchantReportStatus,
7+
type MerchantReportType,
8+
type MerchantReportVersion,
9+
} from '@/business-report/constants';
10+
11+
export class WebsiteDto {
12+
@ApiProperty({ type: String })
13+
id!: string;
14+
15+
@ApiProperty({ type: String })
16+
url!: string;
17+
18+
@ApiProperty({ type: String })
19+
createdAt!: string;
20+
21+
@ApiProperty({ type: String })
22+
updatedAt!: string;
23+
}
24+
25+
export class BusinessReportDto {
26+
@ApiProperty({ type: String })
27+
id!: string;
28+
29+
@ApiProperty({ type: String })
30+
websiteId!: string;
31+
32+
@ApiProperty({ type: String })
33+
merchantId!: string;
34+
35+
@ApiProperty({ type: String, enum: MERCHANT_REPORT_TYPES })
36+
reportType!: MerchantReportType;
37+
38+
@ApiProperty({ type: String, enum: MERCHANT_REPORT_VERSIONS })
39+
workflowVersion!: MerchantReportVersion;
40+
41+
@ApiProperty({ type: String })
42+
parentCompanyName!: string;
43+
44+
@ApiProperty({ type: String, enum: MERCHANT_REPORT_STATUSES })
45+
status!: MerchantReportStatus;
46+
47+
@ApiProperty({ type: Number })
48+
riskScore!: number;
49+
50+
@ApiProperty({ type: Boolean })
51+
isAlert!: boolean;
52+
53+
@ApiProperty({ type: WebsiteDto })
54+
website!: WebsiteDto;
55+
56+
@ApiProperty({ type: String })
57+
createdAt!: string;
58+
59+
@ApiProperty({ type: String })
60+
updatedAt!: string;
61+
62+
@ApiProperty({ type: Object })
63+
data!: Record<string, unknown>;
64+
}

services/workflows-service/src/business-report/dto/create-business-report.dto.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { IsIn, IsOptional, IsString, MinLength } from 'class-validator';
33
import { countryCodes } from '@ballerine/common';
44
import {
55
MERCHANT_REPORT_TYPES,
6+
MERCHANT_REPORT_TYPES_MAP,
67
MERCHANT_REPORT_VERSIONS_MAP,
78
type MerchantReportType,
89
type MerchantReportVersion,
@@ -21,6 +22,7 @@ export class CreateBusinessReportDto {
2122
@ApiProperty({
2223
required: true,
2324
type: String,
25+
example: 'https://www.example.com',
2426
})
2527
@MinLength(1)
2628
@IsString()
@@ -38,6 +40,8 @@ export class CreateBusinessReportDto {
3840
@ApiProperty({
3941
required: false,
4042
type: String,
43+
enum: countryCodes,
44+
default: 'GB',
4145
})
4246
@IsOptional()
4347
@IsIn(Object.values(countryCodes))
@@ -46,6 +50,7 @@ export class CreateBusinessReportDto {
4650
@ApiProperty({
4751
required: true,
4852
type: String,
53+
example: MERCHANT_REPORT_TYPES_MAP.MERCHANT_REPORT_T1,
4954
})
5055
@IsIn(Object.values(MERCHANT_REPORT_TYPES))
5156
reportType!: MerchantReportType;

0 commit comments

Comments
 (0)