Skip to content

Commit

Permalink
Merge pull request #32 from formio/processors-with-appserver-2
Browse files Browse the repository at this point in the history
Processor changes to work with other forms and validation processes.
  • Loading branch information
travist authored Feb 7, 2024
2 parents d1d59fe + 0974c1a commit 6412298
Show file tree
Hide file tree
Showing 16 changed files with 133 additions and 63 deletions.
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@formio/core",
"version": "2.0.0-rc.9",
"version": "2.0.0-dev.3",
"description": "The core Form.io renderering framework.",
"main": "lib/index.js",
"exports": {
Expand All @@ -9,7 +9,8 @@
"./sdk": "./lib/sdk/index.js",
"./model": "./lib/model/index.js",
"./process": "./lib/process/index.js",
"./template": "./lib/template/index.js"
"./template": "./lib/template/index.js",
"./types": "./lib/types/index.js"
},
"scripts": {
"test": "TEST=1 mocha -r ts-node/register -r tsconfig-paths/register -r mock-local-storage -r jsdom-global/register -b -t 0 'src/**/__tests__/*.test.ts'",
Expand Down Expand Up @@ -41,7 +42,9 @@
},
"files": [
"dist",
"lib"
"lib",
"types.js",
"types.d.ts"
],
"homepage": "https://github.com/formio/core#readme",
"devDependencies": {
Expand All @@ -50,6 +53,7 @@
"@types/dompurify": "^3.0.5",
"@types/fetch-mock": "^7.3.8",
"@types/flatpickr": "^3.1.2",
"@types/inputmask": "^5.0.7",
"@types/lodash": "^4.14.201",
"@types/lodash.template": "^4.5.3",
"@types/mocha": "^10.0.4",
Expand Down Expand Up @@ -88,6 +92,7 @@
"dompurify": "^3.0.6",
"eventemitter3": "^5.0.0",
"fetch-ponyfill": "^7.1.0",
"inputmask": "^5.0.9-beta.45",
"json-logic-js": "^2.0.2",
"lodash": "^4.17.21",
"moment": "^2.29.4"
Expand Down
29 changes: 19 additions & 10 deletions src/process/calculation/index.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
import JSONLogic from 'modules/jsonlogic';
import { ProcessorFn, ProcessorFnSync, CalculationScope, CalculationContext, ProcessorInfo } from 'types';
import { ProcessorFn, ProcessorFnSync, CalculationScope, CalculationContext, ProcessorInfo, FilterScope } from 'types';
import _set from 'lodash/set';
import { getComponentKey } from 'utils/formUtil';
const Evaluator = JSONLogic.evaluator;

export const shouldCalculate = (context: CalculationContext): boolean => {
const { component } = context;
if (!component.calculateValue || (component.hasOwnProperty('calculateServer') && !component.calculateServer)) {
const { component, config } = context;
if (
!component.calculateValue ||
(config?.server && !component.calculateServer)
) {
return false;
}
return true;
};

export const calculateProcessSync: ProcessorFnSync<CalculationScope> = (context: CalculationContext) => {
const { component, row, evalContext, scope, path } = context;
const { component, row, evalContext, scope, path, value } = context;
if (!shouldCalculate(context)) {
return;
}
const evalContextValue = evalContext ? evalContext(context) : context;
evalContextValue.value = null;
if (!scope.calculated) scope.calculated = [];
const newValue = Evaluator.evaluate(component.calculateValue, evalContextValue, 'value');
scope.calculated.push({
path,
value: newValue
});
_set(row, component.key, newValue);
let newValue = Evaluator.evaluate(component.calculateValue, evalContextValue, 'value');

// Only set a new value if it is not "null" which would be the case if no calculation occurred.
if (newValue !== null) {
scope.calculated.push({
path,
value: newValue
});
_set(row, getComponentKey(component), newValue);
context.value = newValue;
}
return;
};

Expand Down
7 changes: 4 additions & 3 deletions src/process/conditions/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ProcessorFn, ProcessorFnSync, ConditionsScope, ProcessorInfo, ConditionsContext, SimpleConditional, JSONConditional, LegacyConditional, SimpleConditionalConditions, Component, NestedComponent, FilterScope } from 'types';
import { Utils } from 'utils';
import unset from 'lodash/unset';
import { componentInfo, getComponentPath } from 'utils/formUtil';
import { componentInfo, getComponentKey, getComponentPath } from 'utils/formUtil';
import {
checkCustomConditional,
checkJsonConditional,
Expand Down Expand Up @@ -108,14 +108,15 @@ export const conditionalProcess = (context: ConditionsContext, isHidden: Conditi
Utils.eachComponentData([component], row, (comp: Component, data: any, compRow: any, compPath: string) => {
scope.conditionals?.push({ path: getComponentPath(comp, compPath), conditionallyHidden: true });
if (!comp.hasOwnProperty('clearOnHide') || comp.clearOnHide) {
unset(compRow, comp.key);
unset(compRow, getComponentKey(comp));
}
});
}
else {
scope.conditionals.push({ path, conditionallyHidden: true });
if (!component.hasOwnProperty('clearOnHide') || component.clearOnHide) {
unset(row, component.key);
unset(row, getComponentKey(component));
context.value = undefined;
}
}
}
Expand Down
12 changes: 7 additions & 5 deletions src/process/defaultValue/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import JSONLogic from 'modules/jsonlogic';
import { ProcessorFn, ProcessorFnSync, ConditionsScope, ProcessorInfo, DefaultValueContext } from 'types';
import { ProcessorFn, ProcessorFnSync, ConditionsScope, ProcessorInfo, DefaultValueContext, FilterScope } from 'types';
import has from 'lodash/has';
import set from 'lodash/set';
import { getComponentKey } from 'utils/formUtil';
const Evaluator = JSONLogic.evaluator;

export const hasCustomDefaultValue = (context: DefaultValueContext): boolean => {
Expand Down Expand Up @@ -34,7 +35,7 @@ export const customDefaultValueProcessSync: ProcessorFnSync<ConditionsScope> = (
return;
}
if (!scope.defaultValues) scope.defaultValues = [];
if (has(row, component.key)) {
if (has(row, getComponentKey(component))) {
return;
}
let defaultValue = null;
Expand All @@ -51,7 +52,7 @@ export const customDefaultValueProcessSync: ProcessorFnSync<ConditionsScope> = (
});
}
if (defaultValue !== null && defaultValue !== undefined) {
set(row, component.key, defaultValue);
set(row, getComponentKey(component), defaultValue);
}
};

