Skip to content

Commit

Permalink
Bal 3521 (#3015)
Browse files Browse the repository at this point in the history
* feat: implemented common useHttp hook

* feat: initial entity field group

* feat: implemented interaction logic with entity & tests

* feat: implemented EntityFieldGroup (WIP)

* feat: added entity type parameter to ui definition & updated stories

* Bal 3486 (#3034)

* feat: added entity creation & deletion endpoints

* fix: fixed entity deletion

* feat: finalized creation & deletion logic of entities

* fix: lock fix

* Bal 3487(WIP) (#3036)

* feat(workflows-service): implemented document controller, service, repository, and dto

* fix(workflows-service): added cascase on delete

* refactor(workflows-service): removed uploadFile method from collection flow service

* feat(workflows-service): added the ability to reupload document (#3019)

* feat: implemented documents upload

* feat: reworked creation ui

* feat: implemented document creation & deletion

* feat: finalized entity group field

* fix: fixed tests

* fix: cleanup

* fix: format fix

* fix: fixed build

---------

Co-authored-by: Omri Levy <[email protected]>
Co-authored-by: Omri Levy <[email protected]>
Co-authored-by: Shane <[email protected]>

* fix: crypto mock in tests

* feat: reworked documentfield & integrated entityfield to kyb & updated enpoints (#3040)

* feat: added end user creation skip for created entities in kyb (#3044)

* feat: implemented http document deletion on hide & fixed tests (#3045)

* feat: implemented sync plugin & fixes (#3047)

* fix: format

* fix: refactored test

* fix: refactor

* fix: refactor

* fix: updated logging & removed debugger

* feat: added external schema for cf document && updated endpoint

* fix: replaced id with ballerineEntityId & updated endpoint

* fix: fixed test

---------

Co-authored-by: Omri Levy <[email protected]>
Co-authored-by: Omri Levy <[email protected]>
Co-authored-by: Shane <[email protected]>
  • Loading branch information
4 people authored Feb 12, 2025
1 parent 86a481a commit 01ac5db
Show file tree
Hide file tree
Showing 94 changed files with 3,489 additions and 664 deletions.
3 changes: 3 additions & 0 deletions apps/kyb-app/src/domains/collection-flow/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ export interface UISchema {
};
uiOptions?: UIOptions;
version: number;
metadata: {
businessId: string;
};
}

export * from './ui-schema.types';
Original file line number Diff line number Diff line change
Expand Up @@ -80,23 +80,29 @@ export const CollectionFlowV2 = withSessionProtected(() => {
const [isLogoLoaded, setLogoLoaded] = useState(customer?.logoImageUri ? false : true);

useEffect(() => {
if (!customer?.logoImageUri) return;
if (!customer?.logoImageUri) {
return;
}

// Resseting loaded state in case of logo change
setLogoLoaded(false);
}, [customer?.logoImageUri]);

if (getCollectionFlowState(initialContext)?.status === CollectionFlowStatusesEnum.approved)
if (getCollectionFlowState(initialContext)?.status === CollectionFlowStatusesEnum.approved) {
return <Approved />;
}

if (getCollectionFlowState(initialContext)?.status === CollectionFlowStatusesEnum.rejected)
if (getCollectionFlowState(initialContext)?.status === CollectionFlowStatusesEnum.rejected) {
return <Rejected />;
}

if (getCollectionFlowState(initialContext)?.status === CollectionFlowStatusesEnum.completed)
if (getCollectionFlowState(initialContext)?.status === CollectionFlowStatusesEnum.completed) {
return <CompletedScreen />;
}

if (getCollectionFlowState(initialContext)?.status === CollectionFlowStatusesEnum.failed)
if (getCollectionFlowState(initialContext)?.status === CollectionFlowStatusesEnum.failed) {
return <FailedScreen />;
}

return definition && collectionFlowData ? (
<DynamicUI initialState={initialUIState}>
Expand All @@ -119,11 +125,17 @@ export const CollectionFlowV2 = withSessionProtected(() => {
>
{() => {
// Temp state, has to be resolved to success or failure by plugins
if (state === 'done') return <LoadingScreen />;
if (state === 'done') {
return <LoadingScreen />;
}

if (isCompleted(state)) return <CompletedScreen />;
if (isCompleted(state)) {
return <CompletedScreen />;
}

if (isFailed(state)) return <FailedScreen />;
if (isFailed(state)) {
return <FailedScreen />;
}

return (
<DynamicUI.PageResolver
Expand Down Expand Up @@ -251,6 +263,7 @@ export const CollectionFlowV2 = withSessionProtected(() => {
}
context={payload}
isRevision={isRevision}
metadata={schema?.metadata}
/>
</PluginsRunner>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import './validator';

import { useDynamicUIContext } from '@/components/organisms/DynamicUI/hooks/useDynamicUIContext';
import { useStateManagerContext } from '@/components/organisms/DynamicUI/StateManager/components/StateProvider/hooks/useStateManagerContext';
import { UISchema } from '@/domains/collection-flow';
import { CollectionFlowContext } from '@/domains/collection-flow/types/flow-context.types';
import { DynamicFormV2, IDynamicFormValidationParams, IFormElement, IFormRef } from '@ballerine/ui';
import { FunctionComponent, useCallback, useMemo, useRef } from 'react';
Expand All @@ -18,6 +19,7 @@ interface ICollectionFlowUIProps<TValues = CollectionFlowContext> {
elements: Array<IFormElement<any, any>>;
context: TValues;
isRevision?: boolean;
metadata: UISchema['metadata'];
}

const validationParams: IDynamicFormValidationParams = {
Expand All @@ -32,6 +34,7 @@ export const CollectionFlowUI: FunctionComponent<ICollectionFlowUIProps> = ({
elements,
context,
isRevision,
metadata: _uiSchemaMetadata,
}) => {
const { stateApi } = useStateManagerContext();
const { helpers } = useDynamicUIContext();
Expand Down Expand Up @@ -60,8 +63,9 @@ export const CollectionFlowUI: FunctionComponent<ICollectionFlowUIProps> = ({
_appState: {
isSyncing,
},
..._uiSchemaMetadata,
}),
[appMetadata, pluginStatuses, isSyncing],
[appMetadata, pluginStatuses, isSyncing, _uiSchemaMetadata],
);

const handleChange = useCallback(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { DEFINITION_PLUGIN_NAME, definitionPlugin } from './plugins/definition-plugin';
import { EVENT_PLUGIN_NAME, eventPlugin } from './plugins/event.plugin';
import { OCR_PLUGIN_NAME, ocrPlugin } from './plugins/ocr.plugin';
import { SYNC_PLUGIN_NAME, syncPlugin } from './plugins/sync-plugin';
import { TRANSFORMER_PLUGIN_NAME, transformerPlugin } from './plugins/transformer.plugin';

export const pluginsRepository = {
[EVENT_PLUGIN_NAME]: eventPlugin,
[OCR_PLUGIN_NAME]: ocrPlugin,
[TRANSFORMER_PLUGIN_NAME]: transformerPlugin,
[DEFINITION_PLUGIN_NAME]: definitionPlugin,
[SYNC_PLUGIN_NAME]: syncPlugin,
};

export const getPlugin = (pluginName: string) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { syncContext } from '@/domains/collection-flow';
import { CollectionFlowContext } from '@/domains/collection-flow/types/flow-context.types';
import jsonata from 'jsonata';
import { toast } from 'sonner';
import { TPluginRunner } from '../types';

export const SYNC_PLUGIN_NAME = 'sync';

export interface ISyncPluginParams {
transform?: string;
}

export const syncPlugin: TPluginRunner<ISyncPluginParams> = async (context, _, pluginParams) => {
try {
const syncPayload = pluginParams?.transform
? await jsonata(pluginParams.transform).evaluate(context)
: context;

await syncContext(syncPayload as CollectionFlowContext);
} catch (error) {
toast.error('Failed to sync using plugin.');
console.error(error);
}

return context;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { syncContext } from '@/domains/collection-flow';
import { toast } from 'sonner';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { SYNC_PLUGIN_NAME, syncPlugin } from './sync-plugin';

vi.mock('@/domains/collection-flow', () => ({
syncContext: vi.fn(),
}));

vi.mock('sonner', () => ({
toast: {
error: vi.fn(),
},
}));

describe('syncPlugin', () => {
const mockContext = {
someData: 'test',
};

const mockedSyncContext = vi.mocked(syncContext);
const mockedToastError = vi.mocked(toast.error);

beforeEach(() => {
vi.clearAllMocks();
});

it('should call syncContext with the provided context', async () => {
await syncPlugin(mockContext, {} as any, {});

expect(mockedSyncContext).toHaveBeenCalledTimes(1);
expect(mockedSyncContext).toHaveBeenCalledWith(mockContext);
});

it('should return the original context after successful sync', async () => {
const result = await syncPlugin(mockContext, {} as any, {});

expect(result).toBe(mockContext);
});

it('should handle errors and show toast message', async () => {
const mockError = new Error('Sync failed');
mockedSyncContext.mockRejectedValueOnce(mockError);

const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});

const result = await syncPlugin(mockContext, {} as any, {});

expect(mockedToastError).toHaveBeenCalledTimes(1);
expect(mockedToastError).toHaveBeenCalledWith('Failed to sync using plugin.');
expect(consoleSpy).toHaveBeenCalledWith(mockError);
expect(result).toBe(mockContext);

consoleSpy.mockRestore();
});

it('should have the correct plugin name exported', () => {
expect(SYNC_PLUGIN_NAME).toBe('sync');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export const usePluginsHandler = () => {
const runners = getPluginRunner(eventName, element);
const context = stateApi.getContext();

if (!runners?.length) return;
if (!runners?.length) {
return;
}

console.log(`Found plugins ${JSON.stringify(runners)} for event ${eventName}`);

Expand Down
1 change: 1 addition & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"react-json-view": "^1.21.3",
"react-phone-input-2": "^2.15.1",
"recharts": "^2.7.2",
"sonner": "^1.4.3",
"string-ts": "^1.2.0",
"tailwind-merge": "^1.10.0",
"zod": "^3.23.4"
Expand Down
4 changes: 4 additions & 0 deletions packages/ui/src/common/hooks/useHttp/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './types';
export * from './useHttp';
export * from './utils/format-headers';
export * from './utils/request';
7 changes: 7 additions & 0 deletions packages/ui/src/common/hooks/useHttp/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface IHttpParams {
url: string;
resultPath: string;
headers?: Record<string, string>;
method?: 'POST' | 'PUT' | 'GET' | 'DELETE';
timeout?: number;
}
50 changes: 50 additions & 0 deletions packages/ui/src/common/hooks/useHttp/useHttp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { AnyObject } from '@/common/types';
import get from 'lodash/get';
import { useCallback, useState } from 'react';
import { IHttpParams } from './types';
import { request } from './utils/request';

export const useHttp = (params: IHttpParams, metadata: AnyObject) => {
const [responseError, setResponseError] = useState<Error | null>(null);
const [isLoading, setIsLoading] = useState(false);

const runRequest = useCallback(
async (
requestPayload?: any,
other?: {
params?: AnyObject;
},
) => {
setIsLoading(true);
setResponseError(null);

try {
const response = await request(
{
...params,
url: params.url,
},
metadata,
requestPayload,
other?.params,
);

return params.resultPath ? get(response, params.resultPath) : response;
} catch (error) {
console.error(error);
setResponseError(error as Error);

throw error;
} finally {
setIsLoading(false);
}
},
[params, metadata],
);

return {
isLoading,
error: responseError,
run: runRequest,
};
};
Loading

0 comments on commit 01ac5db

Please sign in to comment.