Skip to content

Commit

Permalink
feat: reworked data insertion on JSONform of type array & ui bump (#1531
Browse files Browse the repository at this point in the history
)

* feat: reworked data insertion on JSONform of type array & ui bump

* fix: lock fix

* fix: fixed removal loop bug

* fix: lock fix

---------

Co-authored-by: Alon Peretz <[email protected]>
  • Loading branch information
chesterkmr and alonp99 authored Nov 5, 2023
1 parent 352b205 commit b7b1aa9
Show file tree
Hide file tree
Showing 39 changed files with 839 additions and 268 deletions.
1 change: 1 addition & 0 deletions apps/backoffice-v2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Patch Changes

- Updated dependencies
- @ballerine/ui@0.3.8
- @ballerine/common@0.7.19
- @ballerine/workflow-browser-sdk@0.5.17
- @ballerine/workflow-node-sdk@0.5.17
Expand Down
2 changes: 2 additions & 0 deletions apps/kyb-app/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

### Patch Changes

- Updated dependencies
- @ballerine/ui@0.3.8
- AsiaVerify integrations
- @ballerine/blocks@0.1.15
- @ballerine/workflow-browser-sdk@0.5.15
Expand Down
9 changes: 0 additions & 9 deletions apps/kyb-app/DynamicElements/helpers/engine-manager.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { useMemo } from 'react';
import { Document, UIElement, UIPage } from '@app/domains/collection-flow';
import { ErrorField } from '@app/components/organisms/DynamicUI/rule-engines';
import { findDefinitionByDestinationPath } from '@app/components/organisms/UIRenderer/elements/JSONForm/helpers/findDefinitionByName';
import { Document, UIElement, UIPage } from '@app/domains/collection-flow';
import { AnyObject } from '@ballerine/ui';
import {
findDefinitionByDestinationPath,
findDefinitionByName,
} from '@app/components/organisms/UIRenderer/elements/JSONForm/helpers/findDefinitionByName';
import { useMemo } from 'react';

export interface PageError {
page: number;
Expand All @@ -30,7 +27,7 @@ export const usePageErrors = (context: AnyObject, pages: UIPage[]): PageError[]
});

