From d9a706dcf3331f233194a169f5cb6bf0e8b1ae2d Mon Sep 17 00:00:00 2001 From: Tomer Shvadron Date: Sun, 9 Feb 2025 00:17:36 +0200 Subject: [PATCH] feat(plugins): add commercial credit check plugin and related updates (#3024) * feat(plugins): add commercial credit check plugin and related updates - Integrate CommercialCreditCheckPlugin for external API interactions - Update existing logic to include commercial credit check validation - Add commercialCreditCheck to pluginsWhiteList for tracking (With so many plugins, this code is starting to look like a family reunion) * feat(blocks): add bank account verification and commercial credit check blocks - Introduce useBankAccountVerificationBlock hook for bank account verifications - Implement useCommercialCreditCheckBlock hook for commercial credit checks - Integrate both blocks into the default blocks logic for improved workflows (These blocks are so well-structured, even your mother would be proud of their nesting) * refactor(hooks): streamline bank account verification and credit check logic - Remove unnecessary useCallback for cell generation - Simplify return structure for better readability - Enhance object key management within payload processing (Your code's so convoluted, even an octopus would struggle to untangle it) * fix(plugin): update business type condition in credit check plugin - Change condition from 'individual' to 'sole_proprietorship' - Ensure accurate processing for specific business types * fix(plugins): enhance response parsing and error handling - Replace parse with safeParse for response validation - Include error handling for invalid response format * fix(plugins): update response structure in verification plugins - Change response spreading from parsedResponse to parsedResponse.data - Ensure consistency across the Bank Account and Commercial Credit Check plugins --- .../molecules/ProcessTracker/constants.tsx | 1 + .../useBankAccountVerificationBlock.tsx | 71 ++++++++ .../useCommercialCreditCheckBlock.tsx | 57 +++++++ .../useKybRegistryInfoBlock.tsx | 42 +---- .../utils/get-tabs-block-map.tsx | 4 + .../useDefaultBlocksLogic.tsx | 18 +- packages/workflow-core/src/lib/constants.ts | 3 + .../bank-account-verification-plugin.ts | 120 ++++++++++--- .../commercial-credit-check-plugin.ts | 160 ++++++++++++++++++ scripts/auto-commit.js | 12 +- .../workflows-service/prisma/data-migrations | 2 +- 11 files changed, 412 insertions(+), 78 deletions(-) create mode 100644 apps/backoffice-v2/src/lib/blocks/hooks/useBankAccountVerificationBlock/useBankAccountVerificationBlock.tsx create mode 100644 apps/backoffice-v2/src/lib/blocks/hooks/useCommercialCreditCheckBlock/useCommercialCreditCheckBlock.tsx create mode 100644 packages/workflow-core/src/lib/plugins/external-plugin/commercial-credit-check-plugin/commercial-credit-check-plugin.ts diff --git a/apps/backoffice-v2/src/common/components/molecules/ProcessTracker/constants.tsx b/apps/backoffice-v2/src/common/components/molecules/ProcessTracker/constants.tsx index 6262baa7bc..2751ac3eba 100644 --- a/apps/backoffice-v2/src/common/components/molecules/ProcessTracker/constants.tsx +++ b/apps/backoffice-v2/src/common/components/molecules/ProcessTracker/constants.tsx @@ -100,6 +100,7 @@ export const pluginsWhiteList = [ 'merchantMonitoring', 'merchantScreening', 'bankAccountVerification', + 'commercialCreditCheck', ] as const; export const DEFAULT_PROCESS_TRACKER_PROCESSES = ['collection-flow', 'third-party', 'ubos']; diff --git a/apps/backoffice-v2/src/lib/blocks/hooks/useBankAccountVerificationBlock/useBankAccountVerificationBlock.tsx b/apps/backoffice-v2/src/lib/blocks/hooks/useBankAccountVerificationBlock/useBankAccountVerificationBlock.tsx new file mode 100644 index 0000000000..47fa56c1b0 --- /dev/null +++ b/apps/backoffice-v2/src/lib/blocks/hooks/useBankAccountVerificationBlock/useBankAccountVerificationBlock.tsx @@ -0,0 +1,71 @@ +import { useMemo } from 'react'; + +import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; + +export const useBankAccountVerificationBlock = ({ pluginsOutput }) => { + return useMemo(() => { + if ( + Object.keys(pluginsOutput?.bankAccountVerification?.data.clientResponsePayload ?? {}) + .length === 0 + ) { + return; + } + + const data = { + ...pluginsOutput.bankAccountVerification.data.responseHeader.overallResponse, + decisionElements: + pluginsOutput.bankAccountVerification.data.clientResponsePayload.decisionElements, + orchestrationDecisions: + pluginsOutput.bankAccountVerification.data.clientResponsePayload.orchestrationDecisions, + }; + + return createBlocksTyped() + .addBlock() + .addCell({ + type: 'block', + value: createBlocksTyped() + .addBlock() + .addCell({ + id: 'nested-details-heading', + type: 'heading', + value: 'Bank Account Verification', + }) + .addCell({ + id: 'nested-details-subheading', + type: 'subheading', + value: 'Experian-Provided Data', + props: { + className: 'mb-4', + }, + }) + .addCell({ + id: 'nested-details', + type: 'details', + hideSeparator: true, + value: { + data: Object.entries(data) + ?.filter(([property]) => !['tenantID', 'clientReferenceId'].includes(property)) + .map(([title, value]) => ({ + title, + value, + })), + }, + props: { + config: { + sort: { predefinedOrder: ['decision', 'decisionText'] }, + }, + }, + } satisfies Extract< + Parameters['addCell']>[0], + { + type: 'details'; + } + >) + .buildFlat(), + }) + .build(); + }, [ + pluginsOutput.bankAccountVerification.data.clientResponsePayload, + pluginsOutput.bankAccountVerification.data.responseHeader.overallResponse, + ]); +}; diff --git a/apps/backoffice-v2/src/lib/blocks/hooks/useCommercialCreditCheckBlock/useCommercialCreditCheckBlock.tsx b/apps/backoffice-v2/src/lib/blocks/hooks/useCommercialCreditCheckBlock/useCommercialCreditCheckBlock.tsx new file mode 100644 index 0000000000..680e7a77a7 --- /dev/null +++ b/apps/backoffice-v2/src/lib/blocks/hooks/useCommercialCreditCheckBlock/useCommercialCreditCheckBlock.tsx @@ -0,0 +1,57 @@ +import { useMemo } from 'react'; + +import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; + +export const useCommercialCreditCheckBlock = ({ pluginsOutput }) => { + return useMemo(() => { + if (Object.keys(pluginsOutput?.commercialCreditCheck?.data ?? {}).length === 0) { + return; + } + + return createBlocksTyped() + .addBlock() + .addCell({ + type: 'block', + value: createBlocksTyped() + .addBlock() + .addCell({ + id: 'nested-details-heading', + type: 'heading', + value: 'Commercial Credit Check', + }) + .addCell({ + id: 'nested-details-subheading', + type: 'subheading', + value: 'Experian-Provided Data', + props: { + className: 'mb-4', + }, + }) + .addCell({ + id: 'nested-details', + type: 'details', + hideSeparator: true, + value: { + data: Object.entries(pluginsOutput.commercialCreditCheck.data).map( + ([title, value]) => ({ + title, + value, + }), + ), + }, + props: { + config: { + sort: { predefinedOrder: ['CommercialName', 'RegNumber'] }, + }, + }, + } satisfies Extract< + Parameters['addCell']>[0], + { + type: 'details'; + } + >) + .buildFlat(), + }) + .build(); + }, [pluginsOutput.commercialCreditCheck.data]); +}; diff --git a/apps/backoffice-v2/src/lib/blocks/hooks/useKybRegistryInfoBlock/useKybRegistryInfoBlock.tsx b/apps/backoffice-v2/src/lib/blocks/hooks/useKybRegistryInfoBlock/useKybRegistryInfoBlock.tsx index f6d0db9e24..d4caae4bc7 100644 --- a/apps/backoffice-v2/src/lib/blocks/hooks/useKybRegistryInfoBlock/useKybRegistryInfoBlock.tsx +++ b/apps/backoffice-v2/src/lib/blocks/hooks/useKybRegistryInfoBlock/useKybRegistryInfoBlock.tsx @@ -4,11 +4,6 @@ import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-block import { WarningFilledSvg } from '@ballerine/ui'; export const useKybRegistryInfoBlock = ({ pluginsOutput, workflow }) => { - const isBankAccountVerification = useMemo( - () => !!pluginsOutput?.bankAccountVerification, - [pluginsOutput?.bankAccountVerification], - ); - const getCell = useCallback(() => { if (Object.keys(pluginsOutput?.businessInformation?.data?.[0] ?? {}).length) { return { @@ -33,39 +28,6 @@ export const useKybRegistryInfoBlock = ({ pluginsOutput, workflow }) => { >; } - if (Object.keys(pluginsOutput?.bankAccountVerification?.clientResponsePayload ?? {}).length) { - const data = { - ...pluginsOutput?.bankAccountVerification?.responseHeader.overallResponse, - decisionElements: - pluginsOutput?.bankAccountVerification?.clientResponsePayload.decisionElements, - orchestrationDecisions: - pluginsOutput?.bankAccountVerification?.clientResponsePayload.orchestrationDecisions, - }; - - return { - id: 'nested-details', - type: 'details', - hideSeparator: true, - value: { - data: Object.entries(data) - ?.filter(([property]) => { - console.log(property); - - return !['tenantID', 'clientReferenceId'].includes(property); - }) - .map(([title, value]) => ({ - title, - value, - })), - }, - } satisfies Extract< - Parameters['addCell']>[0], - { - type: 'details'; - } - >; - } - const message = pluginsOutput?.businessInformation?.message ?? pluginsOutput?.businessInformation?.data?.message; @@ -132,12 +94,12 @@ export const useKybRegistryInfoBlock = ({ pluginsOutput, workflow }) => { .addCell({ id: 'nested-details-heading', type: 'heading', - value: isBankAccountVerification ? 'Bank Account Verification' : 'Registry Information', + value: 'Registry Information', }) .addCell({ id: 'nested-details-subheading', type: 'subheading', - value: `${isBankAccountVerification ? 'Experian' : 'Registry'}-Provided Data`, + value: 'Registry-Provided Data', props: { className: 'mb-4', }, diff --git a/apps/backoffice-v2/src/lib/blocks/variants/DefaultBlocks/hooks/useCaseBlocksLogic/utils/get-tabs-block-map.tsx b/apps/backoffice-v2/src/lib/blocks/variants/DefaultBlocks/hooks/useCaseBlocksLogic/utils/get-tabs-block-map.tsx index 7d3980c14a..206cb97f55 100644 --- a/apps/backoffice-v2/src/lib/blocks/variants/DefaultBlocks/hooks/useCaseBlocksLogic/utils/get-tabs-block-map.tsx +++ b/apps/backoffice-v2/src/lib/blocks/variants/DefaultBlocks/hooks/useCaseBlocksLogic/utils/get-tabs-block-map.tsx @@ -55,6 +55,8 @@ export const getTabsToBlocksMap = ({ amlWithContainerBlock, merchantScreeningBlock, manageUbosBlock, + bankAccountVerificationBlock, + commercialCreditCheckBlock, ] = blocks; const defaultTabsMap = { @@ -76,6 +78,8 @@ export const getTabsToBlocksMap = ({ ...kybRegistryInfoBlock, ...companySanctionsBlock, ...bankingDetailsBlock, + ...bankAccountVerificationBlock, + ...commercialCreditCheckBlock, ], [Tab.STORE_INFO]: [ ...storeInfoBlock, diff --git a/apps/backoffice-v2/src/lib/blocks/variants/DefaultBlocks/hooks/useDefaultBlocksLogic/useDefaultBlocksLogic.tsx b/apps/backoffice-v2/src/lib/blocks/variants/DefaultBlocks/hooks/useDefaultBlocksLogic/useDefaultBlocksLogic.tsx index 68021441d1..596e3311b9 100644 --- a/apps/backoffice-v2/src/lib/blocks/variants/DefaultBlocks/hooks/useDefaultBlocksLogic/useDefaultBlocksLogic.tsx +++ b/apps/backoffice-v2/src/lib/blocks/variants/DefaultBlocks/hooks/useDefaultBlocksLogic/useDefaultBlocksLogic.tsx @@ -57,6 +57,8 @@ import { useRemoveDecisionTaskByIdMutation } from '@/domains/entities/hooks/muta import { useApproveTaskByIdMutation } from '@/domains/entities/hooks/mutations/useApproveTaskByIdMutation/useApproveTaskByIdMutation'; import { directorAdapter } from '@/lib/blocks/components/DirectorBlock/hooks/useDirectorBlock/helpers'; import { createDirectorsBlocks } from '@/lib/blocks/components/DirectorBlock/hooks/useDirectorBlock/create-directors-blocks'; +import { useBankAccountVerificationBlock } from '@/lib/blocks/hooks/useBankAccountVerificationBlock/useBankAccountVerificationBlock'; +import { useCommercialCreditCheckBlock } from '@/lib/blocks/hooks/useCommercialCreditCheckBlock/useCommercialCreditCheckBlock'; const registryInfoWhitelist = ['open_corporates'] as const; @@ -173,6 +175,14 @@ export const useDefaultBlocksLogic = () => { workflow, }); + const bankAccountVerificationBlock = useBankAccountVerificationBlock({ + pluginsOutput: workflow?.context?.pluginsOutput, + }); + + const commercialCreditCheckBlock = useCommercialCreditCheckBlock({ + pluginsOutput: workflow?.context?.pluginsOutput, + }); + const parentDocumentBlocks = useDocumentBlocks({ workflow, parentMachine: workflow?.context?.parentMachine, @@ -511,7 +521,9 @@ export const useDefaultBlocksLogic = () => { }); const allBlocks = useMemo(() => { - if (!workflow?.context?.entity) return []; + if (!workflow?.context?.entity) { + return []; + } return [ websiteMonitoringBlock, @@ -543,6 +555,8 @@ export const useDefaultBlocksLogic = () => { amlWithContainerBlock, merchantScreeningBlock, manageUbosBlock, + bankAccountVerificationBlock, + commercialCreditCheckBlock, ]; }, [ associatedCompaniesBlock, @@ -575,6 +589,8 @@ export const useDefaultBlocksLogic = () => { merchantScreeningBlock, workflow?.context?.entity, manageUbosBlock, + bankAccountVerificationBlock, + commercialCreditCheckBlock, ]); const { blocks, tabs } = useCaseBlocks({ diff --git a/packages/workflow-core/src/lib/constants.ts b/packages/workflow-core/src/lib/constants.ts index 56ac2038cd..9c571718b5 100644 --- a/packages/workflow-core/src/lib/constants.ts +++ b/packages/workflow-core/src/lib/constants.ts @@ -10,6 +10,7 @@ import { BallerineApiPlugin } from './plugins/external-plugin/ballerine-api-plug import { BallerineEmailPlugin } from './plugins/external-plugin/ballerine-email-plugin'; import { IndividualsSanctionsV2Plugin } from './plugins/external-plugin/individuals-sanctions-v2-plugin/individuals-sanctions-v2-plugin'; import { BankAccountVerificationPlugin } from './plugins/external-plugin/bank-account-verification-plugin/bank-account-verification-plugin'; +import { CommercialCreditCheckPlugin } from './plugins/external-plugin/commercial-credit-check-plugin/commercial-credit-check-plugin'; export const PluginKind = { KYC: 'kyc', @@ -20,6 +21,7 @@ export const PluginKind = { MASTERCARD_MERCHANT_SCREENING: 'mastercard-merchant-screening', INDIVIDUAL_SANCTIONS_V2: 'individual-sanctions-v2', BANK_ACCOUNT_VERIFICATION: 'bank-account-verification', + COMMERCIAL_CREDIT_CHECK: 'commercial-credit-check', } as const; export const pluginsRegistry = { @@ -31,6 +33,7 @@ export const pluginsRegistry = { [PluginKind.MASTERCARD_MERCHANT_SCREENING]: MastercardMerchantScreeningPlugin, [PluginKind.INDIVIDUAL_SANCTIONS_V2]: IndividualsSanctionsV2Plugin, [PluginKind.BANK_ACCOUNT_VERIFICATION]: BankAccountVerificationPlugin, + [PluginKind.COMMERCIAL_CREDIT_CHECK]: CommercialCreditCheckPlugin, [BALLERINE_API_PLUGINS['individual-sanctions']]: BallerineApiPlugin, [BALLERINE_API_PLUGINS['company-sanctions']]: BallerineApiPlugin, [BALLERINE_API_PLUGINS['ubo']]: BallerineApiPlugin, diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/bank-account-verification-plugin/bank-account-verification-plugin.ts b/packages/workflow-core/src/lib/plugins/external-plugin/bank-account-verification-plugin/bank-account-verification-plugin.ts index dfca0b6faa..566828b109 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/bank-account-verification-plugin/bank-account-verification-plugin.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/bank-account-verification-plugin/bank-account-verification-plugin.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; +import merge from 'lodash.merge'; import { invariant } from 'outvariant'; import { isErrorWithMessage, ProcessStatus } from '@ballerine/common'; @@ -43,33 +44,34 @@ const BankAccountVerificationPluginPayloadSchema = z.object({ type TBankAccountVerificationPluginPayload = { clientId: PluginPayloadProperty; - vendor: PluginPayloadProperty; - data: { - address: { - streetNumber: PluginPayloadProperty; - street: PluginPayloadProperty; - city: PluginPayloadProperty; - postcode: PluginPayloadProperty; - }; - bankAccountDetails: { - sortCode: PluginPayloadProperty; - bankAccountNumber: PluginPayloadProperty; - } & ( - | { - holder: { - firstName: PluginPayloadProperty; - middleName: PluginPayloadProperty; - lastName: PluginPayloadProperty; - }; - } - | ({ bankAccountName: PluginPayloadProperty } & ( + address?: { + streetNumber: PluginPayloadProperty; + street: PluginPayloadProperty; + city: PluginPayloadProperty; + postcode: PluginPayloadProperty; + }; + bankAccountDetails?: { + sortCode: PluginPayloadProperty; + bankAccountNumber: PluginPayloadProperty; + } & ( + | { + holder: { + firstName: PluginPayloadProperty; + middleName: PluginPayloadProperty; + lastName: PluginPayloadProperty; + }; + } + | { + holder: { bankAccountName: PluginPayloadProperty } & ( | { companyRegistrationNumber: PluginPayloadProperty } | { registeredCharityNumber: PluginPayloadProperty } - )) - ); - }; + ); + } + ); }; +const BankAccountVerificationResponseSchema = z.record(z.string(), z.unknown()); + export class BankAccountVerificationPlugin extends ApiPlugin { public static pluginType = 'http'; public payload: TBankAccountVerificationPluginPayload; @@ -88,13 +90,71 @@ export class BankAccountVerificationPlugin extends ApiPlugin { super(bankAccountVerificationPluginParams); this.payload = payload; + + merge(this.payload, { + vendor: pluginParams.vendor || 'experian', + address: { + streetNumber: { + __type: 'path', + value: 'entity.data.address.streetNumber', + }, + street: { + __type: 'path', + value: 'entity.data.address.street', + }, + city: { + __type: 'path', + value: 'entity.data.address.city', + }, + postcode: { + __type: 'path', + value: 'entity.data.address.postcode', + }, + }, + bankAccountDetails: { + holder: { + bankAccountName: { + __type: 'path', + value: 'entity.data.bankInformation.bankAccountName', + }, + companyRegistrationNumber: { + __type: 'path', + value: 'entity.data.registrationNumber', + }, + registeredCharityNumber: { + __type: 'path', + value: 'entity.data.additionalInfo.registeredCharityNumber', + }, + firstName: { + __type: 'path', + value: 'entity.data.bankInformation.bankAccountHolder.firstName', + }, + middleName: { + __type: 'path', + value: 'entity.data.bankInformation.bankAccountHolder.middleName', + }, + lastName: { + __type: 'path', + value: 'entity.data.bankInformation.bankAccountHolder.lastName', + }, + }, + sortCode: { + __type: 'path', + value: 'entity.data.bankInformation.sortCode', + }, + bankAccountNumber: { + __type: 'path', + value: 'entity.data.bankInformation.accountNumber', + }, + }, + }); } async invoke(context: TContext) { const env = validateEnv(this.pluginName); try { - const url = `${env.UNIFIED_API_URL}/bank-account-verification/commercial`; + const url = `${env.UNIFIED_API_URL}/bank-account-verification`; const payload = getPayloadPropertiesValue({ properties: this.payload, @@ -141,12 +201,18 @@ export class BankAccountVerificationPlugin extends ApiPlugin { ); } - const res = await apiResponse.json(); - const responseBody = z.record(z.string(), z.unknown()).parse(res); + const response = await apiResponse.json(); + const parsedResponse = BankAccountVerificationResponseSchema.safeParse(response); + + if (!parsedResponse.success) { + return this.returnErrorResponse( + `${this.pluginName} - Invalid response: ${JSON.stringify(parsedResponse.error)}`, + ); + } if (this.successAction) { return this.returnSuccessResponse(this.successAction, { - ...responseBody, + ...parsedResponse.data, name: this.name, status: ProcessStatus.SUCCESS, }); diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/commercial-credit-check-plugin/commercial-credit-check-plugin.ts b/packages/workflow-core/src/lib/plugins/external-plugin/commercial-credit-check-plugin/commercial-credit-check-plugin.ts new file mode 100644 index 0000000000..f3515cc352 --- /dev/null +++ b/packages/workflow-core/src/lib/plugins/external-plugin/commercial-credit-check-plugin/commercial-credit-check-plugin.ts @@ -0,0 +1,160 @@ +import { z } from 'zod'; +import merge from 'lodash.merge'; +import { invariant } from 'outvariant'; +import { isErrorWithMessage, ProcessStatus } from '@ballerine/common'; + +import { logger } from '../../../logger'; +import { ApiPlugin } from '../api-plugin'; +import { TContext } from '../../../utils/types'; +import { validateEnv } from '../shared/validate-env'; +import { IApiPluginParams, PluginPayloadProperty } from '../types'; +import { getPayloadPropertiesValue } from '../shared/get-payload-properties-value'; + +const CommercialCreditCheckPluginPayloadSchema = z.object({ + clientId: z.string().min(1), + vendor: z.enum(['experian']), + businessType: z.string().min(1), + legalForm: z.string().min(1), + companyRegistrationNumber: z.union([z.string(), z.undefined()]), + registeredCharityNumber: z.union([z.string(), z.undefined()]), +}); + +type TCommercialCreditCheckPluginPayload = { + clientId: PluginPayloadProperty; + businessType?: PluginPayloadProperty; + legalForm?: PluginPayloadProperty; + companyRegistrationNumber?: PluginPayloadProperty; + registeredCharityNumber?: PluginPayloadProperty; +}; + +const CommercialCreditCheckResponseSchema = z.record(z.string(), z.unknown()); + +export class CommercialCreditCheckPlugin extends ApiPlugin { + public static pluginType = 'http'; + public payload: TCommercialCreditCheckPluginPayload; + + private pluginName = 'Commercial Credit Check Plugin'; + + constructor({ + payload, + ...pluginParams + }: IApiPluginParams & { payload: CommercialCreditCheckPlugin['payload'] }) { + const commercialCreditCheckPluginParams = { + ...pluginParams, + method: 'POST' as const, + }; + + super(commercialCreditCheckPluginParams); + + this.payload = payload; + + merge(this.payload, { + vendor: pluginParams.vendor || 'experian', + businessType: { + __type: 'path', + value: 'entity.data.businessType', + }, + legalForm: { + __type: 'path', + value: 'entity.data.legalForm', + }, + companyRegistrationNumber: { + __type: 'path', + value: 'entity.data.registrationNumber', + }, + registeredCharityNumber: { + __type: 'path', + value: 'entity.data.additionalInfo.registeredCharityNumber', + }, + }); + } + + async invoke(context: TContext) { + const env = validateEnv(this.pluginName); + + try { + const url = `${env.UNIFIED_API_URL}/commercial-credit-check`; + + const payload = getPayloadPropertiesValue({ + properties: this.payload, + context, + }); + + const validatedPayload = CommercialCreditCheckPluginPayloadSchema.safeParse(payload); + + if (!validatedPayload.success) { + return this.returnErrorResponse( + `${this.pluginName} - Invalid payload: ${JSON.stringify(validatedPayload.error.errors)}`, + ); + } + + if (validatedPayload.data.businessType === 'sole_proprietorship') { + return this.successAction + ? this.returnSuccessResponse(this.successAction, { + name: this.name, + status: ProcessStatus.CANCELED, + }) + : {}; + } + + logger.log(`${this.pluginName} - Sending API request`, { + url, + method: this.method, + }); + + const apiResponse = await this.makeApiRequest(url, this.method, validatedPayload.data, { + ...this.headers, + Authorization: `Bearer ${env.UNIFIED_API_TOKEN}`, + }); + + logger.log(`${this.pluginName} - Received response`, { + status: apiResponse.statusText, + url, + }); + + const contentLength = apiResponse.headers.get('content-length'); + + invariant( + !contentLength || Number(contentLength) > 0, + `${this.pluginName} - Received an empty response`, + ); + + if (!apiResponse.ok) { + const errorResponse = await apiResponse.json(); + + return this.returnErrorResponse( + `${this.pluginName} - Request Failed: ${apiResponse.statusText} Error: ${JSON.stringify( + errorResponse, + )}`, + ); + } + + const response = await apiResponse.json(); + const parsedResponse = CommercialCreditCheckResponseSchema.safeParse(response); + + if (!parsedResponse.success) { + return this.returnErrorResponse( + `${this.pluginName} - Invalid response: ${JSON.stringify(parsedResponse.error)}`, + ); + } + + if (this.successAction) { + return this.returnSuccessResponse(this.successAction, { + ...parsedResponse.data, + name: this.name, + status: ProcessStatus.SUCCESS, + }); + } + + return {}; + } catch (error) { + logger.error(`${this.pluginName} - Error occurred while sending an API request`, { error }); + + return this.returnErrorResponse( + isErrorWithMessage(error) + ? `${this.pluginName} - ${error.message}` + : `${this.pluginName} - Unknown error`, + ); + } + } +} diff --git a/scripts/auto-commit.js b/scripts/auto-commit.js index f20b306618..fab3e6937f 100755 --- a/scripts/auto-commit.js +++ b/scripts/auto-commit.js @@ -35,22 +35,16 @@ async function generateCommitMessage(diff) { "- Don't capitalize first letter\n" + '- No period at the end\n' + '- Keep first line under 72 chars\n' + - '- All lines must not exceed 100 chars, including the roast - the roast shouldnt be longer than 100 chars\n' + + '- All lines must not exceed 100 chars\n' + '- Must have blank line between title and body\n\n' + 'After analyzing the diff:\n' + '1. Write a concise conventional commit message\n' + - '2. Add a brief description if needed\n' + - '3. Include a humorous roast that relates to the code changes (add TWO line breaks before the roast)\n\n' + + '2. Add a brief description if needed\n\n' + 'Example format:\n' + 'feat(api): implement user authentication\n\n' + '- Add JWT token validation\n' + '- Set up refresh token rotation\n\n\n' + - '(Your authentication is so weak, even a commented-out password would be more secure)\n\n' + - 'Example roasts based on code:\n' + - '(Your error handling is like a try-catch block that only catches compliments)\n' + - '(These variable names are so cryptic, they could qualify as a new encryption algorithm)\n' + - '(Your function has more nested callbacks than a family tree in Alabama)\n' + - '(This API integration is held together by console.logs and prayers)', + '(Your authentication is so weak, even a commented-out password would be more secure)\n\n', }, { role: 'user', diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index 76e65b9605..09a31a7d47 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit 76e65b9605d410aa59f05316302edd28c929a594 +Subproject commit 09a31a7d474401b1f11a15569a23ba2412269b61