Expand All @@ -65,7 +66,7 @@ export const serverDefaultValueProcessSync: ProcessorFnSync<ConditionsScope> = (
return;
}
if (!scope.defaultValues) scope.defaultValues = [];
if (has(row, component.key)) {
if (has(row, getComponentKey(component))) {
return;
}
let defaultValue = null;
Expand All @@ -83,7 +84,8 @@ export const serverDefaultValueProcessSync: ProcessorFnSync<ConditionsScope> = (
});
}
if (defaultValue !== null && defaultValue !== undefined) {
set(row, component.key, defaultValue);
set(row, getComponentKey(component), defaultValue);
context.value = defaultValue;
}
};

Expand Down
6 changes: 4 additions & 2 deletions src/process/fetch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ProcessorFn, ProcessorInfo, FetchContext, FetchScope, FetchFn } from 't
import get from 'lodash/get';
import set from 'lodash/set';
import { Evaluator } from 'utils';
import { getComponentKey } from 'utils/formUtil';

export const shouldFetch = (context: FetchContext): boolean => {
const { component } = context;
Expand Down Expand Up @@ -64,13 +65,14 @@ export const fetchProcess: ProcessorFn<FetchScope> = async (context: FetchContex
const mapFunction = get(component, 'fetch.mapFunction');

// Set the row data of the fetched value.
set(row, component.key, mapFunction ? Evaluator.evaluate(mapFunction, {
const key = getComponentKey(component);
set(row, key, mapFunction ? Evaluator.evaluate(mapFunction, {
...evalContextValue,
...{responseData: result}
}, 'value') : result);
scope.fetched.push({
path,
value: get(row, component.key)
value: get(row, key)
});
}
catch (err: any) {
Expand Down
2 changes: 1 addition & 1 deletion src/process/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const ProcessorMap: Record<string, ProcessorInfo<any, any>> = {
};

export const ProcessTargets: ProcessTarget = {
server: [
submission: [
filterProcessInfo,
serverDefaultValueProcessInfo,
fetchProcessInfo,
Expand Down
12 changes: 3 additions & 9 deletions src/process/processOne.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import { get } from "lodash";
import { CheckboxComponent, Component, ProcessorsContext, ProcessorType } from "types";
import { Component, ProcessorsContext, ProcessorType } from "types";
import { getComponentKey } from "utils/formUtil";

export function dataValue(component: Component, row: any) {
let key = component.key;
if (
component.type === 'checkbox' &&
component.inputType === 'radio' &&
(component as CheckboxComponent).name
) {
key = (component as CheckboxComponent).name;
}
const key = getComponentKey(component);
return key ? get(row, key) : undefined;
}

Expand Down
18 changes: 13 additions & 5 deletions src/process/validation/rules/validateMask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import _, { isEmpty } from 'lodash';
import { FieldError } from 'error';
import { TextFieldComponent, DataObject, RuleFn, RuleFnSync, ValidationContext } from 'types';
import { ProcessorInfo } from 'types/process/ProcessorInfo';
import Inputmask from 'inputmask';

const isMaskType = (obj: any): obj is DataObject & { maskName: string; value: string } => {
return (
Expand Down Expand Up @@ -119,21 +120,28 @@ export const validateMaskSync: RuleFnSync = (context: ValidationContext) => {
if (!shouldValidate(context)) {
return null;
}
let inputMask: (string | RegExp)[] | undefined;
let inputMask: string | string[] | undefined;
let maskValue: string | undefined;
if (component.allowMultipleMasks && (component as TextFieldComponent).inputMasks?.length) {
const mask = value && isMaskType(value) ? value : undefined;
const formioInputMask = getMaskByLabel(component as TextFieldComponent, mask?.maskName);
if (formioInputMask) {
inputMask = getInputMask(formioInputMask);
inputMask = formioInputMask;
}
maskValue = mask?.value;
} else {
inputMask = getInputMask((component as TextFieldComponent).inputMask || '');
inputMask = (component as TextFieldComponent).inputMask || '';
}
if (value != null && inputMask) {
if (!inputMask) {
return null;
}
if (value && inputMask && typeof value === 'string' && component.type === 'textfield' ) {
return Inputmask.isValid(value, {mask: inputMask.toString()}) ? null : new FieldError('mask', context);
}
let inputMaskArr = getInputMask(inputMask);
if (value != null && inputMaskArr) {
const error = new FieldError('mask', context);
return matchInputMask(maskValue || value, inputMask) ? null : error;
return matchInputMask(maskValue || value, inputMaskArr) ? null : error;
}
return null;
};
Expand Down
19 changes: 14 additions & 5 deletions src/process/validation/rules/validateUnique.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,27 @@ export const shouldValidate = (context: ValidationContext) => {
};

export const validateUnique: RuleFn = async (context: ValidationContext) => {
const { value, config } = context;
const { value, config, component } = context;
if (!shouldValidate(context)) {
return null;
}

if (!config || !config.database) {
throw new ValidatorError("Can't test for unique value without a database config object");
}
const isUnique = await config.database?.isUnique(context, value);
return isUnique
? null
: new FieldError('unique', context);
try {
const isUnique = await config.database?.isUnique(context, value);
if (typeof isUnique === 'string') {
return new FieldError('unique', {
...context,
component: {...component, conflictId: isUnique},
});
}
return (isUnique === true) ? null : new FieldError('unique', context);
}
catch (err: any) {
throw new ValidatorError(err.message || err);
}
};

export const validateUniqueInfo: ProcessorInfo<ValidationContext, FieldError | null> = {
Expand Down
1 change: 1 addition & 0 deletions src/types/BaseComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export type BaseComponent = {
allowMultipleMasks?: boolean;
addons?: any[]; // TODO: this should go away
inputType?: string;
conflictId?: string;
errors?: Record<string, string>;
truncateMultipleSpaces?: boolean;
};
Loading

0 comments on commit 6412298

Please sign in to comment.