pagesWithErrors.forEach(pageError => {
pageError.errors = (context.documents as Document[])
pageError.errors = ((context.documents as Document[]) || [])
.filter((document, index) => {
if (
!(document?.decision?.status == 'revision' || document?.decision?.status == 'rejected')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ActionHandler } from '@app/components/organisms/DynamicUI/StateManager/components/ActionsHandler/action-handlers/action-handler.abstract';
import { AnyObject } from '@ballerine/ui';
import ky from 'ky';
import jmespath from 'jmespath';
import set from 'lodash/set';
import { Action, IRule } from '@app/domains/collection-flow';
import { EngineManager } from '@app/components/organisms/DynamicUI/StateManager/components/ActionsHandler/helpers/engine-manager';
import { EventEngine } from '@app/components/organisms/DynamicUI/rule-engines/event.engine';
import { JsonLogicRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/json-logic.rule-engine';
import { JsonSchemaRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/json-schema.rule-engine';
import { EngineManager } from '@app/components/organisms/DynamicUI/StateManager/components/ActionsHandler/helpers/engine-manager';
import { Action, IRule } from '@app/domains/collection-flow';
import { AnyObject } from '@ballerine/ui';
import jmespath from 'jmespath';
import ky from 'ky';
import set from 'lodash/set';

export interface ApiActionParams {
url: string;
Expand Down Expand Up @@ -89,7 +89,7 @@ export class ApiActionHandler implements ActionHandler {
if (!engine) throw new Error(`Provided rule with engine ${rule.type} not supported`);

//@ts-ignore
return engine.test(context, rule as IRule);
return engine.validate(context, rule as IRule);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { RuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/rule-engine.abstract';

export class EngineManager {
constructor(private readonly engines: RuleEngine[]) {}
private enginesMap = new Map<string, RuleEngine>();

constructor(private readonly engines: RuleEngine[]) {
engines.forEach(engine => {
this.enginesMap.set(engine.ENGINE_NAME, engine);
});
}

getEngine(engineName: string): RuleEngine | null {
return this.engines.find(engine => engine.ENGINE_NAME === engineName) || null;
return this.enginesMap.get(engineName) || null;
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { EngineManager } from '@app/components/organisms/DynamicUI/StateManager/components/ActionsHandler/helpers/engine-manager';
import { JsonLogicRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/json-logic.rule-engine';
import { Action, UIElement } from '@app/domains/collection-flow';
import { AnyObject } from '@ballerine/ui';
import { JsonSchemaRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/json-schema.rule-engine';
import { UIState } from '@app/components/organisms/DynamicUI/hooks/useUIStateLogic/types';
import { DocumentsRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/documents.rule-engine';
import { JmespathRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/jmespath.rule-engine';
import { JsonLogicRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/json-logic.rule-engine';
import { JsonSchemaRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/json-schema.rule-engine';
import { Action, UIElement } from '@app/domains/collection-flow';
import { AnyObject } from '@ballerine/ui';

export const getDispatchableActions = (
context: AnyObject,
Expand All @@ -26,7 +26,8 @@ export const getDispatchableActions = (
return (
action.dispatchOn?.rules?.length &&
action.dispatchOn?.rules?.every(
rule => engineManager.getEngine(rule.type).test(context, rule, definition, state).isValid,
rule =>
engineManager.getEngine(rule.type).validate(context, rule, definition, state).isValid,
)
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import {
JsonLogicRuleEngine,
RuleTestResult,
} from '@app/components/organisms/DynamicUI/rule-engines';
import { DocumentsRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/documents.rule-engine';
import { JmespathRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/jmespath.rule-engine';
import { JsonSchemaRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/json-schema.rule-engine';
import { Rule, UIElement } from '@app/domains/collection-flow';
import { AnyObject } from '@ballerine/ui';
import { useEffect, useMemo, useRef, useState } from 'react';
import { JmespathRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/jmespath.rule-engine';
import { DocumentsRuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/documents.rule-engine';

export const useRuleExecutor = (
context: AnyObject,
Expand Down Expand Up @@ -49,7 +49,7 @@ export const useRuleExecutor = (
//TO DO: Find solution on how to define array items in schemas
// ctx.documents = ctx?.documents.filter(Boolean);

return engine.test(ctx, rule, definition, uiState);
return engine.validate(ctx, rule, definition, uiState);
}) || [];

setExecutionResult(executionResult);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ export class DocumentsRuleEngine implements RuleEngine {
public readonly ENGINE_NAME = 'destination-engine';
private ruleManager = new EngineManager([new JmespathRuleEngine(), new JsonLogicRuleEngine()]);

test(context: AnyObject, rule: unknown, definition: UIElement<AnyObject>, state: UIState) {
validate(context: AnyObject, rule: unknown, definition: UIElement<AnyObject>, state: UIState) {
if (this.isDestinationValidatorRule(rule)) {
const errors: ErrorField[] = [];

rule.value.forEach(params => {
const isRequired = this.isRule(params.required)
? this.ruleManager
.getEngine(params.required.type)
.test(context, params.required, definition, state).isValid
.validate(context, params.required, definition, state).isValid
: params.required;

const document = ((context.documents || []) as Document[]).find(
Expand Down Expand Up @@ -50,6 +50,10 @@ export class DocumentsRuleEngine implements RuleEngine {
throw new Error(`Invalid rule provided to ${this.ENGINE_NAME}`);
}

test() {
return true;
}

private isRule(rule: unknown): rule is Rule {
return typeof rule === 'object' && 'type' in rule;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { RuleEngine } from '@app/components/organisms/DynamicUI/rule-engines/rul
export class EventEngine implements RuleEngine {
public ENGINE_NAME = 'event';

test() {
validate() {
return { isValid: true, errors: [] };
}

test() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@ import jmespath from 'jmespath';
export class JmespathRuleEngine implements RuleEngine {
public readonly ENGINE_NAME = 'jmespath';

test(
validate(
context: unknown,
rule: Rule,
definition: UIElement<AnyObject>,
_: UIElement<AnyObject>,
uiState: UIState,
): RuleTestResult {
const result = this.test({ ...(context as AnyObject), uiState }, rule);

return {
isValid: Boolean(result),
errors: [],
};
}

test(context: unknown, rule: Rule) {
if (this.isJmesPath(rule)) {
const result = jmespath.search({ ...(context as AnyObject), uiState }, rule.value) as boolean;
return {
isValid: Boolean(result),
errors: [],
};
return jmespath.search(context, rule.value) as boolean;
}

throw new Error(`Invalid rule provided to ${this.ENGINE_NAME}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,26 @@ import jsonLogic from 'json-logic-js';
export class JsonLogicRuleEngine implements RuleEngine {
public readonly ENGINE_NAME = 'json-logic';

test(context: unknown, rule: unknown, _: UIElement<AnyObject>, uiState: UIState): RuleTestResult {
if (this.isJsonRule(rule)) {
const result = jsonLogic.apply(rule.value, {
validate(
context: unknown,
rule: unknown,
_: UIElement<AnyObject>,
uiState: UIState,
): RuleTestResult {
const result = this.test(
{
...(context as object),
uiState,
} as AnyObject) as boolean;
} as AnyObject,
rule,
);

return { isValid: result, errors: [] };
}

return { isValid: result, errors: [] };
test(context: unknown, rule: unknown) {
if (this.isJsonRule(rule)) {
return jsonLogic.apply(rule.value, context as AnyObject) as boolean;
}

throw new Error(`Invalid rule provided to ${this.ENGINE_NAME}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import {
} from '@app/components/organisms/DynamicUI/rule-engines/rule-engine.abstract';
import { Rule, UIElement } from '@app/domains/collection-flow';
import { AnyObject } from '@ballerine/ui';
import Ajv from 'ajv/dist/2019';
import ajvErrors from 'ajv-errors';
import addFormats from 'ajv-formats';
import Ajv from 'ajv/dist/2019';

export class JsonSchemaRuleEngine implements RuleEngine {
public readonly ENGINE_NAME = 'json-schema';

test(context: unknown, rule: Rule, definition: UIElement<AnyObject>) {
validate(context: unknown, rule: Rule, definition: UIElement<AnyObject>) {
const validator = new Ajv({ allErrors: true, useDefaults: true });
addFormats(validator, {
formats: ['email', 'uri', 'date', 'date-time'],
Expand All @@ -29,6 +29,19 @@ export class JsonSchemaRuleEngine implements RuleEngine {
return { isValid: true, errors: [] };
}

test(context: unknown, rule: Rule) {
const validator = new Ajv({ allErrors: true, useDefaults: true });
addFormats(validator, {
formats: ['email', 'uri', 'date', 'date-time'],
keywords: true,
});
ajvErrors(validator, { singleError: true });

const validationResult = validator.validate(rule.value, context);

return validationResult;
}

private __extractErrorsWithFields(validator: Ajv, definition: UIElement<AnyObject>) {
const result = validator.errors?.map(error => {
const erroredParams = Object.values(error.params) as string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ export interface RuleTestResult {
export abstract class RuleEngine {
public readonly ENGINE_NAME: string;

abstract test(
abstract validate(
context: unknown,
rule: Rule,
definition: UIElement<AnyObject>,
uiState: UIState,
): RuleTestResult;

abstract test(context: unknown, rule: Rule): boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { createFormSchemaFromUIElements } from '@app/components/organisms/UIRend
import { createInitialFormData } from '@app/components/organisms/UIRenderer/elements/JSONForm/helpers/createInitialFormData';
import { UIElementComponent } from '@app/components/organisms/UIRenderer/types';

import { DataCreationParams } from '@app/components/organisms/UIRenderer/elements/JSONForm/hocs/withInitialDataCreation';
import {
jsonFormFields,
jsonFormLayouts,
} from '@app/components/organisms/UIRenderer/elements/JSONForm/json-form.fields';
import { useDataInsertionLogic } from '@app/components/organisms/UIRenderer/hooks/useDataInsertionLogic';
import { DefinitionInsertionParams } from '@app/components/organisms/UIRenderer/hooks/useDataInsertionLogic/types';
import { useUIElementErrors } from '@app/components/organisms/UIRenderer/hooks/useUIElementErrors/useUIElementErrors';
import { useUIElementProps } from '@app/components/organisms/UIRenderer/hooks/useUIElementProps';
import { useUIElementState } from '@app/components/organisms/UIRenderer/hooks/useUIElementState';
Expand All @@ -19,7 +20,7 @@ import get from 'lodash/get';
import set from 'lodash/set';
import { useCallback, useEffect, useMemo, useRef } from 'react';

export interface JSONFormElementBaseParams extends DataCreationParams {
export interface JSONFormElementBaseParams {
jsonFormDefinition: RJSFSchema;
uiSchema?: UiSchema;
label?: string;
Expand All @@ -28,11 +29,13 @@ export interface JSONFormElementBaseParams extends DataCreationParams {
documentData?: AnyObject;
}

export interface JSONFormElementParams {
export interface JSONFormElementParams extends DefinitionInsertionParams {
jsonFormDefinition?: { type?: string; required?: string[] };
}

export const JSONForm: UIElementComponent<JSONFormElementParams> = ({ definition, actions }) => {
export const JSONForm: UIElementComponent<JSONFormElementParams> = ({ definition }) => {
useDataInsertionLogic(definition, definition?.options?.jsonFormDefinition?.type !== 'array');

const { state: elementState } = useUIElementState(definition);
const { hidden } = useUIElementProps(definition);
const { formSchema, uiSchema } = useMemo(
Expand All @@ -47,7 +50,7 @@ export const JSONForm: UIElementComponent<JSONFormElementParams> = ({ definition
const formRef = useRef<any>(null);

useEffect(() => {
const elementValue = get(payload, definition.valueDestination);
const elementValue = get(payload, definition.valueDestination) as unknown;

// TO DO: ADD this logic to jmespath @blokh
if (definition.options?.jsonFormDefinition?.type === 'array' && Array.isArray(elementValue)) {
Expand All @@ -56,7 +59,7 @@ export const JSONForm: UIElementComponent<JSONFormElementParams> = ({ definition
set(
payload,
definition.valueDestination,
elementValue.map(obj => ({
elementValue.map((obj: AnyObject) => ({
...obj,
additionalInfo: {
...obj.additionalInfo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { createFormSchemaFromUIElements } from '@app/components/organisms/UIRend
import { createInitialFormData } from '@app/components/organisms/UIRenderer/elements/JSONForm/helpers/createInitialFormData';
import { UIElementComponent } from '@app/components/organisms/UIRenderer/types';

import { JSONFormElementParams } from '@app/components/organisms/UIRenderer/elements/JSONForm/JSONForm';
import { jsonFormFields } from '@app/components/organisms/UIRenderer/elements/JSONForm/json-form.fields';
import { AnyObject, DynamicForm } from '@ballerine/ui';
import { RJSFSchema, UiSchema } from '@rjsf/utils';
import { useCallback, useMemo, useState } from 'react';
import set from 'lodash/set';
import { jsonFormFields } from '@app/components/organisms/UIRenderer/elements/JSONForm/json-form.fields';
import { useCallback, useMemo } from 'react';

export interface JSONFormElementBaseParams {
jsonFormDefinition: RJSFSchema;
Expand All @@ -18,16 +19,11 @@ export interface JSONFormElementBaseParams {
documentData?: AnyObject;
}

export interface JSONFormElementParams {
jsonFormDefinition?: { type?: string; required?: string[] };
}

export const Documents: UIElementComponent<JSONFormElementParams> = ({ definition, actions }) => {
const { formSchema, uiSchema } = useMemo(
() => createFormSchemaFromUIElements(definition),
[definition],
);
const [isUploading, setUploading] = useState(false);
const { stateApi } = useStateManagerContext();

const { payload } = useStateManagerContext();
Expand Down
Loading

0 comments on commit b7b1aa9

Please sign in to comment.