From 706590c0d0387f3b1b54c58ace630877addd40de Mon Sep 17 00:00:00 2001 From: Illia Rudniev Date: Tue, 21 Jan 2025 16:13:46 +0200 Subject: [PATCH 1/8] feat: added request payload and plugins input --- packages/workflow-core/src/lib/constants.ts | 2 + .../external-plugin/api-plugin.test.ts | 144 +++++++++++++++++- .../lib/plugins/external-plugin/api-plugin.ts | 58 +++++-- .../individuals-sanctions-v2-plugin.test.ts | 5 +- .../mastercard-merchant-screening-plugin.ts | 15 +- .../workflow-core/src/lib/plugins/types.ts | 14 +- .../workflow-core/src/lib/workflow-runner.ts | 22 ++- 7 files changed, 229 insertions(+), 31 deletions(-) diff --git a/packages/workflow-core/src/lib/constants.ts b/packages/workflow-core/src/lib/constants.ts index 56ac2038cd..de10baa231 100644 --- a/packages/workflow-core/src/lib/constants.ts +++ b/packages/workflow-core/src/lib/constants.ts @@ -44,3 +44,5 @@ export const pluginsRegistry = { new (...args: any[]) => any > >; + +export const REQUEST_PAYLOAD_BLACKLIST = ['callbackUrl']; diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.test.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.test.ts index 02f2574396..dddb0496b0 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.test.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.test.ts @@ -1,8 +1,9 @@ -import { describe, expect, it } from 'vitest'; -import { WorkflowRunner } from '../../workflow-runner'; +import { ProcessStatus } from '@ballerine/common'; +import { beforeEach, describe, expect, it, SpyInstance, vi } from 'vitest'; import { WorkflowRunnerArgs } from '../../types'; +import { WorkflowRunner } from '../../workflow-runner'; +import { ApiPlugin } from '../external-plugin'; import { ISerializableHttpPluginParams } from './types'; -import { ProcessStatus } from '@ballerine/common'; const createWorkflowRunner = ( definition: WorkflowRunnerArgs['definition'], @@ -201,4 +202,141 @@ describe('workflow-runner', () => { }); }); }); + + describe('apiPlugin.invoke', async () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should call removeBlacklistedKeys', async () => { + const context = { data: 'test' }; + const apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + request: { + transformers: [], + }, + }); + + const removeBlacklistedKeysSpy = vi.spyOn(apiPlugin, 'removeBlacklistedKeys'); + const transformDataSpy = vi.spyOn(apiPlugin, 'transformData'); + const validateContentSpy = vi.spyOn(apiPlugin, 'validateContent'); + const makeApiRequestSpy = vi.spyOn(apiPlugin, 'makeApiRequest'); + + transformDataSpy.mockResolvedValue(context); + validateContentSpy.mockResolvedValue({ isValidRequest: true }); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: true, + json: () => Promise.resolve(context), + headers: new Headers(), + }); + + await apiPlugin.invoke(context); + + expect(removeBlacklistedKeysSpy).toHaveBeenCalledWith(context); + }); + + describe('requestPayload', () => { + let apiPlugin: ApiPlugin; + let transformDataSpy: ReturnType; + let validateContentSpy: ReturnType; + let makeApiRequestSpy: ReturnType; + + beforeEach(() => { + vi.clearAllMocks(); + + apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + response: { + transformers: [], + }, + request: { + transformers: [], + }, + }); + + transformDataSpy = vi.spyOn(apiPlugin, 'transformData') as SpyInstance; + validateContentSpy = vi.spyOn(apiPlugin, 'validateContent') as SpyInstance; + makeApiRequestSpy = vi.spyOn(apiPlugin, 'makeApiRequest') as SpyInstance; + }); + + it('status ok should include requestPayload', async () => { + const context = { test: '123' }; + + transformDataSpy.mockResolvedValue(context); + validateContentSpy.mockResolvedValue({ isValidRequest: true }); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: true, + json: () => Promise.resolve({}), + headers: new Headers(), + }); + + expect(await apiPlugin.invoke(context)).toHaveProperty('requestPayload', context); + }); + + it('failed request should include requestPayload', async () => { + const context = { test: '123' }; + + transformDataSpy.mockResolvedValue(context); + validateContentSpy.mockResolvedValue({ isValidRequest: false }); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: false, + json: () => Promise.resolve({}), + headers: new Headers(), + }); + + await apiPlugin.invoke(context); + + expect(await apiPlugin.invoke(context)).toHaveProperty('requestPayload', context); + }); + }); + }); + + describe('removeBlacklistedKeys', () => { + let apiPlugin: ApiPlugin; + + beforeEach(() => { + apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + }); + }); + + it('removes blacklisted keys from request payload', () => { + const payload = { callbackUrl: 'https://example.com', data: 'test' }; + const result = apiPlugin.removeBlacklistedKeys(payload); + expect(result).toEqual({ data: 'test' }); + }); + + it('doesnt remove non-blacklisted keys', () => { + const payload = { callbackUrl: 'https://example.com', data: 'test' }; + const result = apiPlugin.removeBlacklistedKeys(payload); + expect(result).toEqual({ data: 'test' }); + }); + + it('correctly handles nested objects', () => { + const payload = { data: { callbackUrl: 'https://example.com' } }; + const result = apiPlugin.removeBlacklistedKeys(payload); + expect(result).toEqual({ data: {} }); + }); + }); }); diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.ts index ba4562215f..13297c3c02 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.ts @@ -1,7 +1,6 @@ import { AnyRecord, isErrorWithMessage, isObject } from '@ballerine/common'; - +import { REQUEST_PAYLOAD_BLACKLIST } from '../../constants'; import { logger } from '../../logger'; -import { IApiPluginParams } from './types'; import { HelpersTransformer, TContext, @@ -10,6 +9,7 @@ import { Transformers, Validator, } from '../../utils'; +import { IApiPluginParams } from './types'; const invokedAtTransformer: HelpersTransformer = new HelpersTransformer([ { @@ -71,7 +71,7 @@ export class ApiPlugin { ); if (!isValidRequest) { - return this.returnErrorResponse(errorMessage!); + return this.returnErrorResponse(errorMessage!, requestPayload); } } @@ -85,6 +85,8 @@ export class ApiPlugin { method: this.method, }); + requestPayload = this.removeBlacklistedKeys(requestPayload); + const apiResponse = await this.makeApiRequest( _url, this.method, @@ -114,13 +116,17 @@ export class ApiPlugin { ); if (!isValidResponse) { - return this.returnErrorResponse(errorMessage!); + return this.returnErrorResponse(errorMessage!, requestPayload); } if (this.successAction) { - return this.returnSuccessResponse(this.successAction, { - ...responseBody, - }); + return this.returnSuccessResponse( + this.successAction, + { + ...responseBody, + }, + requestPayload, + ); } return {}; @@ -129,10 +135,14 @@ export class ApiPlugin { return this.returnErrorResponse( 'Request Failed: ' + apiResponse.statusText + ' Error: ' + JSON.stringify(errorResponse), + requestPayload, ); } } catch (error) { - return this.returnErrorResponse(isErrorWithMessage(error) ? error.message : ''); + return this.returnErrorResponse( + isErrorWithMessage(error) ? error.message : '', + requestPayload, + ); } } @@ -163,12 +173,16 @@ export class ApiPlugin { return await this.replaceAllVariables(_url, context); } - returnSuccessResponse(callbackAction: string, responseBody: AnyRecord) { - return { callbackAction, responseBody }; + returnSuccessResponse( + callbackAction: string, + responseBody: AnyRecord, + requestPayload?: AnyRecord, + ) { + return { callbackAction, responseBody, requestPayload }; } - returnErrorResponse(errorMessage: string) { - return { callbackAction: this.errorAction, error: errorMessage }; + returnErrorResponse(errorMessage: string, requestPayload?: AnyRecord) { + return { callbackAction: this.errorAction, error: errorMessage, requestPayload }; } async makeApiRequest( @@ -412,4 +426,24 @@ export class ApiPlugin { } }, record as unknown); } + + removeBlacklistedKeys(payload: AnyRecord = {}) { + const payloadWithoutBlacklistedKeys: AnyRecord = structuredClone(payload); + + for (const key in payloadWithoutBlacklistedKeys) { + if (REQUEST_PAYLOAD_BLACKLIST.includes(key)) { + delete payloadWithoutBlacklistedKeys[key]; + + continue; + } + + const value = payload[key]; + + if (typeof value === 'object') { + payloadWithoutBlacklistedKeys[key] = this.removeBlacklistedKeys(value as AnyRecord); + } + } + + return payloadWithoutBlacklistedKeys; + } } diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/individuals-sanctions-v2-plugin/individuals-sanctions-v2-plugin.test.ts b/packages/workflow-core/src/lib/plugins/external-plugin/individuals-sanctions-v2-plugin/individuals-sanctions-v2-plugin.test.ts index eec361679d..8a32ecfe50 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/individuals-sanctions-v2-plugin/individuals-sanctions-v2-plugin.test.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/individuals-sanctions-v2-plugin/individuals-sanctions-v2-plugin.test.ts @@ -1,13 +1,14 @@ +import { ProcessStatus, UnifiedApiReason } from '@ballerine/common'; +import nock from 'nock'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { IndividualsSanctionsV2Plugin } from './individuals-sanctions-v2-plugin'; -import nock from 'nock'; -import { ProcessStatus, UnifiedApiReason } from '@ballerine/common'; describe('IndividualsSanctionsV2Plugin', () => { beforeEach(() => { nock.disableNetConnect(); vi.unstubAllEnvs(); vi.unstubAllGlobals(); + vi.clearAllMocks(); }); describe('when a JMESPath request transformer is passed', () => { diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/mastercard-merchant-screening-plugin.ts b/packages/workflow-core/src/lib/plugins/external-plugin/mastercard-merchant-screening-plugin.ts index e2f6ab8cd0..acc8e0365c 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/mastercard-merchant-screening-plugin.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/mastercard-merchant-screening-plugin.ts @@ -1,10 +1,10 @@ import { AnyRecord, isErrorWithMessage, isObject } from '@ballerine/common'; +import { State } from 'country-state-city'; import { alpha2ToAlpha3 } from 'i18n-iso-countries'; import { logger } from '../../logger'; import { TContext } from '../../utils/types'; import { ApiPlugin } from './api-plugin'; import { IApiPluginParams } from './types'; -import { State } from 'country-state-city'; export class MastercardMerchantScreeningPlugin extends ApiPlugin { public static pluginType = 'http'; @@ -103,13 +103,17 @@ export class MastercardMerchantScreeningPlugin extends ApiPlugin { ); if (!isValidResponse) { - return this.returnErrorResponse(errorMessage!); + return this.returnErrorResponse(errorMessage!, requestPayload); } if (this.successAction) { - return this.returnSuccessResponse(this.successAction, { - ...responseBody, - }); + return this.returnSuccessResponse( + this.successAction, + { + ...responseBody, + }, + requestPayload, + ); } return {}; @@ -118,6 +122,7 @@ export class MastercardMerchantScreeningPlugin extends ApiPlugin { return this.returnErrorResponse( 'Request Failed: ' + apiResponse.statusText + ' Error: ' + JSON.stringify(errorResponse), + requestPayload, ); } } catch (error) { diff --git a/packages/workflow-core/src/lib/plugins/types.ts b/packages/workflow-core/src/lib/plugins/types.ts index b7f50ee0d7..1e1e42847b 100644 --- a/packages/workflow-core/src/lib/plugins/types.ts +++ b/packages/workflow-core/src/lib/plugins/types.ts @@ -1,12 +1,12 @@ -import { ApiPlugin } from './external-plugin/api-plugin'; -import { WebhookPlugin } from './external-plugin/webhook-plugin'; -import { KycPlugin } from './external-plugin/kyc-plugin'; -import { IterativePlugin } from './common-plugin/iterative-plugin'; -import { TContext } from '../utils'; -import { ChildWorkflowPlugin } from './common-plugin/child-workflow-plugin'; -import { TransformerPlugin } from '../plugins/common-plugin/transformer-plugin'; import { RiskRulePlugin } from '@/lib/plugins/common-plugin/risk-rules-plugin'; import { WorkflowTokenPlugin } from '@/lib/plugins/common-plugin/workflow-token-plugin'; +import { TransformerPlugin } from '../plugins/common-plugin/transformer-plugin'; +import { TContext } from '../utils'; +import { ChildWorkflowPlugin } from './common-plugin/child-workflow-plugin'; +import { IterativePlugin } from './common-plugin/iterative-plugin'; +import { ApiPlugin } from './external-plugin/api-plugin'; +import { KycPlugin } from './external-plugin/kyc-plugin'; +import { WebhookPlugin } from './external-plugin/webhook-plugin'; export type PluginAction = { workflowId: string; context: any; event: any; state: any }; export type InvokePluginAction = { context: TContext }; diff --git a/packages/workflow-core/src/lib/workflow-runner.ts b/packages/workflow-core/src/lib/workflow-runner.ts index 22726ee9fe..8ba49e4c72 100644 --- a/packages/workflow-core/src/lib/workflow-runner.ts +++ b/packages/workflow-core/src/lib/workflow-runner.ts @@ -4,8 +4,8 @@ import { search } from 'jmespath'; import * as jsonLogic from 'json-logic-js'; import type { ActionFunction, MachineOptions, StateMachine } from 'xstate'; import { assign, createMachine, interpret } from 'xstate'; -import { pluginsRegistry } from './constants'; import { BUILT_IN_ACTION } from './built-in-action'; +import { pluginsRegistry } from './constants'; import { HttpError } from './errors'; import { BUILT_IN_EVENT } from './index'; import { logger } from './logger'; @@ -772,7 +772,7 @@ export class WorkflowRunner { private async __invokeApiPlugin(apiPlugin: HttpPlugin, additionalContext?: AnyRecord) { // @ts-expect-error - multiple types of plugins return different responses - const { callbackAction, responseBody, error } = await apiPlugin.invoke?.( + const { callbackAction, responseBody, requestPayload, error } = await apiPlugin.invoke?.( { ...this.context, workflowRuntimeConfig: this.#__config, @@ -804,6 +804,12 @@ export class WorkflowRunner { responseBody, apiPlugin.persistResponseDestination, ); + + this.context = this.mergeToContext( + this.context, + { requestPayload, status: ProcessStatus.SUCCESS }, + `pluginsInput.${apiPlugin.name}`, + ); } if (!apiPlugin.persistResponseDestination && responseBody) { @@ -812,6 +818,12 @@ export class WorkflowRunner { responseBody, `pluginsOutput.${apiPlugin.name}`, ); + + this.context = this.mergeToContext( + this.context, + { requestPayload, status: ProcessStatus.SUCCESS }, + `pluginsInput.${apiPlugin.name}`, + ); } if (error) { @@ -820,6 +832,12 @@ export class WorkflowRunner { { name: apiPlugin.name, error, status: ProcessStatus.ERROR }, `pluginsOutput.${apiPlugin.name}`, ); + + this.context = this.mergeToContext( + this.context, + { requestPayload, error, status: ProcessStatus.ERROR }, + `pluginsInput.${apiPlugin.name}`, + ); } await this.sendEvent({ type: callbackAction }); From e1d489e152d3b76b5181080fcb4ae478ed4ba1d5 Mon Sep 17 00:00:00 2001 From: Illia Rudniev Date: Wed, 22 Jan 2025 14:24:11 +0200 Subject: [PATCH 2/8] feat: reorganized files --- .../api-plugin-workflow-runner.test.ts} | 146 +----------------- .../api-plugin/api-plugin.test.ts | 141 +++++++++++++++++ .../{ => api-plugin}/api-plugin.ts | 10 +- .../external-plugin/api-plugin/constants.ts | 1 + .../external-plugin/api-plugin/index.ts | 1 + 5 files changed, 152 insertions(+), 147 deletions(-) rename packages/workflow-core/src/lib/plugins/external-plugin/{api-plugin.test.ts => api-plugin/api-plugin-workflow-runner.test.ts} (56%) create mode 100644 packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.test.ts rename packages/workflow-core/src/lib/plugins/external-plugin/{ => api-plugin}/api-plugin.ts (97%) create mode 100644 packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/constants.ts create mode 100644 packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/index.ts diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.test.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin-workflow-runner.test.ts similarity index 56% rename from packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.test.ts rename to packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin-workflow-runner.test.ts index dddb0496b0..36e67282b3 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.test.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin-workflow-runner.test.ts @@ -1,9 +1,8 @@ import { ProcessStatus } from '@ballerine/common'; -import { beforeEach, describe, expect, it, SpyInstance, vi } from 'vitest'; -import { WorkflowRunnerArgs } from '../../types'; -import { WorkflowRunner } from '../../workflow-runner'; -import { ApiPlugin } from '../external-plugin'; -import { ISerializableHttpPluginParams } from './types'; +import { describe, expect, it } from 'vitest'; +import { WorkflowRunnerArgs } from '../../../types'; +import { WorkflowRunner } from '../../../workflow-runner'; +import { ISerializableHttpPluginParams } from '../types'; const createWorkflowRunner = ( definition: WorkflowRunnerArgs['definition'], @@ -202,141 +201,4 @@ describe('workflow-runner', () => { }); }); }); - - describe('apiPlugin.invoke', async () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it('should call removeBlacklistedKeys', async () => { - const context = { data: 'test' }; - const apiPlugin = new ApiPlugin({ - name: 'ballerineEnrichment', - displayName: 'Ballerine Enrichment', - url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', - method: 'GET' as const, - stateNames: ['checkBusinessScore'], - successAction: 'API_CALL_SUCCESS', - errorAction: 'API_CALL_FAILURE', - request: { - transformers: [], - }, - }); - - const removeBlacklistedKeysSpy = vi.spyOn(apiPlugin, 'removeBlacklistedKeys'); - const transformDataSpy = vi.spyOn(apiPlugin, 'transformData'); - const validateContentSpy = vi.spyOn(apiPlugin, 'validateContent'); - const makeApiRequestSpy = vi.spyOn(apiPlugin, 'makeApiRequest'); - - transformDataSpy.mockResolvedValue(context); - validateContentSpy.mockResolvedValue({ isValidRequest: true }); - makeApiRequestSpy.mockResolvedValue({ - statusText: 'OK', - ok: true, - json: () => Promise.resolve(context), - headers: new Headers(), - }); - - await apiPlugin.invoke(context); - - expect(removeBlacklistedKeysSpy).toHaveBeenCalledWith(context); - }); - - describe('requestPayload', () => { - let apiPlugin: ApiPlugin; - let transformDataSpy: ReturnType; - let validateContentSpy: ReturnType; - let makeApiRequestSpy: ReturnType; - - beforeEach(() => { - vi.clearAllMocks(); - - apiPlugin = new ApiPlugin({ - name: 'ballerineEnrichment', - displayName: 'Ballerine Enrichment', - url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', - method: 'GET' as const, - stateNames: ['checkBusinessScore'], - successAction: 'API_CALL_SUCCESS', - errorAction: 'API_CALL_FAILURE', - response: { - transformers: [], - }, - request: { - transformers: [], - }, - }); - - transformDataSpy = vi.spyOn(apiPlugin, 'transformData') as SpyInstance; - validateContentSpy = vi.spyOn(apiPlugin, 'validateContent') as SpyInstance; - makeApiRequestSpy = vi.spyOn(apiPlugin, 'makeApiRequest') as SpyInstance; - }); - - it('status ok should include requestPayload', async () => { - const context = { test: '123' }; - - transformDataSpy.mockResolvedValue(context); - validateContentSpy.mockResolvedValue({ isValidRequest: true }); - makeApiRequestSpy.mockResolvedValue({ - statusText: 'OK', - ok: true, - json: () => Promise.resolve({}), - headers: new Headers(), - }); - - expect(await apiPlugin.invoke(context)).toHaveProperty('requestPayload', context); - }); - - it('failed request should include requestPayload', async () => { - const context = { test: '123' }; - - transformDataSpy.mockResolvedValue(context); - validateContentSpy.mockResolvedValue({ isValidRequest: false }); - makeApiRequestSpy.mockResolvedValue({ - statusText: 'OK', - ok: false, - json: () => Promise.resolve({}), - headers: new Headers(), - }); - - await apiPlugin.invoke(context); - - expect(await apiPlugin.invoke(context)).toHaveProperty('requestPayload', context); - }); - }); - }); - - describe('removeBlacklistedKeys', () => { - let apiPlugin: ApiPlugin; - - beforeEach(() => { - apiPlugin = new ApiPlugin({ - name: 'ballerineEnrichment', - displayName: 'Ballerine Enrichment', - url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', - method: 'GET' as const, - stateNames: ['checkBusinessScore'], - successAction: 'API_CALL_SUCCESS', - errorAction: 'API_CALL_FAILURE', - }); - }); - - it('removes blacklisted keys from request payload', () => { - const payload = { callbackUrl: 'https://example.com', data: 'test' }; - const result = apiPlugin.removeBlacklistedKeys(payload); - expect(result).toEqual({ data: 'test' }); - }); - - it('doesnt remove non-blacklisted keys', () => { - const payload = { callbackUrl: 'https://example.com', data: 'test' }; - const result = apiPlugin.removeBlacklistedKeys(payload); - expect(result).toEqual({ data: 'test' }); - }); - - it('correctly handles nested objects', () => { - const payload = { data: { callbackUrl: 'https://example.com' } }; - const result = apiPlugin.removeBlacklistedKeys(payload); - expect(result).toEqual({ data: {} }); - }); - }); }); diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.test.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.test.ts new file mode 100644 index 0000000000..75002f1b84 --- /dev/null +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.test.ts @@ -0,0 +1,141 @@ +import { beforeEach, describe, expect, it, SpyInstance, vi } from 'vitest'; +import { ApiPlugin } from './api-plugin'; + +describe('ApiPlugin', () => { + describe('apiPlugin.invoke', async () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should call removeBlacklistedKeys', async () => { + const context = { data: 'test' }; + const apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + request: { + transformers: [], + }, + }); + + const removeBlacklistedKeysSpy = vi.spyOn(apiPlugin, 'removeBlacklistedKeys'); + const transformDataSpy = vi.spyOn(apiPlugin, 'transformData'); + const validateContentSpy = vi.spyOn(apiPlugin, 'validateContent'); + const makeApiRequestSpy = vi.spyOn(apiPlugin, 'makeApiRequest'); + + transformDataSpy.mockResolvedValue(context); + validateContentSpy.mockResolvedValue({ isValidRequest: true }); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: true, + json: () => Promise.resolve(context), + headers: new Headers(), + }); + + await apiPlugin.invoke(context); + + expect(removeBlacklistedKeysSpy).toHaveBeenCalledWith(context); + }); + + describe('requestPayload', () => { + let apiPlugin: ApiPlugin; + let transformDataSpy: ReturnType; + let validateContentSpy: ReturnType; + let makeApiRequestSpy: ReturnType; + + beforeEach(() => { + vi.clearAllMocks(); + + apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + response: { + transformers: [], + }, + request: { + transformers: [], + }, + }); + + transformDataSpy = vi.spyOn(apiPlugin, 'transformData') as SpyInstance; + validateContentSpy = vi.spyOn(apiPlugin, 'validateContent') as SpyInstance; + makeApiRequestSpy = vi.spyOn(apiPlugin, 'makeApiRequest') as SpyInstance; + }); + + it('status ok should include requestPayload', async () => { + const context = { test: '123' }; + + transformDataSpy.mockResolvedValue(context); + validateContentSpy.mockResolvedValue({ isValidRequest: true }); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: true, + json: () => Promise.resolve({}), + headers: new Headers(), + }); + + expect(await apiPlugin.invoke(context)).toHaveProperty('requestPayload', context); + }); + + it('failed request should include requestPayload', async () => { + const context = { test: '123' }; + + transformDataSpy.mockResolvedValue(context); + validateContentSpy.mockResolvedValue({ isValidRequest: false }); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: false, + json: () => Promise.resolve({}), + headers: new Headers(), + }); + + await apiPlugin.invoke(context); + + expect(await apiPlugin.invoke(context)).toHaveProperty('requestPayload', context); + }); + }); + }); + + describe('removeBlacklistedKeys', () => { + let apiPlugin: ApiPlugin; + + beforeEach(() => { + apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + }); + }); + + it('removes blacklisted keys from request payload', () => { + const payload = { callbackUrl: 'https://example.com', data: 'test' }; + const result = apiPlugin.removeBlacklistedKeys(payload); + expect(result).toEqual({ data: 'test' }); + }); + + it('doesnt remove non-blacklisted keys', () => { + const payload = { callbackUrl: 'https://example.com', data: 'test' }; + const result = apiPlugin.removeBlacklistedKeys(payload); + expect(result).toEqual({ data: 'test' }); + }); + + it('correctly handles nested objects', () => { + const payload = { data: { callbackUrl: 'https://example.com' } }; + const result = apiPlugin.removeBlacklistedKeys(payload); + expect(result).toEqual({ data: {} }); + }); + }); +}); diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts similarity index 97% rename from packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.ts rename to packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts index 13297c3c02..800fdbc122 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts @@ -1,6 +1,5 @@ import { AnyRecord, isErrorWithMessage, isObject } from '@ballerine/common'; -import { REQUEST_PAYLOAD_BLACKLIST } from '../../constants'; -import { logger } from '../../logger'; +import { logger } from '../../../logger'; import { HelpersTransformer, TContext, @@ -8,8 +7,9 @@ import { Transformer, Transformers, Validator, -} from '../../utils'; -import { IApiPluginParams } from './types'; +} from '../../../utils'; +import { IApiPluginParams } from '../types'; +import { REQUEST_PAYLOAD_BLACKLIST } from './constants'; const invokedAtTransformer: HelpersTransformer = new HelpersTransformer([ { @@ -431,7 +431,7 @@ export class ApiPlugin { const payloadWithoutBlacklistedKeys: AnyRecord = structuredClone(payload); for (const key in payloadWithoutBlacklistedKeys) { - if (REQUEST_PAYLOAD_BLACKLIST.includes(key)) { + if (REQUEST_PAYLOAD_BLACKLIST.includes(key as (typeof REQUEST_PAYLOAD_BLACKLIST)[number])) { delete payloadWithoutBlacklistedKeys[key]; continue; diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/constants.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/constants.ts new file mode 100644 index 0000000000..fecc8ebeec --- /dev/null +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/constants.ts @@ -0,0 +1 @@ +export const REQUEST_PAYLOAD_BLACKLIST = ['callbackUrl'] as const; diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/index.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/index.ts new file mode 100644 index 0000000000..50525c3d2c --- /dev/null +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/index.ts @@ -0,0 +1 @@ +export * from './api-plugin'; From bf5680a864e152647e72ca809a1fc16ef7ea188f Mon Sep 17 00:00:00 2001 From: Illia Rudniev Date: Wed, 22 Jan 2025 14:25:58 +0200 Subject: [PATCH 3/8] feat: added rendering of merchant screening input to backoffice --- .../src/domains/workflows/fetchers.ts | 11 +++++++ .../useMerchantScreeningBlock/columns.tsx | 32 +++++++++++++++---- .../useMerchantScreeningBlock.tsx | 13 +++++--- .../useDefaultBlocksLogic.tsx | 32 ++++++++++--------- 4 files changed, 62 insertions(+), 26 deletions(-) diff --git a/apps/backoffice-v2/src/domains/workflows/fetchers.ts b/apps/backoffice-v2/src/domains/workflows/fetchers.ts index af75288bb8..08dea034c5 100644 --- a/apps/backoffice-v2/src/domains/workflows/fetchers.ts +++ b/apps/backoffice-v2/src/domains/workflows/fetchers.ts @@ -133,6 +133,17 @@ export const BaseWorkflowByIdSchema = z.object({ }) .passthrough() .optional(), + pluginsInput: z + .object({ + merchantScreening: z + .object({ + requestPayload: z.record(z.string(), z.unknown()).optional(), + }) + .passthrough() + .optional(), + }) + .passthrough() + .optional(), metadata: z .object({ collectionFlowUrl: z.string().url().optional(), diff --git a/apps/backoffice-v2/src/lib/blocks/hooks/useMerchantScreeningBlock/columns.tsx b/apps/backoffice-v2/src/lib/blocks/hooks/useMerchantScreeningBlock/columns.tsx index 2ca6d9cfd5..5d92771d71 100644 --- a/apps/backoffice-v2/src/lib/blocks/hooks/useMerchantScreeningBlock/columns.tsx +++ b/apps/backoffice-v2/src/lib/blocks/hooks/useMerchantScreeningBlock/columns.tsx @@ -1,12 +1,11 @@ -import { createColumnHelper } from '@tanstack/react-table'; import { Button } from '@/common/components/atoms/Button/Button'; -import { ChevronDown } from 'lucide-react'; -import { ctw } from '@/common/utils/ctw/ctw'; -import { JsonDialog, TextWithNAFallback, WarningFilledSvg } from '@ballerine/ui'; import { IndicatorCircle } from '@/common/components/atoms/IndicatorCircle/IndicatorCircle'; -import React from 'react'; +import { ctw } from '@/common/utils/ctw/ctw'; import { IMerchantScreening } from '@/lib/blocks/hooks/useMerchantScreeningBlock/interfaces'; import { isObject, MatchReasonCode } from '@ballerine/common'; +import { JsonDialog, TextWithNAFallback, WarningFilledSvg } from '@ballerine/ui'; +import { createColumnHelper } from '@tanstack/react-table'; +import { ChevronDown } from 'lucide-react'; const columnHelper = createColumnHelper(); @@ -15,6 +14,7 @@ const summaryColumnHelper = createColumnHelper<{ numberOfInquiries: number; checkDate: string; fullJsonData: string; + merchantScreeningInput: Record; }>(); export const terminatedMatchedMerchantsColumns = [ @@ -123,8 +123,28 @@ export const terminatedMatchedMerchantsSummaryColumns = [ return {checkDate}; }, }), + summaryColumnHelper.accessor('merchantScreeningInput', { + header: 'Checked Properties', + cell: info => { + const fullJsonData = info.getValue(); + + return ( +
+ +
+ ); + }, + }), summaryColumnHelper.accessor('fullJsonData', { - header: 'Full JSON Data', + header: 'Results', cell: info => { const fullJsonData = info.getValue(); diff --git a/apps/backoffice-v2/src/lib/blocks/hooks/useMerchantScreeningBlock/useMerchantScreeningBlock.tsx b/apps/backoffice-v2/src/lib/blocks/hooks/useMerchantScreeningBlock/useMerchantScreeningBlock.tsx index 172d53a100..c83743a057 100644 --- a/apps/backoffice-v2/src/lib/blocks/hooks/useMerchantScreeningBlock/useMerchantScreeningBlock.tsx +++ b/apps/backoffice-v2/src/lib/blocks/hooks/useMerchantScreeningBlock/useMerchantScreeningBlock.tsx @@ -1,29 +1,31 @@ -import React, { useMemo } from 'react'; -import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; import { IndicatorCircle } from '@/common/components/atoms/IndicatorCircle/IndicatorCircle'; import { ReadOnlyDetail } from '@/common/components/atoms/ReadOnlyDetail/ReadOnlyDetail'; +import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; +import { useMemo } from 'react'; -import { IMerchantScreening } from '@/lib/blocks/hooks/useMerchantScreeningBlock/interfaces'; +import { ctw } from '@/common/utils/ctw/ctw'; import { inquiredMatchedMerchantsColumns, terminatedMatchedMerchantsColumns, terminatedMatchedMerchantsSummaryColumns, } from '@/lib/blocks/hooks/useMerchantScreeningBlock/columns'; -import { toTitleCase } from 'string-ts'; import { formatValue } from '@/lib/blocks/hooks/useMerchantScreeningBlock/format-value'; +import { IMerchantScreening } from '@/lib/blocks/hooks/useMerchantScreeningBlock/interfaces'; import { isObject, safeEvery } from '@ballerine/common'; -import { ctw } from '@/common/utils/ctw/ctw'; import { JsonDialog } from '@ballerine/ui'; +import { toTitleCase } from 'string-ts'; export const useMerchantScreeningBlock = ({ terminatedMatchedMerchants, inquiredMatchedMerchants, + merchantScreeningInput, logoUrl = 'https://cdn.ballerine.io/logos/Mastercard%20logo.svg', rawData, checkDate, }: { terminatedMatchedMerchants: IMerchantScreening[]; inquiredMatchedMerchants: IMerchantScreening[]; + merchantScreeningInput: Record; logoUrl: string; rawData: Record; checkDate: string; @@ -73,6 +75,7 @@ export const useMerchantScreeningBlock = ({ terminatedMatches: terminatedMatchedMerchants?.length ?? 0, numberOfInquiries: inquiredMatchedMerchants?.length ?? 0, checkDate, + merchantScreeningInput: merchantScreeningInput ?? {}, fullJsonData: rawData ?? {}, }, ], 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 7a46eaab6d..503e267073 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 @@ -1,17 +1,25 @@ import { MotionButton } from '@/common/components/molecules/MotionButton/MotionButton'; +import { useSearchParamsByEntity } from '@/common/hooks/useSearchParamsByEntity/useSearchParamsByEntity'; import { ctw } from '@/common/utils/ctw/ctw'; +import { omitPropsFromObjectWhitelist } from '@/common/utils/omit-props-from-object-whitelist/omit-props-from-object-whitelist'; import { useAuthenticatedUserQuery } from '@/domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; import { useRevisionTaskByIdMutation } from '@/domains/entities/hooks/mutations/useRevisionTaskByIdMutation/useRevisionTaskByIdMutation'; import { useStorageFilesQuery } from '@/domains/storage/hooks/queries/useStorageFilesQuery/useStorageFilesQuery'; +import { TWorkflowById } from '@/domains/workflows/fetchers'; import { useEventMutation } from '@/domains/workflows/hooks/mutations/useEventMutation/useEventMutation'; +import { useAmlBlock } from '@/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock'; +import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; +import { useAddressBlock } from '@/lib/blocks/hooks/useAddressBlock/useAddressBlock'; import { useAssociatedCompaniesInformationBlock } from '@/lib/blocks/hooks/useAssociatedCompaniesInformationBlock/useAssociatedCompaniesInformationBlock'; import { associatedCompanyAdapter } from '@/lib/blocks/hooks/useAssosciatedCompaniesBlock/associated-company-adapter'; +import { associatedCompanyToWorkflowAdapter } from '@/lib/blocks/hooks/useAssosciatedCompaniesBlock/associated-company-to-workflow-adapter'; import { motionButtonProps, useAssociatedCompaniesBlock, } from '@/lib/blocks/hooks/useAssosciatedCompaniesBlock/useAssociatedCompaniesBlock'; import { useBankingDetailsBlock } from '@/lib/blocks/hooks/useBankingDetailsBlock/useBankingDetailsBlock'; import { useCaseInfoBlock } from '@/lib/blocks/hooks/useCaseInfoBlock/useCaseInfoBlock'; +import { useCaseOverviewBlock } from '@/lib/blocks/hooks/useCaseOverviewBlock/useCaseOverviewBlock'; import { useCompanySanctionsBlock } from '@/lib/blocks/hooks/useCompanySanctionsBlock/useCompanySanctionsBlock'; import { useDirectorsBlocks } from '@/lib/blocks/hooks/useDirectorsBlocks'; import { useDirectorsRegistryProvidedBlock } from '@/lib/blocks/hooks/useDirectorsRegistryProvidedBlock/useDirectorsRegistryProvidedBlock'; @@ -24,6 +32,8 @@ import { useKybRegistryInfoBlock } from '@/lib/blocks/hooks/useKybRegistryInfoBl import { useMainContactBlock } from '@/lib/blocks/hooks/useMainContactBlock/useMainContactBlock'; import { useMainRepresentativeBlock } from '@/lib/blocks/hooks/useMainRepresentativeBlock/useMainRepresentativeBlock'; import { useMapBlock } from '@/lib/blocks/hooks/useMapBlock/useMapBlock'; +import { useMerchantScreeningBlock } from '@/lib/blocks/hooks/useMerchantScreeningBlock/useMerchantScreeningBlock'; +import { useObjectEntriesBlock } from '@/lib/blocks/hooks/useObjectEntriesBlock/useObjectEntriesBlock'; import { useProcessingDetailsBlock } from '@/lib/blocks/hooks/useProcessingDetailsBlock/useProcessingDetailsBlock'; import { useRegistryInfoBlock } from '@/lib/blocks/hooks/useRegistryInfoBlock/useRegistryInfoBlock'; import { useStoreInfoBlock } from '@/lib/blocks/hooks/useStoreInfoBlock/useStoreInfoBlock'; @@ -32,32 +42,22 @@ import { useUbosUserProvidedBlock } from '@/lib/blocks/hooks/useUbosUserProvided import { useWebsiteBasicRequirementBlock } from '@/lib/blocks/hooks/useWebsiteBasicRequirementBlock/useWebsiteBasicRequirementBlock'; import { useWebsiteMonitoringBlock } from '@/lib/blocks/hooks/useWebsiteMonitoringBlock/useWebsiteMonitoringBlock'; import { useCaseBlocks } from '@/lib/blocks/variants/DefaultBlocks/hooks/useCaseBlocksLogic/useCaseBlocks'; +import { useWebsiteMonitoringReportBlock } from '@/lib/blocks/variants/WebsiteMonitoringBlocks/hooks/useWebsiteMonitoringReportBlock/useWebsiteMonitoringReportBlock'; import { useCaseDecision } from '@/pages/Entity/components/Case/hooks/useCaseDecision/useCaseDecision'; import { useCaseState } from '@/pages/Entity/components/Case/hooks/useCaseState/useCaseState'; +import { getAddressDeep } from '@/pages/Entity/hooks/useEntityLogic/utils/get-address-deep/get-address-deep'; import { selectDirectorsDocuments } from '@/pages/Entity/selectors/selectDirectorsDocuments'; import { Send } from 'lucide-react'; import { useMemo } from 'react'; -import { useWebsiteMonitoringReportBlock } from '@/lib/blocks/variants/WebsiteMonitoringBlocks/hooks/useWebsiteMonitoringReportBlock/useWebsiteMonitoringReportBlock'; -import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; -import { useAddressBlock } from '@/lib/blocks/hooks/useAddressBlock/useAddressBlock'; -import { getAddressDeep } from '@/pages/Entity/hooks/useEntityLogic/utils/get-address-deep/get-address-deep'; -import { useCaseOverviewBlock } from '@/lib/blocks/hooks/useCaseOverviewBlock/useCaseOverviewBlock'; -import { useSearchParamsByEntity } from '@/common/hooks/useSearchParamsByEntity/useSearchParamsByEntity'; import { useLocation } from 'react-router-dom'; -import { omitPropsFromObjectWhitelist } from '@/common/utils/omit-props-from-object-whitelist/omit-props-from-object-whitelist'; -import { useObjectEntriesBlock } from '@/lib/blocks/hooks/useObjectEntriesBlock/useObjectEntriesBlock'; -import { useAmlBlock } from '@/lib/blocks/components/AmlBlock/hooks/useAmlBlock/useAmlBlock'; -import { associatedCompanyToWorkflowAdapter } from '@/lib/blocks/hooks/useAssosciatedCompaniesBlock/associated-company-to-workflow-adapter'; -import { useMerchantScreeningBlock } from '@/lib/blocks/hooks/useMerchantScreeningBlock/useMerchantScreeningBlock'; -import { TWorkflowById } from '@/domains/workflows/fetchers'; const registryInfoWhitelist = ['open_corporates'] as const; -import { Button } from '@ballerine/ui'; +import { useManageUbosBlock } from '@/lib/blocks/hooks/useManageUbosBlock/useManageUbosBlock'; import { useCurrentCaseQuery } from '@/pages/Entity/hooks/useCurrentCaseQuery/useCurrentCaseQuery'; -import { toast } from 'sonner'; +import { Button } from '@ballerine/ui'; import { useCallback } from 'react'; -import { useManageUbosBlock } from '@/lib/blocks/hooks/useManageUbosBlock/useManageUbosBlock'; +import { toast } from 'sonner'; export const useDefaultBlocksLogic = () => { const [{ activeTab }] = useSearchParamsByEntity(); @@ -469,6 +469,8 @@ export const useDefaultBlocksLogic = () => { inquiredMatchedMerchants: workflow?.context?.pluginsOutput?.merchantScreening?.processed?.inquiredMatchedMerchants ?? [], + merchantScreeningInput: + workflow?.context?.pluginsInput?.merchantScreening?.requestPayload || {}, logoUrl: workflow?.context?.pluginsOutput?.merchantScreening?.logoUrl, rawData: workflow?.context?.pluginsOutput?.merchantScreening?.raw, checkDate: workflow?.context?.pluginsOutput?.merchantScreening?.processed?.checkDate, From da9309bcfc3c580c42d7aee4e0f0fecacd4d8b27 Mon Sep 17 00:00:00 2001 From: Illia Rudniev Date: Wed, 22 Jan 2025 17:13:12 +0200 Subject: [PATCH 4/8] feat: implemented whitelist & updated tests --- packages/workflow-core/src/lib/constants.ts | 2 - .../api-plugin/api-plugin.test.ts | 162 +++++++++++++++--- .../external-plugin/api-plugin/api-plugin.ts | 38 ++-- .../external-plugin/api-plugin/constants.ts | 1 - ...stercard-merchant-screening-plugin.test.ts | 158 +++++++++++++++++ .../mastercard-merchant-screening-plugin.ts | 21 ++- .../src/lib/plugins/external-plugin/types.ts | 4 +- 7 files changed, 329 insertions(+), 57 deletions(-) delete mode 100644 packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/constants.ts create mode 100644 packages/workflow-core/src/lib/plugins/external-plugin/mastercard-merchant-screening-plugin.test.ts diff --git a/packages/workflow-core/src/lib/constants.ts b/packages/workflow-core/src/lib/constants.ts index de10baa231..56ac2038cd 100644 --- a/packages/workflow-core/src/lib/constants.ts +++ b/packages/workflow-core/src/lib/constants.ts @@ -44,5 +44,3 @@ export const pluginsRegistry = { new (...args: any[]) => any > >; - -export const REQUEST_PAYLOAD_BLACKLIST = ['callbackUrl']; diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.test.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.test.ts index 75002f1b84..ff5ee65002 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.test.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.test.ts @@ -1,3 +1,4 @@ +import { AnyRecord } from '@ballerine/common'; import { beforeEach, describe, expect, it, SpyInstance, vi } from 'vitest'; import { ApiPlugin } from './api-plugin'; @@ -7,7 +8,7 @@ describe('ApiPlugin', () => { vi.clearAllMocks(); }); - it('should call removeBlacklistedKeys', async () => { + it('should call generateRequestPayloadFromWhitelist', async () => { const context = { data: 'test' }; const apiPlugin = new ApiPlugin({ name: 'ballerineEnrichment', @@ -20,9 +21,13 @@ describe('ApiPlugin', () => { request: { transformers: [], }, + whitelistedInputProperties: undefined, }); - const removeBlacklistedKeysSpy = vi.spyOn(apiPlugin, 'removeBlacklistedKeys'); + const generateRequestPayloadFromWhitelistSpy = vi.spyOn( + apiPlugin, + 'generateRequestPayloadFromWhitelist', + ); const transformDataSpy = vi.spyOn(apiPlugin, 'transformData'); const validateContentSpy = vi.spyOn(apiPlugin, 'validateContent'); const makeApiRequestSpy = vi.spyOn(apiPlugin, 'makeApiRequest'); @@ -38,7 +43,8 @@ describe('ApiPlugin', () => { await apiPlugin.invoke(context); - expect(removeBlacklistedKeysSpy).toHaveBeenCalledWith(context); + expect(generateRequestPayloadFromWhitelistSpy).toHaveBeenCalledWith(context); + expect(generateRequestPayloadFromWhitelistSpy).toHaveBeenCalledTimes(1); }); describe('requestPayload', () => { @@ -86,26 +92,62 @@ describe('ApiPlugin', () => { expect(await apiPlugin.invoke(context)).toHaveProperty('requestPayload', context); }); - it('failed request should include requestPayload', async () => { - const context = { test: '123' }; + describe('failed request', () => { + it('should include requestPayload', async () => { + const context = { test: '123' }; - transformDataSpy.mockResolvedValue(context); - validateContentSpy.mockResolvedValue({ isValidRequest: false }); - makeApiRequestSpy.mockResolvedValue({ - statusText: 'OK', - ok: false, - json: () => Promise.resolve({}), - headers: new Headers(), + transformDataSpy.mockResolvedValue(context); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: false, + json: () => Promise.resolve({}), + headers: new Headers(), + }); + + const invokeResult = (await apiPlugin.invoke(context)) as { requestPayload: AnyRecord }; + + expect(invokeResult).toHaveProperty('requestPayload'); + expect(invokeResult.requestPayload).toContain(context); }); + }); - await apiPlugin.invoke(context); + describe('not valid response', () => { + it('should include requestPayload', async () => { + const context = { test: '123' }; - expect(await apiPlugin.invoke(context)).toHaveProperty('requestPayload', context); + transformDataSpy.mockResolvedValue(context); + validateContentSpy.mockResolvedValue({ isValidResponse: false }); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: true, + json: () => Promise.resolve({}), + headers: new Headers(), + }); + + const invokeResult = (await apiPlugin.invoke(context)) as { requestPayload: AnyRecord }; + + expect(invokeResult).toHaveProperty('requestPayload'); + expect(invokeResult.requestPayload).toEqual(context); + }); + }); + + describe('not valid request', () => { + it('should include requestPayload', async () => { + const context = { test: '123' }; + + transformDataSpy.mockResolvedValue(context); + validateContentSpy.mockResolvedValue({ isValidRequest: false }); + + const invokeResult = (await apiPlugin.invoke(context)) as { requestPayload: AnyRecord }; + + expect(invokeResult).toHaveProperty('requestPayload'); + expect(invokeResult.requestPayload).toEqual(context); + }); }); }); }); - describe('removeBlacklistedKeys', () => { + describe('generateRequestPayloadFromWhitelist', () => { let apiPlugin: ApiPlugin; beforeEach(() => { @@ -120,22 +162,88 @@ describe('ApiPlugin', () => { }); }); - it('removes blacklisted keys from request payload', () => { - const payload = { callbackUrl: 'https://example.com', data: 'test' }; - const result = apiPlugin.removeBlacklistedKeys(payload); - expect(result).toEqual({ data: 'test' }); + it('builds request payload from whitelisted input properties', () => { + apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + whitelistedInputProperties: ['allowedProp1', 'allowedProp2'], + }); + + const payload = { + allowedProp1: 'https://example.com', + allowedProp2: 'https://example.com123', + notAllowedProp1: 'https://example.com123', + notAllowedProp2: 'https://example.com123', + }; + const result = apiPlugin.generateRequestPayloadFromWhitelist(payload); + expect(result).toEqual({ + allowedProp1: 'https://example.com', + allowedProp2: 'https://example.com123', + }); }); - it('doesnt remove non-blacklisted keys', () => { - const payload = { callbackUrl: 'https://example.com', data: 'test' }; - const result = apiPlugin.removeBlacklistedKeys(payload); - expect(result).toEqual({ data: 'test' }); + it('should include nested objects of whitelisted properties', () => { + apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + whitelistedInputProperties: ['allowedProp1', 'allowedProp2'], + }); + + const payload = { + allowedProp1: 'https://example.com', + allowedProp2: { + nestedProp1: 'https://example.com123', + nestedProp2: 'https://example.com123', + }, + notAllowedProp1: 'https://example.com123', + notAllowedProp2: 'https://example.com123', + }; + const result = apiPlugin.generateRequestPayloadFromWhitelist(payload); + expect(result).toEqual({ + allowedProp1: 'https://example.com', + allowedProp2: { + nestedProp1: 'https://example.com123', + nestedProp2: 'https://example.com123', + }, + }); }); - it('correctly handles nested objects', () => { - const payload = { data: { callbackUrl: 'https://example.com' } }; - const result = apiPlugin.removeBlacklistedKeys(payload); - expect(result).toEqual({ data: {} }); + it('should not lookup for whitelisted properties in arrays', () => { + apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + whitelistedInputProperties: ['allowedProp1', 'allowedProp2'], + }); + + const payload = { + someArray: [{ allowedProp1: 'https://example.com' }], + allowedProp2: 'https://example.com', + }; + const result = apiPlugin.generateRequestPayloadFromWhitelist(payload); + expect(result).toEqual({ + allowedProp2: 'https://example.com', + }); + }); + + it('should not modify the original payload if no whitelisted properties are provided', () => { + const payload = { data: 'test' }; + const result = apiPlugin.generateRequestPayloadFromWhitelist(payload); + expect(result).toEqual({ data: 'test' }); }); }); }); diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts index 800fdbc122..f76d16a819 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts @@ -9,7 +9,6 @@ import { Validator, } from '../../../utils'; import { IApiPluginParams } from '../types'; -import { REQUEST_PAYLOAD_BLACKLIST } from './constants'; const invokedAtTransformer: HelpersTransformer = new HelpersTransformer([ { @@ -36,6 +35,7 @@ export class ApiPlugin { displayName: string | undefined; secretsManager: IApiPluginParams['secretsManager']; memoizedSecrets: Record | undefined; + whitelistedInputProperties: string[] | undefined; constructor(pluginParams: IApiPluginParams) { this.name = pluginParams.name; @@ -55,14 +55,17 @@ export class ApiPlugin { this.secretsManager = pluginParams.secretsManager; this.displayName = pluginParams.displayName; + this.whitelistedInputProperties = pluginParams.whitelistedInputProperties; } async invoke(context: TContext, additionalContext?: AnyRecord) { let requestPayload; + let outputRequestPayload; try { if (this.request && 'transformers' in this.request) { requestPayload = await this.transformData(this.request.transformers, context); + outputRequestPayload = this.generateRequestPayloadFromWhitelist(requestPayload); const { isValidRequest, errorMessage } = await this.validateContent( this.request.schemaValidator, @@ -71,7 +74,7 @@ export class ApiPlugin { ); if (!isValidRequest) { - return this.returnErrorResponse(errorMessage!, requestPayload); + return this.returnErrorResponse(errorMessage!, outputRequestPayload); } } @@ -85,8 +88,6 @@ export class ApiPlugin { method: this.method, }); - requestPayload = this.removeBlacklistedKeys(requestPayload); - const apiResponse = await this.makeApiRequest( _url, this.method, @@ -116,7 +117,7 @@ export class ApiPlugin { ); if (!isValidResponse) { - return this.returnErrorResponse(errorMessage!, requestPayload); + return this.returnErrorResponse(errorMessage!, outputRequestPayload); } if (this.successAction) { @@ -125,7 +126,7 @@ export class ApiPlugin { { ...responseBody, }, - requestPayload, + outputRequestPayload, ); } @@ -135,13 +136,13 @@ export class ApiPlugin { return this.returnErrorResponse( 'Request Failed: ' + apiResponse.statusText + ' Error: ' + JSON.stringify(errorResponse), - requestPayload, + outputRequestPayload, ); } } catch (error) { return this.returnErrorResponse( isErrorWithMessage(error) ? error.message : '', - requestPayload, + outputRequestPayload, ); } } @@ -427,23 +428,22 @@ export class ApiPlugin { }, record as unknown); } - removeBlacklistedKeys(payload: AnyRecord = {}) { - const payloadWithoutBlacklistedKeys: AnyRecord = structuredClone(payload); + generateRequestPayloadFromWhitelist(payload: AnyRecord = {}) { + if (!this.whitelistedInputProperties) return payload; - for (const key in payloadWithoutBlacklistedKeys) { - if (REQUEST_PAYLOAD_BLACKLIST.includes(key as (typeof REQUEST_PAYLOAD_BLACKLIST)[number])) { - delete payloadWithoutBlacklistedKeys[key]; - - continue; - } + const whitelistedPayload: AnyRecord = {}; + for (const key of this.whitelistedInputProperties) { const value = payload[key]; + whitelistedPayload[key] = value; + + if (value) continue; - if (typeof value === 'object') { - payloadWithoutBlacklistedKeys[key] = this.removeBlacklistedKeys(value as AnyRecord); + if (typeof value === 'object' && !Array.isArray(value)) { + whitelistedPayload[key] = this.generateRequestPayloadFromWhitelist(value as AnyRecord); } } - return payloadWithoutBlacklistedKeys; + return whitelistedPayload; } } diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/constants.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/constants.ts deleted file mode 100644 index fecc8ebeec..0000000000 --- a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const REQUEST_PAYLOAD_BLACKLIST = ['callbackUrl'] as const; diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/mastercard-merchant-screening-plugin.test.ts b/packages/workflow-core/src/lib/plugins/external-plugin/mastercard-merchant-screening-plugin.test.ts new file mode 100644 index 0000000000..62e4b0d78a --- /dev/null +++ b/packages/workflow-core/src/lib/plugins/external-plugin/mastercard-merchant-screening-plugin.test.ts @@ -0,0 +1,158 @@ +import { AnyRecord } from '@ballerine/common'; +import { beforeEach, describe, expect, it, SpyInstance, vi } from 'vitest'; +import { MastercardMerchantScreeningPlugin } from './mastercard-merchant-screening-plugin'; + +describe('Mastercard Merchant Screening Plugin', () => { + it('should use default whitelisted properties', () => { + const plugin = new MastercardMerchantScreeningPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + }); + expect(plugin.whitelistedInputProperties).toEqual(['searchGlobally', 'merchant', 'principals']); + }); + + describe('invoke', () => { + describe('requestPayload', () => { + let mastercardMerchantScreeningPlugin: MastercardMerchantScreeningPlugin; + let transformDataSpy: ReturnType; + let validateContentSpy: ReturnType; + let makeApiRequestSpy: ReturnType; + + beforeEach(() => { + vi.clearAllMocks(); + + mastercardMerchantScreeningPlugin = new MastercardMerchantScreeningPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + response: { + transformers: [], + }, + request: { + transformers: [], + }, + }); + + transformDataSpy = vi.spyOn( + mastercardMerchantScreeningPlugin, + 'transformData', + ) as SpyInstance; + validateContentSpy = vi.spyOn( + mastercardMerchantScreeningPlugin, + 'validateContent', + ) as SpyInstance; + makeApiRequestSpy = vi.spyOn( + mastercardMerchantScreeningPlugin, + 'makeApiRequest', + ) as SpyInstance; + }); + + it('status ok should include requestPayload', async () => { + const context = { searchGlobally: true, merchant: {}, principals: [] }; + + transformDataSpy.mockResolvedValue(context); + validateContentSpy.mockResolvedValue({ isValidRequest: true }); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: true, + json: () => Promise.resolve({}), + headers: new Headers(), + }); + + const invokeResult = (await mastercardMerchantScreeningPlugin.invoke(context)) as { + requestPayload: AnyRecord; + }; + + expect(invokeResult).toHaveProperty('requestPayload'); + expect(invokeResult.requestPayload).toHaveProperty('merchant'); + expect(invokeResult.requestPayload).toHaveProperty('principals'); + expect(invokeResult.requestPayload).toHaveProperty('searchGlobally'); + }); + + it('failed response should include requestPayload', async () => { + const context = { + searchGlobally: true, + merchant: {}, + principals: [], + }; + + transformDataSpy.mockResolvedValue(context); + validateContentSpy.mockResolvedValue({ isValidResponse: false }); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: true, + json: () => Promise.resolve({}), + headers: new Headers(), + }); + + const invokeResult = (await mastercardMerchantScreeningPlugin.invoke(context)) as { + requestPayload: AnyRecord; + }; + + expect(invokeResult).toHaveProperty('requestPayload'); + expect(Object.keys(invokeResult.requestPayload)).toEqual(Object.keys(context)); + }); + + it('failed request should include requestPayload', async () => { + const context = { searchGlobally: true, merchant: {}, principals: [] }; + + transformDataSpy.mockResolvedValue(context); + validateContentSpy.mockResolvedValue({ isValidRequest: true }); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: false, + json: () => Promise.resolve({}), + headers: new Headers(), + }); + + const invokeResult = (await mastercardMerchantScreeningPlugin.invoke(context)) as { + requestPayload: AnyRecord; + }; + + expect(invokeResult).toHaveProperty('requestPayload'); + expect(Object.keys(invokeResult.requestPayload)).toEqual(Object.keys(context)); + }); + }); + }); + + describe('removeBlacklistedKeys', () => { + let mastercardMerchantScreeningPlugin: MastercardMerchantScreeningPlugin; + + beforeEach(() => { + mastercardMerchantScreeningPlugin = new MastercardMerchantScreeningPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + whitelistedInputProperties: ['searchGlobally', 'merchant', 'principals'], + }); + }); + + it('removes non-whitelisted keys from request payload', () => { + const payload = { + searchGlobally: true, + merchant: {}, + principals: [], + notWhitelisted: 'Hello World', + }; + const result = mastercardMerchantScreeningPlugin.generateRequestPayloadFromWhitelist(payload); + expect(result).toEqual({ + searchGlobally: true, + merchant: {}, + principals: [], + }); + }); + }); +}); diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/mastercard-merchant-screening-plugin.ts b/packages/workflow-core/src/lib/plugins/external-plugin/mastercard-merchant-screening-plugin.ts index acc8e0365c..03d3f39e6c 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/mastercard-merchant-screening-plugin.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/mastercard-merchant-screening-plugin.ts @@ -13,6 +13,7 @@ export class MastercardMerchantScreeningPlugin extends ApiPlugin { super({ ...pluginParams, method: 'POST' as const, + whitelistedInputProperties: ['searchGlobally', 'merchant', 'principals'], }); } @@ -21,7 +22,6 @@ export class MastercardMerchantScreeningPlugin extends ApiPlugin { if (this.request && 'transformers' in this.request && this.request.transformers) { requestPayload = await this.transformData(this.request.transformers, context); - const { isValidRequest, errorMessage } = await this.validateContent( this.request.schemaValidator, requestPayload, @@ -29,7 +29,10 @@ export class MastercardMerchantScreeningPlugin extends ApiPlugin { ); if (!isValidRequest) { - return this.returnErrorResponse(errorMessage!); + return this.returnErrorResponse( + errorMessage!, + this.generateRequestPayloadFromWhitelist(requestPayload), + ); } } @@ -103,7 +106,10 @@ export class MastercardMerchantScreeningPlugin extends ApiPlugin { ); if (!isValidResponse) { - return this.returnErrorResponse(errorMessage!, requestPayload); + return this.returnErrorResponse( + errorMessage!, + this.generateRequestPayloadFromWhitelist(requestPayload), + ); } if (this.successAction) { @@ -112,7 +118,7 @@ export class MastercardMerchantScreeningPlugin extends ApiPlugin { { ...responseBody, }, - requestPayload, + this.generateRequestPayloadFromWhitelist(requestPayload), ); } @@ -122,13 +128,16 @@ export class MastercardMerchantScreeningPlugin extends ApiPlugin { return this.returnErrorResponse( 'Request Failed: ' + apiResponse.statusText + ' Error: ' + JSON.stringify(errorResponse), - requestPayload, + this.generateRequestPayloadFromWhitelist(requestPayload), ); } } catch (error) { logger.error('Error occurred while sending an API request', { error }); - return this.returnErrorResponse(isErrorWithMessage(error) ? error.message : ''); + return this.returnErrorResponse( + isErrorWithMessage(error) ? error.message : '', + this.generateRequestPayloadFromWhitelist(requestPayload), + ); } } } diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/types.ts b/packages/workflow-core/src/lib/plugins/external-plugin/types.ts index f4d3a55057..486d207dc6 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/types.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/types.ts @@ -2,8 +2,8 @@ import { TJsonSchema, Transformers, Validator } from '../../utils'; import { THelperFormatingLogic } from '../../utils/context-transformers/types'; import { ActionablePlugin } from '../types'; -import { AnyRecord } from '@ballerine/common'; import { SecretsManager } from '@/lib/types'; +import { AnyRecord } from '@ballerine/common'; import { ApiEmailTemplates } from './vendor-consts'; export interface ValidatableTransformer { @@ -27,7 +27,7 @@ export interface IApiPluginParams { persistResponseDestination?: string; displayName: string | undefined; secretsManager?: SecretsManager; - + whitelistedInputProperties?: string[]; invoke?(...args: any[]): any; } From ac1dc65a8790c40101a2c548ac8e9e6f74b1de1b Mon Sep 17 00:00:00 2001 From: Illia Rudniev Date: Wed, 22 Jan 2025 18:00:13 +0200 Subject: [PATCH 5/8] feat: invokedAt is now can be disbabled --- .../api-plugin/api-plugin.test.ts | 237 +++++++++++------- .../external-plugin/api-plugin/api-plugin.ts | 22 +- .../src/lib/plugins/external-plugin/types.ts | 1 + 3 files changed, 169 insertions(+), 91 deletions(-) diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.test.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.test.ts index ff5ee65002..b8ec7b88ba 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.test.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.test.ts @@ -1,9 +1,9 @@ import { AnyRecord } from '@ballerine/common'; import { beforeEach, describe, expect, it, SpyInstance, vi } from 'vitest'; -import { ApiPlugin } from './api-plugin'; +import { ApiPlugin, invokedAtTransformerDefinition } from './api-plugin'; describe('ApiPlugin', () => { - describe('apiPlugin.invoke', async () => { + describe('apiPlugin.invoke', () => { beforeEach(() => { vi.clearAllMocks(); }); @@ -145,105 +145,174 @@ describe('ApiPlugin', () => { }); }); }); - }); - describe('generateRequestPayloadFromWhitelist', () => { - let apiPlugin: ApiPlugin; + describe('includeInvokedAt', () => { + let apiPlugin: ApiPlugin; + let transformDataSpy: SpyInstance; + let validateContentSpy: SpyInstance; + let makeApiRequestSpy: SpyInstance; - beforeEach(() => { - apiPlugin = new ApiPlugin({ - name: 'ballerineEnrichment', - displayName: 'Ballerine Enrichment', - url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', - method: 'GET' as const, - stateNames: ['checkBusinessScore'], - successAction: 'API_CALL_SUCCESS', - errorAction: 'API_CALL_FAILURE', + beforeEach(() => { + vi.clearAllMocks(); + + apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + request: { transformers: [] }, + response: { transformers: [] }, + includeInvokedAt: true, + }); + + transformDataSpy = vi.spyOn(apiPlugin, 'transformData') as SpyInstance; + validateContentSpy = vi.spyOn(apiPlugin, 'validateContent') as SpyInstance; + makeApiRequestSpy = vi.spyOn(apiPlugin, 'makeApiRequest') as SpyInstance; }); - }); - it('builds request payload from whitelisted input properties', () => { - apiPlugin = new ApiPlugin({ - name: 'ballerineEnrichment', - displayName: 'Ballerine Enrichment', - url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', - method: 'GET' as const, - stateNames: ['checkBusinessScore'], - successAction: 'API_CALL_SUCCESS', - errorAction: 'API_CALL_FAILURE', - whitelistedInputProperties: ['allowedProp1', 'allowedProp2'], + it('should include invokedAt transformer if includeInvokedAt is true', async () => { + apiPlugin.includeInvokedAt = true; + + const context = { test: '123' }; + const response = {}; + + validateContentSpy.mockResolvedValue({ isValidResponse: true, isValidRequest: true }); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: true, + json: () => Promise.resolve(response), + headers: new Headers(), + }); + + await apiPlugin.invoke(context); + + expect(transformDataSpy).toHaveBeenLastCalledWith( + [expect.objectContaining({ mapping: [invokedAtTransformerDefinition] })], + expect.objectContaining({ invokedAt: expect.any(Number) }), + ); }); - const payload = { - allowedProp1: 'https://example.com', - allowedProp2: 'https://example.com123', - notAllowedProp1: 'https://example.com123', - notAllowedProp2: 'https://example.com123', - }; - const result = apiPlugin.generateRequestPayloadFromWhitelist(payload); - expect(result).toEqual({ - allowedProp1: 'https://example.com', - allowedProp2: 'https://example.com123', + it('should not include invokedAt transformer if includeInvokedAt is false', async () => { + apiPlugin.includeInvokedAt = false; + + const context = { test: '123' }; + const response = {}; + + validateContentSpy.mockResolvedValue({ isValidResponse: true, isValidRequest: true }); + makeApiRequestSpy.mockResolvedValue({ + statusText: 'OK', + ok: true, + json: () => Promise.resolve(response), + headers: new Headers(), + }); + + await apiPlugin.invoke(context); + + expect(transformDataSpy).toHaveBeenLastCalledWith([], response); }); }); - it('should include nested objects of whitelisted properties', () => { - apiPlugin = new ApiPlugin({ - name: 'ballerineEnrichment', - displayName: 'Ballerine Enrichment', - url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', - method: 'GET' as const, - stateNames: ['checkBusinessScore'], - successAction: 'API_CALL_SUCCESS', - errorAction: 'API_CALL_FAILURE', - whitelistedInputProperties: ['allowedProp1', 'allowedProp2'], + describe('generateRequestPayloadFromWhitelist', () => { + let apiPlugin: ApiPlugin; + + beforeEach(() => { + apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + }); }); - const payload = { - allowedProp1: 'https://example.com', - allowedProp2: { - nestedProp1: 'https://example.com123', - nestedProp2: 'https://example.com123', - }, - notAllowedProp1: 'https://example.com123', - notAllowedProp2: 'https://example.com123', - }; - const result = apiPlugin.generateRequestPayloadFromWhitelist(payload); - expect(result).toEqual({ - allowedProp1: 'https://example.com', - allowedProp2: { - nestedProp1: 'https://example.com123', - nestedProp2: 'https://example.com123', - }, + it('builds request payload from whitelisted input properties', () => { + apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + whitelistedInputProperties: ['allowedProp1', 'allowedProp2'], + }); + + const payload = { + allowedProp1: 'https://example.com', + allowedProp2: 'https://example.com123', + notAllowedProp1: 'https://example.com123', + notAllowedProp2: 'https://example.com123', + }; + const result = apiPlugin.generateRequestPayloadFromWhitelist(payload); + expect(result).toEqual({ + allowedProp1: 'https://example.com', + allowedProp2: 'https://example.com123', + }); }); - }); - it('should not lookup for whitelisted properties in arrays', () => { - apiPlugin = new ApiPlugin({ - name: 'ballerineEnrichment', - displayName: 'Ballerine Enrichment', - url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', - method: 'GET' as const, - stateNames: ['checkBusinessScore'], - successAction: 'API_CALL_SUCCESS', - errorAction: 'API_CALL_FAILURE', - whitelistedInputProperties: ['allowedProp1', 'allowedProp2'], + it('should include nested objects of whitelisted properties', () => { + apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + whitelistedInputProperties: ['allowedProp1', 'allowedProp2'], + }); + + const payload = { + allowedProp1: 'https://example.com', + allowedProp2: { + nestedProp1: 'https://example.com123', + nestedProp2: 'https://example.com123', + }, + notAllowedProp1: 'https://example.com123', + notAllowedProp2: 'https://example.com123', + }; + const result = apiPlugin.generateRequestPayloadFromWhitelist(payload); + expect(result).toEqual({ + allowedProp1: 'https://example.com', + allowedProp2: { + nestedProp1: 'https://example.com123', + nestedProp2: 'https://example.com123', + }, + }); }); - const payload = { - someArray: [{ allowedProp1: 'https://example.com' }], - allowedProp2: 'https://example.com', - }; - const result = apiPlugin.generateRequestPayloadFromWhitelist(payload); - expect(result).toEqual({ - allowedProp2: 'https://example.com', + it('should not lookup for whitelisted properties in arrays', () => { + apiPlugin = new ApiPlugin({ + name: 'ballerineEnrichment', + displayName: 'Ballerine Enrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.jsonn', + method: 'GET' as const, + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + whitelistedInputProperties: ['allowedProp1', 'allowedProp2'], + }); + + const payload = { + someArray: [{ allowedProp1: 'https://example.com' }], + allowedProp2: 'https://example.com', + }; + const result = apiPlugin.generateRequestPayloadFromWhitelist(payload); + expect(result).toEqual({ + allowedProp2: 'https://example.com', + }); }); - }); - it('should not modify the original payload if no whitelisted properties are provided', () => { - const payload = { data: 'test' }; - const result = apiPlugin.generateRequestPayloadFromWhitelist(payload); - expect(result).toEqual({ data: 'test' }); + it('should not modify the original payload if no whitelisted properties are provided', () => { + const payload = { data: 'test' }; + const result = apiPlugin.generateRequestPayloadFromWhitelist(payload); + expect(result).toEqual({ data: 'test' }); + }); }); }); }); diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts index f76d16a819..1515ac7ced 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts @@ -10,12 +10,14 @@ import { } from '../../../utils'; import { IApiPluginParams } from '../types'; -const invokedAtTransformer: HelpersTransformer = new HelpersTransformer([ - { - source: 'invokedAt', - target: 'invokedAt', - method: 'setTimeToRecordUTC', - }, +export const invokedAtTransformerDefinition = { + source: 'invokedAt', + target: 'invokedAt', + method: 'setTimeToRecordUTC', +}; + +export const invokedAtTransformer: HelpersTransformer = new HelpersTransformer([ + invokedAtTransformerDefinition, ] as THelperFormatingLogic); export class ApiPlugin { @@ -36,6 +38,7 @@ export class ApiPlugin { secretsManager: IApiPluginParams['secretsManager']; memoizedSecrets: Record | undefined; whitelistedInputProperties: string[] | undefined; + includeInvokedAt: boolean; constructor(pluginParams: IApiPluginParams) { this.name = pluginParams.name; @@ -56,6 +59,7 @@ export class ApiPlugin { this.displayName = pluginParams.displayName; this.whitelistedInputProperties = pluginParams.whitelistedInputProperties; + this.includeInvokedAt = pluginParams.includeInvokedAt ?? true; } async invoke(context: TContext, additionalContext?: AnyRecord) { @@ -103,10 +107,14 @@ export class ApiPlugin { url: _url, }); + console.log('apiResponse', apiResponse.ok); + if (apiResponse.ok) { const result = await apiResponse.json(); - const responseTransformers = [...(this.response?.transformers || []), invokedAtTransformer]; + const responseTransformers = this.includeInvokedAt + ? [...(this.response?.transformers || []), invokedAtTransformer] + : this.response?.transformers || []; const responseBody = await this.transformData(responseTransformers, result as AnyRecord); diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/types.ts b/packages/workflow-core/src/lib/plugins/external-plugin/types.ts index 486d207dc6..3791077698 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/types.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/types.ts @@ -28,6 +28,7 @@ export interface IApiPluginParams { displayName: string | undefined; secretsManager?: SecretsManager; whitelistedInputProperties?: string[]; + includeInvokedAt?: boolean; invoke?(...args: any[]): any; } From c8acb86476d3d4a4570b76a1abe596b6e43e9033 Mon Sep 17 00:00:00 2001 From: Illia Rudniev Date: Wed, 22 Jan 2025 18:02:10 +0200 Subject: [PATCH 6/8] feat: workflow-core bump --- apps/backoffice-v2/CHANGELOG.md | 7 ++++++ apps/backoffice-v2/package.json | 6 ++--- apps/kyb-app/CHANGELOG.md | 6 +++++ apps/kyb-app/package.json | 4 ++-- examples/headless-example/CHANGELOG.md | 6 +++++ examples/headless-example/package.json | 4 ++-- packages/workflow-core/CHANGELOG.md | 6 +++++ packages/workflow-core/package.json | 2 +- pnpm-lock.yaml | 24 +++++++++---------- sdks/workflow-browser-sdk/CHANGELOG.md | 7 ++++++ sdks/workflow-browser-sdk/package.json | 4 ++-- sdks/workflow-node-sdk/CHANGELOG.md | 7 ++++++ sdks/workflow-node-sdk/package.json | 4 ++-- services/workflows-service/CHANGELOG.md | 8 +++++++ services/workflows-service/package.json | 6 ++--- .../workflows-service/prisma/data-migrations | 2 +- 16 files changed, 75 insertions(+), 28 deletions(-) diff --git a/apps/backoffice-v2/CHANGELOG.md b/apps/backoffice-v2/CHANGELOG.md index 82a0e0e189..f0852443ff 100644 --- a/apps/backoffice-v2/CHANGELOG.md +++ b/apps/backoffice-v2/CHANGELOG.md @@ -1,5 +1,12 @@ # @ballerine/backoffice-v2 +## 0.7.96 + +### Patch Changes + +- @ballerine/workflow-browser-sdk@0.6.88 +- @ballerine/workflow-node-sdk@0.6.88 + ## 0.7.95 ### Patch Changes diff --git a/apps/backoffice-v2/package.json b/apps/backoffice-v2/package.json index 3c7b17fef7..a50635b28e 100644 --- a/apps/backoffice-v2/package.json +++ b/apps/backoffice-v2/package.json @@ -1,6 +1,6 @@ { "name": "@ballerine/backoffice-v2", - "version": "0.7.95", + "version": "0.7.96", "description": "Ballerine - Backoffice", "homepage": "https://github.com/ballerine-io/ballerine", "type": "module", @@ -54,8 +54,8 @@ "dependencies": { "@ballerine/blocks": "0.2.34", "@ballerine/common": "0.9.68", - "@ballerine/workflow-browser-sdk": "0.6.87", - "@ballerine/workflow-node-sdk": "0.6.87", + "@ballerine/workflow-browser-sdk": "0.6.88", + "@ballerine/workflow-node-sdk": "0.6.88", "@ballerine/react-pdf-toolkit": "^1.2.67", "@ballerine/ui": "^0.5.67", "@botpress/webchat": "^2.1.10", diff --git a/apps/kyb-app/CHANGELOG.md b/apps/kyb-app/CHANGELOG.md index 66211e79c8..a70681b319 100644 --- a/apps/kyb-app/CHANGELOG.md +++ b/apps/kyb-app/CHANGELOG.md @@ -1,5 +1,11 @@ # kyb-app +## 0.3.115 + +### Patch Changes + +- @ballerine/workflow-browser-sdk@0.6.88 + ## 0.3.114 ### Patch Changes diff --git a/apps/kyb-app/package.json b/apps/kyb-app/package.json index 2c9a38c4c3..b291cf580e 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.3.114", + "version": "0.3.115", "type": "module", "scripts": { "dev": "vite", @@ -18,7 +18,7 @@ "dependencies": { "@ballerine/blocks": "0.2.34", "@ballerine/common": "^0.9.68", - "@ballerine/workflow-browser-sdk": "0.6.87", + "@ballerine/workflow-browser-sdk": "0.6.88", "@ballerine/ui": "0.5.67", "@lukemorales/query-key-factory": "^1.0.3", "@radix-ui/react-icons": "^1.3.0", diff --git a/examples/headless-example/CHANGELOG.md b/examples/headless-example/CHANGELOG.md index 704624f77c..65660e5906 100644 --- a/examples/headless-example/CHANGELOG.md +++ b/examples/headless-example/CHANGELOG.md @@ -1,5 +1,11 @@ # @ballerine/headless-example +## 0.3.87 + +### Patch Changes + +- @ballerine/workflow-browser-sdk@0.6.88 + ## 0.3.86 ### Patch Changes diff --git a/examples/headless-example/package.json b/examples/headless-example/package.json index 75e19648f9..32339203b5 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.3.86", + "version": "0.3.87", "type": "module", "scripts": { "spellcheck": "cspell \"*\"", @@ -35,7 +35,7 @@ }, "dependencies": { "@ballerine/common": "0.9.68", - "@ballerine/workflow-browser-sdk": "0.6.87", + "@ballerine/workflow-browser-sdk": "0.6.88", "@felte/reporter-svelte": "^1.1.5", "@felte/validator-zod": "^1.0.13", "@fontsource/inter": "^4.5.15", diff --git a/packages/workflow-core/CHANGELOG.md b/packages/workflow-core/CHANGELOG.md index f5ce815677..a3c4bf102b 100644 --- a/packages/workflow-core/CHANGELOG.md +++ b/packages/workflow-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @ballerine/workflow-core +## 0.6.88 + +### Patch Changes + +- Added pluginsInput with requestPayload per plugins & invokedAt now disablable + ## 0.6.87 ### Patch Changes diff --git a/packages/workflow-core/package.json b/packages/workflow-core/package.json index 50efaa154d..2217140191 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.6.87", + "version": "0.6.88", "description": "workflow-core", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b83602af1a..7048be94d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -85,10 +85,10 @@ importers: specifier: ^0.5.67 version: link:../../packages/ui '@ballerine/workflow-browser-sdk': - specifier: 0.6.87 + specifier: 0.6.88 version: link:../../sdks/workflow-browser-sdk '@ballerine/workflow-node-sdk': - specifier: 0.6.87 + specifier: 0.6.88 version: link:../../sdks/workflow-node-sdk '@botpress/webchat': specifier: ^2.1.10 @@ -539,7 +539,7 @@ importers: specifier: 0.5.67 version: link:../../packages/ui '@ballerine/workflow-browser-sdk': - specifier: 0.6.87 + specifier: 0.6.88 version: link:../../sdks/workflow-browser-sdk '@lukemorales/query-key-factory': specifier: ^1.0.3 @@ -1015,7 +1015,7 @@ importers: specifier: 0.9.68 version: link:../../packages/common '@ballerine/workflow-browser-sdk': - specifier: 0.6.87 + specifier: 0.6.88 version: link:../../sdks/workflow-browser-sdk '@felte/reporter-svelte': specifier: ^1.1.5 @@ -2289,7 +2289,7 @@ importers: specifier: 0.9.68 version: link:../../packages/common '@ballerine/workflow-core': - specifier: 0.6.87 + specifier: 0.6.88 version: link:../../packages/workflow-core xstate: specifier: ^4.37.0 @@ -2428,7 +2428,7 @@ importers: sdks/workflow-node-sdk: dependencies: '@ballerine/workflow-core': - specifier: 0.6.87 + specifier: 0.6.88 version: link:../../packages/workflow-core json-logic-js: specifier: ^2.0.2 @@ -2676,10 +2676,10 @@ importers: specifier: 0.9.68 version: link:../../packages/common '@ballerine/workflow-core': - specifier: 0.6.87 + specifier: 0.6.88 version: link:../../packages/workflow-core '@ballerine/workflow-node-sdk': - specifier: 0.6.87 + specifier: 0.6.88 version: link:../../sdks/workflow-node-sdk '@faker-js/faker': specifier: ^7.6.0 @@ -20306,7 +20306,7 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.23.3) '@types/babel__core': 7.20.4 react-refresh: 0.14.0 - vite: 4.5.3(@types/node@20.9.2) + vite: 4.5.3(@types/node@18.17.19) transitivePeerDependencies: - supports-color dev: true @@ -38536,7 +38536,7 @@ packages: strip-ansi: 6.0.1 tiny-invariant: 1.3.1 typescript: 5.1.6 - vite: 4.5.3(@types/node@20.9.2) + vite: 4.5.3(@types/node@18.17.19) vscode-languageclient: 7.0.0 vscode-languageserver: 7.0.0 vscode-languageserver-textdocument: 1.0.11 @@ -38667,7 +38667,7 @@ packages: kolorist: 1.8.0 sirv: 2.0.3 ufo: 1.3.2 - vite: 4.5.3(@types/node@20.9.2) + vite: 4.5.3(@types/node@18.17.19) transitivePeerDependencies: - rollup - supports-color @@ -38746,7 +38746,7 @@ packages: debug: 4.3.4(supports-color@8.1.1) globrex: 0.1.2 tsconfck: 2.1.2(typescript@5.1.6) - vite: 4.5.3(@types/node@20.9.2) + vite: 4.5.3(@types/node@18.17.19) transitivePeerDependencies: - supports-color - typescript diff --git a/sdks/workflow-browser-sdk/CHANGELOG.md b/sdks/workflow-browser-sdk/CHANGELOG.md index b786fef346..b7533bbf8e 100644 --- a/sdks/workflow-browser-sdk/CHANGELOG.md +++ b/sdks/workflow-browser-sdk/CHANGELOG.md @@ -1,5 +1,12 @@ # @ballerine/workflow-browser-sdk +## 0.6.88 + +### Patch Changes + +- Updated dependencies + - @ballerine/workflow-core@0.6.88 + ## 0.6.87 ### Patch Changes diff --git a/sdks/workflow-browser-sdk/package.json b/sdks/workflow-browser-sdk/package.json index 48b96d09b5..86c244f53b 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.6.87", + "version": "0.6.88", "description": "workflow-browser-sdk", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", @@ -34,7 +34,7 @@ }, "dependencies": { "@ballerine/common": "0.9.68", - "@ballerine/workflow-core": "0.6.87", + "@ballerine/workflow-core": "0.6.88", "xstate": "^4.37.0" }, "devDependencies": { diff --git a/sdks/workflow-node-sdk/CHANGELOG.md b/sdks/workflow-node-sdk/CHANGELOG.md index 0e3ee7eaf8..b09b2ebad8 100644 --- a/sdks/workflow-node-sdk/CHANGELOG.md +++ b/sdks/workflow-node-sdk/CHANGELOG.md @@ -1,5 +1,12 @@ # @ballerine/workflow-node-sdk +## 0.6.88 + +### Patch Changes + +- Updated dependencies + - @ballerine/workflow-core@0.6.88 + ## 0.6.87 ### Patch Changes diff --git a/sdks/workflow-node-sdk/package.json b/sdks/workflow-node-sdk/package.json index acb7d7341a..8b4f5e9105 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.6.87", + "version": "0.6.88", "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.6.87", + "@ballerine/workflow-core": "0.6.88", "json-logic-js": "^2.0.2", "xstate": "^4.36.0" }, diff --git a/services/workflows-service/CHANGELOG.md b/services/workflows-service/CHANGELOG.md index 53c65b7712..85fa32d77c 100644 --- a/services/workflows-service/CHANGELOG.md +++ b/services/workflows-service/CHANGELOG.md @@ -1,5 +1,13 @@ # @ballerine/workflows-service +## 0.7.92 + +### Patch Changes + +- Updated dependencies + - @ballerine/workflow-core@0.6.88 + - @ballerine/workflow-node-sdk@0.6.88 + ## 0.7.91 ### Patch Changes diff --git a/services/workflows-service/package.json b/services/workflows-service/package.json index 2e9b602326..520b2ecb49 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.7.91", + "version": "0.7.92", "description": "workflow-service", "scripts": { "spellcheck": "cspell \"*\"", @@ -51,8 +51,8 @@ "@aws-sdk/lib-storage": "3.347.1", "@aws-sdk/s3-request-presigner": "3.347.1", "@ballerine/common": "0.9.68", - "@ballerine/workflow-core": "0.6.87", - "@ballerine/workflow-node-sdk": "0.6.87", + "@ballerine/workflow-core": "0.6.88", + "@ballerine/workflow-node-sdk": "0.6.88", "@faker-js/faker": "^7.6.0", "@nestjs/axios": "^2.0.0", "@nestjs/common": "^9.3.12", diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index cccba77231..742886837a 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit cccba77231119da4db772844cc83c55785d938de +Subproject commit 742886837ac9d818aa96740afa8cc1732e5c7a95 From 9cf9d7c18fe7ec2189e1ac752ea363f0c2ebe8c2 Mon Sep 17 00:00:00 2001 From: Illia Rudniev Date: Wed, 22 Jan 2025 18:02:58 +0200 Subject: [PATCH 7/8] fix: clean --- .../src/lib/plugins/external-plugin/api-plugin/api-plugin.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts index 1515ac7ced..b847daac89 100644 --- a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin/api-plugin.ts @@ -107,8 +107,6 @@ export class ApiPlugin { url: _url, }); - console.log('apiResponse', apiResponse.ok); - if (apiResponse.ok) { const result = await apiResponse.json(); From 1ba46893de2287d8405b1c06f81f8d1f621b271c Mon Sep 17 00:00:00 2001 From: Omri Levy Date: Thu, 23 Jan 2025 15:36:24 +0200 Subject: [PATCH 8/8] chore(*): updated workflow-core --- apps/backoffice-v2/CHANGELOG.md | 7 +++++++ apps/backoffice-v2/package.json | 6 +++--- apps/kyb-app/CHANGELOG.md | 6 ++++++ apps/kyb-app/package.json | 4 ++-- examples/headless-example/CHANGELOG.md | 6 ++++++ examples/headless-example/package.json | 4 ++-- packages/workflow-core/CHANGELOG.md | 6 ++++++ packages/workflow-core/package.json | 2 +- pnpm-lock.yaml | 20 +++++++++---------- sdks/workflow-browser-sdk/CHANGELOG.md | 7 +++++++ sdks/workflow-browser-sdk/package.json | 4 ++-- sdks/workflow-node-sdk/CHANGELOG.md | 7 +++++++ sdks/workflow-node-sdk/package.json | 4 ++-- services/workflows-service/CHANGELOG.md | 8 ++++++++ services/workflows-service/package.json | 6 +++--- .../workflows-service/prisma/data-migrations | 2 +- 16 files changed, 73 insertions(+), 26 deletions(-) diff --git a/apps/backoffice-v2/CHANGELOG.md b/apps/backoffice-v2/CHANGELOG.md index f0852443ff..e6949d710a 100644 --- a/apps/backoffice-v2/CHANGELOG.md +++ b/apps/backoffice-v2/CHANGELOG.md @@ -1,5 +1,12 @@ # @ballerine/backoffice-v2 +## 0.7.97 + +### Patch Changes + +- @ballerine/workflow-browser-sdk@0.6.89 +- @ballerine/workflow-node-sdk@0.6.89 + ## 0.7.96 ### Patch Changes diff --git a/apps/backoffice-v2/package.json b/apps/backoffice-v2/package.json index a50635b28e..5cd39a4dfe 100644 --- a/apps/backoffice-v2/package.json +++ b/apps/backoffice-v2/package.json @@ -1,6 +1,6 @@ { "name": "@ballerine/backoffice-v2", - "version": "0.7.96", + "version": "0.7.97", "description": "Ballerine - Backoffice", "homepage": "https://github.com/ballerine-io/ballerine", "type": "module", @@ -54,8 +54,8 @@ "dependencies": { "@ballerine/blocks": "0.2.34", "@ballerine/common": "0.9.68", - "@ballerine/workflow-browser-sdk": "0.6.88", - "@ballerine/workflow-node-sdk": "0.6.88", + "@ballerine/workflow-browser-sdk": "0.6.89", + "@ballerine/workflow-node-sdk": "0.6.89", "@ballerine/react-pdf-toolkit": "^1.2.67", "@ballerine/ui": "^0.5.67", "@botpress/webchat": "^2.1.10", diff --git a/apps/kyb-app/CHANGELOG.md b/apps/kyb-app/CHANGELOG.md index a70681b319..a63be0c06a 100644 --- a/apps/kyb-app/CHANGELOG.md +++ b/apps/kyb-app/CHANGELOG.md @@ -1,5 +1,11 @@ # kyb-app +## 0.3.116 + +### Patch Changes + +- @ballerine/workflow-browser-sdk@0.6.89 + ## 0.3.115 ### Patch Changes diff --git a/apps/kyb-app/package.json b/apps/kyb-app/package.json index b291cf580e..fafdfd97dd 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.3.115", + "version": "0.3.116", "type": "module", "scripts": { "dev": "vite", @@ -18,7 +18,7 @@ "dependencies": { "@ballerine/blocks": "0.2.34", "@ballerine/common": "^0.9.68", - "@ballerine/workflow-browser-sdk": "0.6.88", + "@ballerine/workflow-browser-sdk": "0.6.89", "@ballerine/ui": "0.5.67", "@lukemorales/query-key-factory": "^1.0.3", "@radix-ui/react-icons": "^1.3.0", diff --git a/examples/headless-example/CHANGELOG.md b/examples/headless-example/CHANGELOG.md index 65660e5906..c143e0b99f 100644 --- a/examples/headless-example/CHANGELOG.md +++ b/examples/headless-example/CHANGELOG.md @@ -1,5 +1,11 @@ # @ballerine/headless-example +## 0.3.88 + +### Patch Changes + +- @ballerine/workflow-browser-sdk@0.6.89 + ## 0.3.87 ### Patch Changes diff --git a/examples/headless-example/package.json b/examples/headless-example/package.json index 32339203b5..5bf20576e4 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.3.87", + "version": "0.3.88", "type": "module", "scripts": { "spellcheck": "cspell \"*\"", @@ -35,7 +35,7 @@ }, "dependencies": { "@ballerine/common": "0.9.68", - "@ballerine/workflow-browser-sdk": "0.6.88", + "@ballerine/workflow-browser-sdk": "0.6.89", "@felte/reporter-svelte": "^1.1.5", "@felte/validator-zod": "^1.0.13", "@fontsource/inter": "^4.5.15", diff --git a/packages/workflow-core/CHANGELOG.md b/packages/workflow-core/CHANGELOG.md index a3c4bf102b..a7c4ca9580 100644 --- a/packages/workflow-core/CHANGELOG.md +++ b/packages/workflow-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @ballerine/workflow-core +## 0.6.89 + +### Patch Changes + +- Updated workflow-core + ## 0.6.88 ### Patch Changes diff --git a/packages/workflow-core/package.json b/packages/workflow-core/package.json index 2217140191..dd90f1c124 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.6.88", + "version": "0.6.89", "description": "workflow-core", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7048be94d8..c0c6288800 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -85,10 +85,10 @@ importers: specifier: ^0.5.67 version: link:../../packages/ui '@ballerine/workflow-browser-sdk': - specifier: 0.6.88 + specifier: 0.6.89 version: link:../../sdks/workflow-browser-sdk '@ballerine/workflow-node-sdk': - specifier: 0.6.88 + specifier: 0.6.89 version: link:../../sdks/workflow-node-sdk '@botpress/webchat': specifier: ^2.1.10 @@ -539,7 +539,7 @@ importers: specifier: 0.5.67 version: link:../../packages/ui '@ballerine/workflow-browser-sdk': - specifier: 0.6.88 + specifier: 0.6.89 version: link:../../sdks/workflow-browser-sdk '@lukemorales/query-key-factory': specifier: ^1.0.3 @@ -1015,7 +1015,7 @@ importers: specifier: 0.9.68 version: link:../../packages/common '@ballerine/workflow-browser-sdk': - specifier: 0.6.88 + specifier: 0.6.89 version: link:../../sdks/workflow-browser-sdk '@felte/reporter-svelte': specifier: ^1.1.5 @@ -2289,7 +2289,7 @@ importers: specifier: 0.9.68 version: link:../../packages/common '@ballerine/workflow-core': - specifier: 0.6.88 + specifier: 0.6.89 version: link:../../packages/workflow-core xstate: specifier: ^4.37.0 @@ -2428,7 +2428,7 @@ importers: sdks/workflow-node-sdk: dependencies: '@ballerine/workflow-core': - specifier: 0.6.88 + specifier: 0.6.89 version: link:../../packages/workflow-core json-logic-js: specifier: ^2.0.2 @@ -2676,10 +2676,10 @@ importers: specifier: 0.9.68 version: link:../../packages/common '@ballerine/workflow-core': - specifier: 0.6.88 + specifier: 0.6.89 version: link:../../packages/workflow-core '@ballerine/workflow-node-sdk': - specifier: 0.6.88 + specifier: 0.6.89 version: link:../../sdks/workflow-node-sdk '@faker-js/faker': specifier: ^7.6.0 @@ -25455,7 +25455,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.54.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.62.0(eslint@8.54.0)(typescript@4.9.3) debug: 3.2.7 eslint: 8.54.0 eslint-import-resolver-node: 0.3.9 @@ -25620,7 +25620,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.54.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.62.0(eslint@8.54.0)(typescript@4.9.3) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 diff --git a/sdks/workflow-browser-sdk/CHANGELOG.md b/sdks/workflow-browser-sdk/CHANGELOG.md index b7533bbf8e..9f33660872 100644 --- a/sdks/workflow-browser-sdk/CHANGELOG.md +++ b/sdks/workflow-browser-sdk/CHANGELOG.md @@ -1,5 +1,12 @@ # @ballerine/workflow-browser-sdk +## 0.6.89 + +### Patch Changes + +- Updated dependencies + - @ballerine/workflow-core@0.6.89 + ## 0.6.88 ### Patch Changes diff --git a/sdks/workflow-browser-sdk/package.json b/sdks/workflow-browser-sdk/package.json index 86c244f53b..d79af8e6cb 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.6.88", + "version": "0.6.89", "description": "workflow-browser-sdk", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", @@ -34,7 +34,7 @@ }, "dependencies": { "@ballerine/common": "0.9.68", - "@ballerine/workflow-core": "0.6.88", + "@ballerine/workflow-core": "0.6.89", "xstate": "^4.37.0" }, "devDependencies": { diff --git a/sdks/workflow-node-sdk/CHANGELOG.md b/sdks/workflow-node-sdk/CHANGELOG.md index b09b2ebad8..a95af003b3 100644 --- a/sdks/workflow-node-sdk/CHANGELOG.md +++ b/sdks/workflow-node-sdk/CHANGELOG.md @@ -1,5 +1,12 @@ # @ballerine/workflow-node-sdk +## 0.6.89 + +### Patch Changes + +- Updated dependencies + - @ballerine/workflow-core@0.6.89 + ## 0.6.88 ### Patch Changes diff --git a/sdks/workflow-node-sdk/package.json b/sdks/workflow-node-sdk/package.json index 8b4f5e9105..8a520d9556 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.6.88", + "version": "0.6.89", "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.6.88", + "@ballerine/workflow-core": "0.6.89", "json-logic-js": "^2.0.2", "xstate": "^4.36.0" }, diff --git a/services/workflows-service/CHANGELOG.md b/services/workflows-service/CHANGELOG.md index 85fa32d77c..41274225b9 100644 --- a/services/workflows-service/CHANGELOG.md +++ b/services/workflows-service/CHANGELOG.md @@ -1,5 +1,13 @@ # @ballerine/workflows-service +## 0.7.93 + +### Patch Changes + +- Updated dependencies + - @ballerine/workflow-core@0.6.89 + - @ballerine/workflow-node-sdk@0.6.89 + ## 0.7.92 ### Patch Changes diff --git a/services/workflows-service/package.json b/services/workflows-service/package.json index 520b2ecb49..e85dfa70df 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.7.92", + "version": "0.7.93", "description": "workflow-service", "scripts": { "spellcheck": "cspell \"*\"", @@ -51,8 +51,8 @@ "@aws-sdk/lib-storage": "3.347.1", "@aws-sdk/s3-request-presigner": "3.347.1", "@ballerine/common": "0.9.68", - "@ballerine/workflow-core": "0.6.88", - "@ballerine/workflow-node-sdk": "0.6.88", + "@ballerine/workflow-core": "0.6.89", + "@ballerine/workflow-node-sdk": "0.6.89", "@faker-js/faker": "^7.6.0", "@nestjs/axios": "^2.0.0", "@nestjs/common": "^9.3.12", diff --git a/services/workflows-service/prisma/data-migrations b/services/workflows-service/prisma/data-migrations index 742886837a..7c056b12ff 160000 --- a/services/workflows-service/prisma/data-migrations +++ b/services/workflows-service/prisma/data-migrations @@ -1 +1 @@ -Subproject commit 742886837ac9d818aa96740afa8cc1732e5c7a95 +Subproject commit 7c056b12ffa30c536a48be04178b437f3735d2db