diff --git a/packages/angular-material/src/other/label.renderer.ts b/packages/angular-material/src/other/label.renderer.ts index 9bec468555..a0bb076e72 100644 --- a/packages/angular-material/src/other/label.renderer.ts +++ b/packages/angular-material/src/other/label.renderer.ts @@ -35,7 +35,8 @@ import { OwnPropsOfRenderer, RankedTester, rankWith, - uiTypeIs + uiTypeIs, + getOrCreateAjv } from '@jsonforms/core'; import { Subscription } from 'rxjs'; @@ -46,7 +47,7 @@ const mapStateToProps = ( const visible = ownProps.visible !== undefined ? ownProps.visible - : isVisible(ownProps.uischema, getData(state)); + : isVisible(ownProps.uischema, getData(state), undefined, getOrCreateAjv(state)); return { visible diff --git a/packages/core/src/reducers/core.ts b/packages/core/src/reducers/core.ts index a9234c379a..03093ec745 100644 --- a/packages/core/src/reducers/core.ts +++ b/packages/core/src/reducers/core.ts @@ -283,6 +283,7 @@ export const coreReducer = ( export const extractData = (state: JsonFormsCore) => get(state, 'data'); export const extractSchema = (state: JsonFormsCore) => get(state, 'schema'); export const extractUiSchema = (state: JsonFormsCore) => get(state, 'uischema'); +export const extractOrCreateAjv = (state: JsonFormsCore) => getOrCreateAjv(state); export const errorsAt = ( instancePath: string, diff --git a/packages/core/src/reducers/index.ts b/packages/core/src/reducers/index.ts index d2053df64c..44fb42ded1 100644 --- a/packages/core/src/reducers/index.ts +++ b/packages/core/src/reducers/index.ts @@ -32,6 +32,7 @@ import { extractRefParserOptions, extractSchema, extractUiSchema, + extractOrCreateAjv, subErrorsAt, ValidationMode } from './core'; @@ -63,6 +64,7 @@ import RefParser from 'json-schema-ref-parser'; import { cellReducer } from './cells'; import { configReducer } from './config'; import get from 'lodash/get'; +import { Ajv } from 'ajv'; export { rendererReducer, @@ -98,6 +100,9 @@ export const getUiSchema = (state: JsonFormsState): UISchemaElement => extractUiSchema(get(state, 'jsonforms.core')); export const getRefParserOptions = (state: JsonFormsState): RefParser.Options => extractRefParserOptions(get(state, 'jsonforms.core')); +export const getOrCreateAjv = ( + state: JsonFormsState +): Ajv => extractOrCreateAjv(get(state, 'jsonforms.core')); export const getDefaultData = ( state: JsonFormsState ): JsonFormsDefaultDataRegistryEntry[] => @@ -108,7 +113,6 @@ export const getRenderers = ( export const getCells = ( state: JsonFormsState ): JsonFormsCellRendererRegistryEntry[] => get(state, 'jsonforms.cells'); - /** * Finds a registered UI schema to use, if any. * @param schema the JSON schema describing the data to be rendered diff --git a/packages/core/src/util/cell.ts b/packages/core/src/util/cell.ts index 89e37e0cda..ba8fe6661c 100644 --- a/packages/core/src/util/cell.ts +++ b/packages/core/src/util/cell.ts @@ -24,7 +24,7 @@ */ import isEmpty from 'lodash/isEmpty'; import union from 'lodash/union'; -import { getConfig, getData, getErrorAt, getSchema } from '../reducers'; +import { getConfig, getData, getErrorAt, getSchema, getOrCreateAjv } from '../reducers'; import { formatErrorMessage, isEnabled, @@ -107,12 +107,12 @@ export const mapStateToCellProps = ( const visible = ownProps.visible !== undefined ? ownProps.visible - : isVisible(uischema, rootData); + : isVisible(uischema, rootData, undefined, getOrCreateAjv(state)); const readOnly = state.jsonforms.readOnly; const enabled = !readOnly && (ownProps.enabled !== undefined ? ownProps.enabled - : isEnabled(uischema, rootData)); + : isEnabled(uischema, rootData, undefined, getOrCreateAjv(state))); const errors = formatErrorMessage( union(getErrorAt(path, schema)(state).map(error => error.message)) ); diff --git a/packages/core/src/util/index.ts b/packages/core/src/util/index.ts index 33c98ee30c..e106bf4e8f 100644 --- a/packages/core/src/util/index.ts +++ b/packages/core/src/util/index.ts @@ -35,6 +35,8 @@ import { toDataPathSegments } from './path'; import { isEnabled, isVisible } from './runtime'; +import { Ajv } from 'ajv'; +import { createAjv } from './validator'; export { createCleanLabel, createLabelDescriptionFrom } from './label'; @@ -132,11 +134,11 @@ export { composePaths, composeWithUi, Paths, toDataPath }; // Runtime -- const Runtime = { - isEnabled(uischema: UISchemaElement, data: any): boolean { - return isEnabled(uischema, data); + isEnabled(uischema: UISchemaElement, data: any, ajv: Ajv = createAjv()): boolean { + return isEnabled(uischema, data,undefined, ajv); }, - isVisible(uischema: UISchemaElement, data: any): boolean { - return isVisible(uischema, data); + isVisible(uischema: UISchemaElement, data: any, ajv: Ajv = createAjv()): boolean { + return isVisible(uischema, data, undefined, ajv); } }; export { isEnabled, isVisible, Runtime, deriveTypes, hasType }; diff --git a/packages/core/src/util/renderer.ts b/packages/core/src/util/renderer.ts index b58db691ee..beba4b7cbf 100644 --- a/packages/core/src/util/renderer.ts +++ b/packages/core/src/util/renderer.ts @@ -38,7 +38,8 @@ import { getSchema, getSubErrorsAt, getUiSchema, - UISchemaTester + UISchemaTester, + getOrCreateAjv } from '../reducers'; import { RankedTester } from '../testers'; import { JsonSchema } from '../models/jsonSchema'; @@ -58,7 +59,7 @@ import { resolveSubSchemas } from '../util'; import { update, CoreActions } from '../actions'; -import { ErrorObject } from 'ajv'; +import { ErrorObject, Ajv } from 'ajv'; import { JsonFormsState } from '../store'; import { AnyAction, Dispatch } from 'redux'; import { JsonFormsRendererRegistryEntry } from '../reducers/renderers'; @@ -274,6 +275,12 @@ export interface StatePropsOfRenderer { */ cells?: JsonFormsCellRendererRegistryEntry[]; + + /** + * AJV instance from core state. + */ + + ajv?: Ajv; } /** @@ -395,12 +402,12 @@ export const mapStateToControlProps = ( const path = composeWithUi(uischema, ownProps.path); const visible: boolean = ownProps.visible === undefined || hasShowRule(uischema) - ? isVisible(uischema, rootData, ownProps.path) + ? isVisible(uischema, rootData, ownProps.path, getOrCreateAjv(state)) : ownProps.visible; const readOnly = state.jsonforms.readOnly; const enabled: boolean = !readOnly && (ownProps.enabled === undefined || hasEnableRule(uischema) - ? isEnabled(uischema, rootData, ownProps.path) + ? isEnabled(uischema, rootData, ownProps.path, getOrCreateAjv(state) ) : ownProps.enabled); const controlElement = uischema as ControlElement; const id = ownProps.id; @@ -702,15 +709,16 @@ export const mapStateToLayoutProps = ( ownProps: OwnPropsOfLayout ): LayoutProps => { const rootData = getData(state); + const ajv = getOrCreateAjv(state); const { uischema } = ownProps; const visible: boolean = ownProps.visible === undefined || hasShowRule(uischema) - ? isVisible(ownProps.uischema, rootData, ownProps.path) + ? isVisible(ownProps.uischema, rootData, ownProps.path, getOrCreateAjv(state)) : ownProps.visible; const readOnly = state.jsonforms.readOnly; const enabled: boolean = !readOnly && (ownProps.enabled === undefined || hasEnableRule(uischema) - ? isEnabled(ownProps.uischema, rootData, ownProps.path) + ? isEnabled(ownProps.uischema, rootData, ownProps.path, getOrCreateAjv(state)) : ownProps.enabled); const data = Resolve.data(rootData, ownProps.path); @@ -725,7 +733,8 @@ export const mapStateToLayoutProps = ( data, uischema: ownProps.uischema, schema: ownProps.schema, - direction: ownProps.direction || layoutDefaultProps.direction + direction: ownProps.direction || layoutDefaultProps.direction, + ajv }; }; @@ -800,7 +809,7 @@ const mapStateToCombinatorRendererProps = ( ); const visible: boolean = ownProps.visible === undefined || hasShowRule(uischema) - ? isVisible(uischema, getData(state), ownProps.path) + ? isVisible(uischema, getData(state), ownProps.path, getOrCreateAjv(state)) : ownProps.visible; const id = ownProps.id; diff --git a/packages/core/src/util/runtime.ts b/packages/core/src/util/runtime.ts index ad773fb3df..9a74cbc2a4 100644 --- a/packages/core/src/util/runtime.ts +++ b/packages/core/src/util/runtime.ts @@ -36,8 +36,7 @@ import { import { resolveData } from './resolvers'; import { composeWithUi } from './path'; import { createAjv } from './validator'; - -const ajv = createAjv(); +import { Ajv } from 'ajv'; const isOrCondition = (condition: Condition): condition is OrCondition => condition.type === 'OR'; @@ -59,16 +58,17 @@ const getConditionScope = (condition: Scopable, path: string): string => { const evaluateCondition = ( data: any, condition: Condition, - path: string + path: string, + ajv: Ajv ): boolean => { if (isAndCondition(condition)) { return condition.conditions.reduce( - (acc, cur) => acc && evaluateCondition(data, cur, path), + (acc, cur) => acc && evaluateCondition(data, cur, path, ajv), true ); } else if (isOrCondition(condition)) { return condition.conditions.reduce( - (acc, cur) => acc || evaluateCondition(data, cur, path), + (acc, cur) => acc || evaluateCondition(data, cur, path, ajv), false ); } else if (isLeafCondition(condition)) { @@ -86,18 +86,20 @@ const evaluateCondition = ( const isRuleFulfilled = ( uischema: UISchemaElement, data: any, - path: string + path: string, + ajv: Ajv ): boolean => { const condition = uischema.rule.condition; - return evaluateCondition(data, condition, path); + return evaluateCondition(data, condition, path, ajv); }; export const evalVisibility = ( uischema: UISchemaElement, data: any, - path: string = undefined + path: string = undefined, + ajv: Ajv ): boolean => { - const fulfilled = isRuleFulfilled(uischema, data, path); + const fulfilled = isRuleFulfilled(uischema, data, path, ajv); switch (uischema.rule.effect) { case RuleEffect.HIDE: @@ -113,9 +115,10 @@ export const evalVisibility = ( export const evalEnablement = ( uischema: UISchemaElement, data: any, - path: string = undefined + path: string = undefined, + ajv: Ajv = createAjv() ): boolean => { - const fulfilled = isRuleFulfilled(uischema, data, path); + const fulfilled = isRuleFulfilled(uischema, data, path, ajv); switch (uischema.rule.effect) { case RuleEffect.DISABLE: @@ -153,10 +156,11 @@ export const hasEnableRule = (uischema: UISchemaElement): boolean => { export const isVisible = ( uischema: UISchemaElement, data: any, - path: string = undefined + path: string = undefined, + ajv: Ajv = createAjv() ): boolean => { if (uischema.rule) { - return evalVisibility(uischema, data, path); + return evalVisibility(uischema, data, path, ajv); } return true; @@ -165,10 +169,11 @@ export const isVisible = ( export const isEnabled = ( uischema: UISchemaElement, data: any, - path: string = undefined + path: string = undefined, + ajv: Ajv = createAjv() ): boolean => { if (uischema.rule) { - return evalEnablement(uischema, data, path); + return evalEnablement(uischema, data, path, ajv); } return true; diff --git a/packages/material/src/layouts/MaterialCategorizationLayout.tsx b/packages/material/src/layouts/MaterialCategorizationLayout.tsx index 12fe185e62..cc4f1456b2 100644 --- a/packages/material/src/layouts/MaterialCategorizationLayout.tsx +++ b/packages/material/src/layouts/MaterialCategorizationLayout.tsx @@ -92,7 +92,8 @@ export class MaterialCategorizationLayoutRenderer extends RendererComponent< uischema, visible, enabled, - selected + selected, + ajv } = this.props; const categorization = uischema as Categorization; const value = this.hasOwnState() ? this.state.activeCategory : selected; @@ -107,7 +108,7 @@ export class MaterialCategorizationLayoutRenderer extends RendererComponent< cells }; const categories = categorization.elements.filter((category: Category) => - isVisible(category, data) + isVisible(category, data, undefined, ajv) ); return ( diff --git a/packages/material/src/layouts/MaterialCategorizationStepperLayout.tsx b/packages/material/src/layouts/MaterialCategorizationStepperLayout.tsx index f574ca9c2b..0866b3c442 100644 --- a/packages/material/src/layouts/MaterialCategorizationStepperLayout.tsx +++ b/packages/material/src/layouts/MaterialCategorizationStepperLayout.tsx @@ -82,7 +82,8 @@ export class MaterialCategorizationStepperLayoutRenderer extends RendererCompone uischema, visible, cells, - config + config, + ajv } = this.props; const categorization = uischema as Categorization; const activeCategory = this.state.activeCategory; @@ -108,7 +109,7 @@ export class MaterialCategorizationStepperLayoutRenderer extends RendererCompone cells }; const categories = categorization.elements.filter((category: Category) => - isVisible(category, data) + isVisible(category, data, undefined, ajv) ); return (