From cc2d578dc280538403b815e7689f0b178ee6feb2 Mon Sep 17 00:00:00 2001 From: Florian Gareis Date: Thu, 15 Jul 2021 12:29:54 +0200 Subject: [PATCH] Move logic out of index files Refactors the core package to use index files only for indexing. Functionality can now always be found in files with appropriate names. --- packages/core/src/Helpers.ts | 38 ++ packages/core/src/actions/actions.ts | 335 +++++++++++++ packages/core/src/actions/index.ts | 311 +----------- packages/core/src/configDefault.ts | 1 + packages/core/src/generators/Generate.ts | 43 ++ packages/core/src/generators/index.ts | 24 +- packages/core/src/generators/schema.ts | 2 +- packages/core/src/generators/uischema.ts | 8 +- packages/core/src/index.ts | 37 +- packages/core/src/models/draft4.ts | 1 + packages/core/src/models/index.ts | 30 ++ packages/core/src/models/jsonSchema.ts | 1 + packages/core/src/models/uischema.ts | 1 + packages/core/src/reducers/cells.ts | 3 +- packages/core/src/reducers/config.ts | 3 +- packages/core/src/reducers/core.ts | 8 +- packages/core/src/reducers/default-data.ts | 3 +- packages/core/src/reducers/i18n.ts | 7 +- packages/core/src/reducers/index.ts | 163 +------ packages/core/src/reducers/reducers.ts | 147 ++++++ packages/core/src/reducers/renderers.ts | 3 +- packages/core/src/reducers/selectors.ts | 65 +++ packages/core/src/reducers/uischemas.ts | 6 +- packages/core/src/store.ts | 13 +- packages/core/src/testers/index.ts | 517 +------------------- packages/core/src/testers/testers.ts | 541 +++++++++++++++++++++ packages/core/src/util/Formatted.ts | 1 + packages/core/src/util/array.ts | 25 + packages/core/src/util/cell.ts | 27 +- packages/core/src/util/combinators.ts | 6 +- packages/core/src/util/ids.ts | 1 + packages/core/src/util/index.ts | 134 +---- packages/core/src/util/label.ts | 4 +- packages/core/src/util/path.ts | 5 +- packages/core/src/util/renderer.ts | 36 +- packages/core/src/util/resolvers.ts | 3 +- packages/core/src/util/runtime.ts | 3 +- packages/core/src/util/schema.ts | 27 +- packages/core/src/util/uischema.ts | 3 +- packages/core/src/util/util.ts | 127 +++++ packages/core/src/util/validator.ts | 3 +- 41 files changed, 1498 insertions(+), 1218 deletions(-) create mode 100644 packages/core/src/Helpers.ts create mode 100644 packages/core/src/actions/actions.ts create mode 100644 packages/core/src/generators/Generate.ts create mode 100644 packages/core/src/models/index.ts create mode 100644 packages/core/src/reducers/reducers.ts create mode 100644 packages/core/src/reducers/selectors.ts create mode 100644 packages/core/src/testers/testers.ts create mode 100644 packages/core/src/util/util.ts diff --git a/packages/core/src/Helpers.ts b/packages/core/src/Helpers.ts new file mode 100644 index 000000000..d275f48ce --- /dev/null +++ b/packages/core/src/Helpers.ts @@ -0,0 +1,38 @@ +/* + The MIT License + + Copyright (c) 2017-2019 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import { convertToValidClassName, createLabelDescriptionFrom } from './util'; +import { ControlElement, JsonSchema, LabelDescription } from './models'; + +export const Helpers: { + createLabelDescriptionFrom( + withLabel: ControlElement, + schema: JsonSchema + ): LabelDescription; + convertToValidClassName(s: string): string; +} = { + createLabelDescriptionFrom, + convertToValidClassName +}; diff --git a/packages/core/src/actions/actions.ts b/packages/core/src/actions/actions.ts new file mode 100644 index 000000000..bc4263139 --- /dev/null +++ b/packages/core/src/actions/actions.ts @@ -0,0 +1,335 @@ +/* + The MIT License + + Copyright (c) 2017-2019 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import AJV, { ErrorObject } from 'ajv'; +import { JsonSchema, UISchemaElement } from '../models'; +import { generateDefaultUISchema, generateJsonSchema } from '../generators'; + +import { RankedTester } from '../testers'; +import { UISchemaTester, ValidationMode } from '../reducers'; + +export const INIT: 'jsonforms/INIT' = 'jsonforms/INIT'; +export const UPDATE_CORE: 'jsonforms/UPDATE_CORE' = `jsonforms/UPDATE_CORE`; +export const SET_AJV: 'jsonforms/SET_AJV' = 'jsonforms/SET_AJV'; +export const UPDATE_DATA: 'jsonforms/UPDATE' = 'jsonforms/UPDATE'; +export const UPDATE_ERRORS: 'jsonforms/UPDATE_ERRORS' = + 'jsonforms/UPDATE_ERRORS'; +export const VALIDATE: 'jsonforms/VALIDATE' = 'jsonforms/VALIDATE'; +export const ADD_RENDERER: 'jsonforms/ADD_RENDERER' = 'jsonforms/ADD_RENDERER'; +export const REMOVE_RENDERER: 'jsonforms/REMOVE_RENDERER' = + 'jsonforms/REMOVE_RENDERER'; +export const ADD_CELL: 'jsonforms/ADD_CELL' = 'jsonforms/ADD_CELL'; +export const REMOVE_CELL: 'jsonforms/REMOVE_CELL' = 'jsonforms/REMOVE_CELL'; +export const SET_CONFIG: 'jsonforms/SET_CONFIG' = 'jsonforms/SET_CONFIG'; +export const ADD_UI_SCHEMA: 'jsonforms/ADD_UI_SCHEMA' = `jsonforms/ADD_UI_SCHEMA`; +export const REMOVE_UI_SCHEMA: 'jsonforms/REMOVE_UI_SCHEMA' = `jsonforms/REMOVE_UI_SCHEMA`; +export const SET_SCHEMA: 'jsonforms/SET_SCHEMA' = `jsonforms/SET_SCHEMA`; +export const SET_UISCHEMA: 'jsonforms/SET_UISCHEMA' = `jsonforms/SET_UISCHEMA`; +export const SET_VALIDATION_MODE: 'jsonforms/SET_VALIDATION_MODE' = + 'jsonforms/SET_VALIDATION_MODE'; + +export const SET_LOCALE: 'jsonforms/SET_LOCALE' = `jsonforms/SET_LOCALE`; +export const SET_LOCALIZED_SCHEMAS: 'jsonforms/SET_LOCALIZED_SCHEMAS' = + 'jsonforms/SET_LOCALIZED_SCHEMAS'; +export const SET_LOCALIZED_UISCHEMAS: 'jsonforms/SET_LOCALIZED_UISCHEMAS' = + 'jsonforms/SET_LOCALIZED_UISCHEMAS'; + +export const ADD_DEFAULT_DATA: 'jsonforms/ADD_DEFAULT_DATA' = `jsonforms/ADD_DEFAULT_DATA`; +export const REMOVE_DEFAULT_DATA: 'jsonforms/REMOVE_DEFAULT_DATA' = `jsonforms/REMOVE_DEFAULT_DATA`; + +export type CoreActions = + | InitAction + | UpdateCoreAction + | UpdateAction + | UpdateErrorsAction + | SetAjvAction + | SetSchemaAction + | SetUISchemaAction + | SetValidationModeAction; + +export interface UpdateAction { + type: 'jsonforms/UPDATE'; + path: string; + updater(existingData?: any): any; +} + +export interface UpdateErrorsAction { + type: 'jsonforms/UPDATE_ERRORS'; + errors: ErrorObject[]; +} + +export interface InitAction { + type: 'jsonforms/INIT'; + data: any; + schema: JsonSchema; + uischema: UISchemaElement; + options?: InitActionOptions | AJV.Ajv; +} + +export interface UpdateCoreAction { + type: 'jsonforms/UPDATE_CORE'; + data?: any; + schema?: JsonSchema; + uischema?: UISchemaElement; + options?: InitActionOptions | AJV.Ajv; +} + +export interface InitActionOptions { + ajv?: AJV.Ajv; + validationMode?: ValidationMode; +} + +export interface SetValidationModeAction { + type: 'jsonforms/SET_VALIDATION_MODE' + validationMode: ValidationMode +} + +export const init = ( + data: any, + schema: JsonSchema = generateJsonSchema(data), + uischema?: UISchemaElement, + options?: InitActionOptions | AJV.Ajv +) => ({ + type: INIT, + data, + schema, + uischema: + typeof uischema === 'object' ? uischema : generateDefaultUISchema(schema), + options +}); + +export const updateCore = ( + data: any, + schema: JsonSchema, + uischema?: UISchemaElement, + options?: AJV.Ajv | InitActionOptions +): UpdateCoreAction => ({ + type: UPDATE_CORE, + data, + schema, + uischema, + options +}); + +export interface RegisterDefaultDataAction { + type: 'jsonforms/ADD_DEFAULT_DATA'; + schemaPath: string; + data: any; +} + +export const registerDefaultData = (schemaPath: string, data: any) => ({ + type: ADD_DEFAULT_DATA, + schemaPath, + data +}); + +export interface UnregisterDefaultDataAction { + type: 'jsonforms/REMOVE_DEFAULT_DATA'; + schemaPath: string; +} + +export const unregisterDefaultData = (schemaPath: string) => ({ + type: REMOVE_DEFAULT_DATA, + schemaPath +}); + +export interface SetAjvAction { + type: 'jsonforms/SET_AJV'; + ajv: AJV.Ajv; +} + +export const setAjv = (ajv: AJV.Ajv) => ({ + type: SET_AJV, + ajv +}); + +export const update = ( + path: string, + updater: (existingData: any) => any +): UpdateAction => ({ + type: UPDATE_DATA, + path, + updater +}); + +export const updateErrors = (errors: ErrorObject[]): UpdateErrorsAction => ({ + type: UPDATE_ERRORS, + errors +}); + +export interface AddRendererAction { + type: 'jsonforms/ADD_RENDERER'; + tester: RankedTester; + renderer: any; +} + +export const registerRenderer = (tester: RankedTester, renderer: any) => ({ + type: ADD_RENDERER, + tester, + renderer +}); + +export interface AddCellRendererAction { + type: 'jsonforms/ADD_CELL'; + tester: RankedTester; + cell: any; +} + +export const registerCell = (tester: RankedTester, cell: any) => ({ + type: ADD_CELL, + tester, + cell +}); + +export interface RemoveCellRendererAction { + type: 'jsonforms/REMOVE_CELL'; + tester: RankedTester; + cell: any; +} + +export const unregisterCell = (tester: RankedTester, cell: any) => ({ + type: REMOVE_CELL, + tester, + cell +}); + +export interface RemoveRendererAction { + type: 'jsonforms/REMOVE_RENDERER'; + tester: RankedTester; + renderer: any; +} + +export const unregisterRenderer = (tester: RankedTester, renderer: any) => ({ + type: REMOVE_RENDERER, + tester, + renderer +}); + +export interface SetConfigAction { + type: 'jsonforms/SET_CONFIG'; + config: any; +} + +export const setConfig = (config: any): SetConfigAction => ({ + type: SET_CONFIG, + config +}); + +export const setValidationMode = (validationMode: ValidationMode): SetValidationModeAction => ({ + type: SET_VALIDATION_MODE, + validationMode +}) + +export type UISchemaActions = AddUISchemaAction | RemoveUISchemaAction; + +export interface AddUISchemaAction { + type: 'jsonforms/ADD_UI_SCHEMA'; + tester: UISchemaTester; + uischema: UISchemaElement; +} + +export const registerUISchema = ( + tester: UISchemaTester, + uischema: UISchemaElement +): AddUISchemaAction => { + return { + type: ADD_UI_SCHEMA, + tester, + uischema + }; +}; + +export interface RemoveUISchemaAction { + type: 'jsonforms/REMOVE_UI_SCHEMA'; + tester: UISchemaTester; +} + +export const unregisterUISchema = ( + tester: UISchemaTester +): RemoveUISchemaAction => { + return { + type: REMOVE_UI_SCHEMA, + tester + }; +}; + +export type LocaleActions = + | SetLocaleAction + | SetLocalizedSchemasAction + | SetLocalizedUISchemasAction; + +export interface SetLocaleAction { + type: 'jsonforms/SET_LOCALE'; + locale: string; +} + +export const setLocale = (locale: string): SetLocaleAction => ({ + type: SET_LOCALE, + locale +}); + +export interface SetLocalizedSchemasAction { + type: 'jsonforms/SET_LOCALIZED_SCHEMAS'; + localizedSchemas: Map; +} + +export const setLocalizedSchemas = ( + localizedSchemas: Map +): SetLocalizedSchemasAction => ({ + type: SET_LOCALIZED_SCHEMAS, + localizedSchemas +}); + +export interface SetSchemaAction { + type: 'jsonforms/SET_SCHEMA'; + schema: JsonSchema; +} + +export const setSchema = (schema: JsonSchema): SetSchemaAction => ({ + type: SET_SCHEMA, + schema +}); + +export interface SetLocalizedUISchemasAction { + type: 'jsonforms/SET_LOCALIZED_UISCHEMAS'; + localizedUISchemas: Map; +} + +export const setLocalizedUISchemas = ( + localizedUISchemas: Map +): SetLocalizedUISchemasAction => ({ + type: SET_LOCALIZED_UISCHEMAS, + localizedUISchemas +}); + +export interface SetUISchemaAction { + type: 'jsonforms/SET_UISCHEMA'; + uischema: UISchemaElement; +} + +export const setUISchema = (uischema: UISchemaElement): SetUISchemaAction => ({ + type: SET_UISCHEMA, + uischema +}); diff --git a/packages/core/src/actions/index.ts b/packages/core/src/actions/index.ts index 9a89ad760..04b1af05b 100644 --- a/packages/core/src/actions/index.ts +++ b/packages/core/src/actions/index.ts @@ -22,314 +22,5 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import AJV, { ErrorObject } from 'ajv'; -import { JsonSchema, UISchemaElement } from '../'; -import { generateDefaultUISchema, generateJsonSchema } from '../generators'; -import { RankedTester } from '../testers'; -import { UISchemaTester } from '../reducers/uischemas'; -import { ValidationMode } from '../reducers/core'; - -export const INIT: 'jsonforms/INIT' = 'jsonforms/INIT'; -export const UPDATE_CORE: 'jsonforms/UPDATE_CORE' = `jsonforms/UPDATE_CORE`; -export const SET_AJV: 'jsonforms/SET_AJV' = 'jsonforms/SET_AJV'; -export const UPDATE_DATA: 'jsonforms/UPDATE' = 'jsonforms/UPDATE'; -export const UPDATE_ERRORS: 'jsonforms/UPDATE_ERRORS' = - 'jsonforms/UPDATE_ERRORS'; -export const VALIDATE: 'jsonforms/VALIDATE' = 'jsonforms/VALIDATE'; -export const ADD_RENDERER: 'jsonforms/ADD_RENDERER' = 'jsonforms/ADD_RENDERER'; -export const REMOVE_RENDERER: 'jsonforms/REMOVE_RENDERER' = - 'jsonforms/REMOVE_RENDERER'; -export const ADD_CELL: 'jsonforms/ADD_CELL' = 'jsonforms/ADD_CELL'; -export const REMOVE_CELL: 'jsonforms/REMOVE_CELL' = 'jsonforms/REMOVE_CELL'; -export const SET_CONFIG: 'jsonforms/SET_CONFIG' = 'jsonforms/SET_CONFIG'; -export const ADD_UI_SCHEMA: 'jsonforms/ADD_UI_SCHEMA' = `jsonforms/ADD_UI_SCHEMA`; -export const REMOVE_UI_SCHEMA: 'jsonforms/REMOVE_UI_SCHEMA' = `jsonforms/REMOVE_UI_SCHEMA`; -export const SET_SCHEMA: 'jsonforms/SET_SCHEMA' = `jsonforms/SET_SCHEMA`; -export const SET_UISCHEMA: 'jsonforms/SET_UISCHEMA' = `jsonforms/SET_UISCHEMA`; -export const SET_VALIDATION_MODE: 'jsonforms/SET_VALIDATION_MODE' = - 'jsonforms/SET_VALIDATION_MODE'; - -export const SET_LOCALE: 'jsonforms/SET_LOCALE' = `jsonforms/SET_LOCALE`; -export const SET_LOCALIZED_SCHEMAS: 'jsonforms/SET_LOCALIZED_SCHEMAS' = - 'jsonforms/SET_LOCALIZED_SCHEMAS'; -export const SET_LOCALIZED_UISCHEMAS: 'jsonforms/SET_LOCALIZED_UISCHEMAS' = - 'jsonforms/SET_LOCALIZED_UISCHEMAS'; - -export const ADD_DEFAULT_DATA: 'jsonforms/ADD_DEFAULT_DATA' = `jsonforms/ADD_DEFAULT_DATA`; -export const REMOVE_DEFAULT_DATA: 'jsonforms/REMOVE_DEFAULT_DATA' = `jsonforms/REMOVE_DEFAULT_DATA`; - -export type CoreActions = - | InitAction - | UpdateCoreAction - | UpdateAction - | UpdateErrorsAction - | SetAjvAction - | SetSchemaAction - | SetUISchemaAction - | SetValidationModeAction; - -export interface UpdateAction { - type: 'jsonforms/UPDATE'; - path: string; - updater(existingData?: any): any; -} - -export interface UpdateErrorsAction { - type: 'jsonforms/UPDATE_ERRORS'; - errors: ErrorObject[]; -} - -export interface InitAction { - type: 'jsonforms/INIT'; - data: any; - schema: JsonSchema; - uischema: UISchemaElement; - options?: InitActionOptions | AJV.Ajv; -} - -export interface UpdateCoreAction { - type: 'jsonforms/UPDATE_CORE'; - data?: any; - schema?: JsonSchema; - uischema?: UISchemaElement; - options?: InitActionOptions | AJV.Ajv; -} - -export interface InitActionOptions { - ajv?: AJV.Ajv; - validationMode?: ValidationMode; -} - -export interface SetValidationModeAction { - type: 'jsonforms/SET_VALIDATION_MODE' - validationMode: ValidationMode -} - -export const init = ( - data: any, - schema: JsonSchema = generateJsonSchema(data), - uischema?: UISchemaElement, - options?: InitActionOptions | AJV.Ajv -) => ({ - type: INIT, - data, - schema, - uischema: - typeof uischema === 'object' ? uischema : generateDefaultUISchema(schema), - options -}); - -export const updateCore = ( - data: any, - schema: JsonSchema, - uischema?: UISchemaElement, - options?: AJV.Ajv | InitActionOptions -): UpdateCoreAction => ({ - type: UPDATE_CORE, - data, - schema, - uischema, - options -}); - -export interface RegisterDefaultDataAction { - type: 'jsonforms/ADD_DEFAULT_DATA'; - schemaPath: string; - data: any; -} - -export const registerDefaultData = (schemaPath: string, data: any) => ({ - type: ADD_DEFAULT_DATA, - schemaPath, - data -}); - -export interface UnregisterDefaultDataAction { - type: 'jsonforms/REMOVE_DEFAULT_DATA'; - schemaPath: string; -} - -export const unregisterDefaultData = (schemaPath: string) => ({ - type: REMOVE_DEFAULT_DATA, - schemaPath -}); - -export interface SetAjvAction { - type: 'jsonforms/SET_AJV'; - ajv: AJV.Ajv; -} - -export const setAjv = (ajv: AJV.Ajv) => ({ - type: SET_AJV, - ajv -}); - -export const update = ( - path: string, - updater: (existingData: any) => any -): UpdateAction => ({ - type: UPDATE_DATA, - path, - updater -}); - -export const updateErrors = (errors: ErrorObject[]): UpdateErrorsAction => ({ - type: UPDATE_ERRORS, - errors -}); - -export interface AddRendererAction { - type: 'jsonforms/ADD_RENDERER'; - tester: RankedTester; - renderer: any; -} - -export const registerRenderer = (tester: RankedTester, renderer: any) => ({ - type: ADD_RENDERER, - tester, - renderer -}); - -export interface AddCellRendererAction { - type: 'jsonforms/ADD_CELL'; - tester: RankedTester; - cell: any; -} - -export const registerCell = (tester: RankedTester, cell: any) => ({ - type: ADD_CELL, - tester, - cell -}); - -export interface RemoveCellRendererAction { - type: 'jsonforms/REMOVE_CELL'; - tester: RankedTester; - cell: any; -} - -export const unregisterCell = (tester: RankedTester, cell: any) => ({ - type: REMOVE_CELL, - tester, - cell -}); - -export interface RemoveRendererAction { - type: 'jsonforms/REMOVE_RENDERER'; - tester: RankedTester; - renderer: any; -} - -export const unregisterRenderer = (tester: RankedTester, renderer: any) => ({ - type: REMOVE_RENDERER, - tester, - renderer -}); - -export interface SetConfigAction { - type: 'jsonforms/SET_CONFIG'; - config: any; -} - -export const setConfig = (config: any): SetConfigAction => ({ - type: SET_CONFIG, - config -}); - -export const setValidationMode = (validationMode: ValidationMode): SetValidationModeAction => ({ - type: SET_VALIDATION_MODE, - validationMode -}) - -export type UISchemaActions = AddUISchemaAction | RemoveUISchemaAction; - -export interface AddUISchemaAction { - type: 'jsonforms/ADD_UI_SCHEMA'; - tester: UISchemaTester; - uischema: UISchemaElement; -} - -export const registerUISchema = ( - tester: UISchemaTester, - uischema: UISchemaElement -): AddUISchemaAction => { - return { - type: ADD_UI_SCHEMA, - tester, - uischema - }; -}; - -export interface RemoveUISchemaAction { - type: 'jsonforms/REMOVE_UI_SCHEMA'; - tester: UISchemaTester; -} - -export const unregisterUISchema = ( - tester: UISchemaTester -): RemoveUISchemaAction => { - return { - type: REMOVE_UI_SCHEMA, - tester - }; -}; - -export type LocaleActions = - | SetLocaleAction - | SetLocalizedSchemasAction - | SetLocalizedUISchemasAction; - -export interface SetLocaleAction { - type: 'jsonforms/SET_LOCALE'; - locale: string; -} - -export const setLocale = (locale: string): SetLocaleAction => ({ - type: SET_LOCALE, - locale -}); - -export interface SetLocalizedSchemasAction { - type: 'jsonforms/SET_LOCALIZED_SCHEMAS'; - localizedSchemas: Map; -} - -export const setLocalizedSchemas = ( - localizedSchemas: Map -): SetLocalizedSchemasAction => ({ - type: SET_LOCALIZED_SCHEMAS, - localizedSchemas -}); - -export interface SetSchemaAction { - type: 'jsonforms/SET_SCHEMA'; - schema: JsonSchema; -} - -export const setSchema = (schema: JsonSchema): SetSchemaAction => ({ - type: SET_SCHEMA, - schema -}); - -export interface SetLocalizedUISchemasAction { - type: 'jsonforms/SET_LOCALIZED_UISCHEMAS'; - localizedUISchemas: Map; -} - -export const setLocalizedUISchemas = ( - localizedUISchemas: Map -): SetLocalizedUISchemasAction => ({ - type: SET_LOCALIZED_UISCHEMAS, - localizedUISchemas -}); - -export interface SetUISchemaAction { - type: 'jsonforms/SET_UISCHEMA'; - uischema: UISchemaElement; -} - -export const setUISchema = (uischema: UISchemaElement): SetUISchemaAction => ({ - type: SET_UISCHEMA, - uischema -}); +export * from './actions'; diff --git a/packages/core/src/configDefault.ts b/packages/core/src/configDefault.ts index 6edef56d9..fe3eb6bc2 100644 --- a/packages/core/src/configDefault.ts +++ b/packages/core/src/configDefault.ts @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + export const configDefault = { /* * [text] whether to restrict the number of characters to maxLength, diff --git a/packages/core/src/generators/Generate.ts b/packages/core/src/generators/Generate.ts new file mode 100644 index 000000000..8c5ced381 --- /dev/null +++ b/packages/core/src/generators/Generate.ts @@ -0,0 +1,43 @@ +/* + The MIT License + + Copyright (c) 2017-2019 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import { generateJsonSchema } from './schema'; +import { createControlElement, generateDefaultUISchema } from './uischema'; +import { ControlElement, JsonSchema, UISchemaElement } from '../'; + +export const Generate: { + jsonSchema(instance: Object, options?: any): JsonSchema; + uiSchema( + jsonSchema: JsonSchema, + layoutType?: string, + prefix?: string, + rootSchema?: JsonSchema + ): UISchemaElement; + controlElement(ref: string): ControlElement; +} = { + jsonSchema: generateJsonSchema, + uiSchema: generateDefaultUISchema, + controlElement: createControlElement +}; diff --git a/packages/core/src/generators/index.ts b/packages/core/src/generators/index.ts index 4d1c54ea1..ac68f8635 100644 --- a/packages/core/src/generators/index.ts +++ b/packages/core/src/generators/index.ts @@ -22,25 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { ControlElement, JsonSchema, UISchemaElement } from '../'; -import { generateJsonSchema } from './schema'; -import { createControlElement, generateDefaultUISchema } from './uischema'; - -export const Generate: { - jsonSchema(instance: Object, options?: any): JsonSchema; - uiSchema( - jsonSchema: JsonSchema, - layoutType?: string, - prefix?: string, - rootSchema?: JsonSchema - ): UISchemaElement; - controlElement(ref: string): ControlElement; -} = { - jsonSchema: generateJsonSchema, - uiSchema: generateDefaultUISchema, - controlElement: createControlElement -}; - -export { generateJsonSchema }; -export { generateDefaultUISchema }; +export * from './Generate'; +export * from './schema'; +export * from './uischema'; diff --git a/packages/core/src/generators/schema.ts b/packages/core/src/generators/schema.ts index 17f560f25..787d8733c 100644 --- a/packages/core/src/generators/schema.ts +++ b/packages/core/src/generators/schema.ts @@ -23,7 +23,7 @@ THE SOFTWARE. */ -import { JsonSchema4 } from '../models/jsonSchema4'; +import { JsonSchema4 } from '../models'; const ADDITIONAL_PROPERTIES = 'additionalProperties'; const REQUIRED_PROPERTIES = 'required'; diff --git a/packages/core/src/generators/uischema.ts b/packages/core/src/generators/uischema.ts index 094293ba4..6b76d7d2d 100644 --- a/packages/core/src/generators/uischema.ts +++ b/packages/core/src/generators/uischema.ts @@ -22,20 +22,20 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import isEmpty from 'lodash/isEmpty'; import startCase from 'lodash/startCase'; import keys from 'lodash/keys'; -import { JsonSchema } from '../models/jsonSchema'; import { ControlElement, isGroup, isLayout, + JsonSchema, LabelElement, Layout, UISchemaElement -} from '../models/uischema'; -import { resolveSchema } from '../util/resolvers'; -import { deriveTypes } from '../util'; +} from '../models'; +import { deriveTypes, resolveSchema } from '../util'; /** * Creates a new ILayout. diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 5d2d9ee23..4e44cb35a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -22,40 +22,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -export * from './util'; -export { JsonSchema } from './models/jsonSchema'; -export { JsonSchema4 } from './models/jsonSchema4'; -export { JsonSchema7 } from './models/jsonSchema7'; -export * from './store'; export * from './actions'; -import * as Actions from './actions'; -export { Actions }; -export * from './reducers'; +export * as Actions from './actions'; export * from './generators'; - -export * from './models/uischema'; - -import * as Test from './testers'; +export * from './models'; +export * from './reducers'; export * from './testers'; -export { Test }; - -import { convertToValidClassName, createLabelDescriptionFrom } from './util'; -import { ControlElement, LabelDescription } from './models/uischema'; -import { JsonSchema } from './models/jsonSchema'; - -const Helpers: { - createLabelDescriptionFrom( - withLabel: ControlElement, - schema: JsonSchema - ): LabelDescription; - convertToValidClassName(s: string): string; -} = { - createLabelDescriptionFrom, - convertToValidClassName -}; - -export { Helpers }; - +export * as Test from './testers'; export * from './util'; + +export * from './Helpers'; export * from './store'; diff --git a/packages/core/src/models/draft4.ts b/packages/core/src/models/draft4.ts index 3fe38fd12..5d165f24e 100644 --- a/packages/core/src/models/draft4.ts +++ b/packages/core/src/models/draft4.ts @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + export const Draft4 = { id: 'http://json-schema.org/draft-04/schema#', $schema: 'http://json-schema.org/draft-04/schema#', diff --git a/packages/core/src/models/index.ts b/packages/core/src/models/index.ts new file mode 100644 index 000000000..0f12d7a98 --- /dev/null +++ b/packages/core/src/models/index.ts @@ -0,0 +1,30 @@ +/* + The MIT License + + Copyright (c) 2017-2019 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +export * from './draft4'; +export * from './jsonSchema'; +export * from './jsonSchema4'; +export * from './jsonSchema7'; +export * from './uischema'; diff --git a/packages/core/src/models/jsonSchema.ts b/packages/core/src/models/jsonSchema.ts index b1cb0c40d..0e9aaa04b 100644 --- a/packages/core/src/models/jsonSchema.ts +++ b/packages/core/src/models/jsonSchema.ts @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import { JsonSchema4 } from './jsonSchema4'; import { JsonSchema7 } from './jsonSchema7'; diff --git a/packages/core/src/models/uischema.ts b/packages/core/src/models/uischema.ts index 43869c502..b0c3de039 100644 --- a/packages/core/src/models/uischema.ts +++ b/packages/core/src/models/uischema.ts @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import { JsonSchema } from './jsonSchema'; /** diff --git a/packages/core/src/reducers/cells.ts b/packages/core/src/reducers/cells.ts index 597446810..f7b28e836 100644 --- a/packages/core/src/reducers/cells.ts +++ b/packages/core/src/reducers/cells.ts @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import { RankedTester } from '../testers'; import { ADD_CELL, @@ -29,7 +30,7 @@ import { REMOVE_CELL, RemoveCellRendererAction } from '../actions'; -import { Reducer } from '../util/type'; +import { Reducer } from '../util'; type ValidCellReducerActions = AddCellRendererAction | RemoveCellRendererAction; diff --git a/packages/core/src/reducers/config.ts b/packages/core/src/reducers/config.ts index a115c6164..e40596042 100644 --- a/packages/core/src/reducers/config.ts +++ b/packages/core/src/reducers/config.ts @@ -22,10 +22,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import merge from 'lodash/merge'; import { SET_CONFIG, SetConfigAction } from '../actions'; import { configDefault } from '../configDefault'; -import { Reducer } from '../util/type'; +import { Reducer } from '../util'; const applyDefaultConfiguration = (config: any = {}) => merge({}, configDefault, config); diff --git a/packages/core/src/reducers/core.ts b/packages/core/src/reducers/core.ts index 788fe726b..1109024bf 100644 --- a/packages/core/src/reducers/core.ts +++ b/packages/core/src/reducers/core.ts @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import cloneDeep from 'lodash/cloneDeep'; import setFp from 'lodash/fp/set'; import get from 'lodash/get'; @@ -43,9 +44,8 @@ import { UPDATE_CORE, UpdateCoreAction } from '../actions'; -import { createAjv } from '../util/validator'; -import { Reducer } from '../util/type'; -import { JsonSchema, UISchemaElement } from '..'; +import { createAjv, Reducer } from '../util'; +import { JsonSchema, UISchemaElement } from '../models'; const validate = (validator: ValidateFunction, data: any): ErrorObject[] => { const valid = validator(data); @@ -104,7 +104,7 @@ const getOrCreateAjv = (state: JsonFormsCore, action?: InitAction | UpdateCoreAc // options object with ajv return action.options.ajv; } else if ( - action.options !== undefined + action.options !== undefined ) { // it is not an option object => should be ajv itself => check for compile function if (isFunction(action.options.compile)) { diff --git a/packages/core/src/reducers/default-data.ts b/packages/core/src/reducers/default-data.ts index 711747a5f..6588842c8 100644 --- a/packages/core/src/reducers/default-data.ts +++ b/packages/core/src/reducers/default-data.ts @@ -22,13 +22,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import { ADD_DEFAULT_DATA, RegisterDefaultDataAction, REMOVE_DEFAULT_DATA, UnregisterDefaultDataAction } from '../actions'; -import { Reducer } from '../util/type'; +import { Reducer } from '../util'; export interface JsonFormsDefaultDataRegistryEntry { schemaPath: string; diff --git a/packages/core/src/reducers/i18n.ts b/packages/core/src/reducers/i18n.ts index cae1293af..f8e91d9a3 100644 --- a/packages/core/src/reducers/i18n.ts +++ b/packages/core/src/reducers/i18n.ts @@ -22,9 +22,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { SET_LOCALE, SET_LOCALIZED_SCHEMAS } from '../actions'; -import { JsonSchema, SET_LOCALIZED_UISCHEMAS, UISchemaElement } from '..'; -import { Reducer } from '../util/type'; + +import { SET_LOCALE, SET_LOCALIZED_SCHEMAS, SET_LOCALIZED_UISCHEMAS } from '../actions'; +import { JsonSchema, UISchemaElement } from '../models'; +import { Reducer } from '../util'; export interface JsonFormsLocaleState { locale?: string; diff --git a/packages/core/src/reducers/index.ts b/packages/core/src/reducers/index.ts index c64571008..cae85eeb0 100644 --- a/packages/core/src/reducers/index.ts +++ b/packages/core/src/reducers/index.ts @@ -22,158 +22,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { ControlElement, UISchemaElement } from '../models/uischema'; -import { - coreReducer, - errorAt, - errorsAt, - extractAjv, - extractData, - extractSchema, - extractUiSchema, - JsonFormsCore, - subErrorsAt, - ValidationMode -} from './core'; -import { - extractDefaultData, - JsonFormsDefaultDataRegistryEntry -} from './default-data'; -import { defaultDataReducer } from '../reducers/default-data'; -import { JsonFormsRendererRegistryEntry, rendererReducer } from './renderers'; -import { JsonFormsState } from '../store'; -import { - findMatchingUISchema, - JsonFormsUISchemaRegistryEntry, - uischemaRegistryReducer, - UISchemaTester -} from './uischemas'; -import { - fetchLocale, - findLocalizedSchema, - findLocalizedUISchema, - i18nReducer -} from './i18n'; -import { Generate } from '../generators'; -import { JsonFormsCellRendererRegistryEntry } from './cells'; -import { JsonSchema } from '../models/jsonSchema'; - -import { cellReducer } from './cells'; -import { configReducer } from './config'; -import get from 'lodash/get'; -import { Ajv } from 'ajv'; - -export { - rendererReducer, - cellReducer, - coreReducer, - i18nReducer, - configReducer, - UISchemaTester, - uischemaRegistryReducer, - findMatchingUISchema, - JsonFormsUISchemaRegistryEntry -}; -export { JsonFormsCore, ValidationMode }; - -export const jsonFormsReducerConfig = { - core: coreReducer, - renderers: rendererReducer, - cells: cellReducer, - config: configReducer, - uischemas: uischemaRegistryReducer, - defaultData: defaultDataReducer, - i18n: i18nReducer -}; - -export const getData = (state: JsonFormsState) => - extractData(get(state, 'jsonforms.core')); -export const getSchema = (state: JsonFormsState): JsonSchema => - extractSchema(get(state, 'jsonforms.core')); -export const getUiSchema = (state: JsonFormsState): UISchemaElement => - extractUiSchema(get(state, 'jsonforms.core')); -export const getAjv = ( - state: JsonFormsState -): Ajv => extractAjv(get(state, 'jsonforms.core')); -export const getDefaultData = ( - state: JsonFormsState -): JsonFormsDefaultDataRegistryEntry[] => - extractDefaultData(get(state, 'jsonforms.defaultData')); -export const getRenderers = ( - state: JsonFormsState -): JsonFormsRendererRegistryEntry[] => get(state, 'jsonforms.renderers'); -export const getCells = ( - state: JsonFormsState -): JsonFormsCellRendererRegistryEntry[] => get(state, 'jsonforms.cells'); -export const getUISchemas = ( - state: JsonFormsState -): JsonFormsUISchemaRegistryEntry[] => get(state, 'jsonforms.uischemas'); - -/** - * Finds a registered UI schema to use, if any. - * @param schema the JSON schema describing the data to be rendered - * @param schemaPath the according schema path - * @param path the instance path - * @param fallbackLayoutType the type of the layout to use - * @param control may be checked for embedded inline uischema options - */ -export const findUISchema = ( - uischemas: JsonFormsUISchemaRegistryEntry[], - schema: JsonSchema, - schemaPath: string, - path: string, - fallbackLayoutType = 'VerticalLayout', - control?: ControlElement, - rootSchema?: JsonSchema -): UISchemaElement => { - // handle options - if (control && control.options && control.options.detail) { - if (typeof control.options.detail === 'string') { - if (control.options.detail.toUpperCase() === 'GENERATE') { - // force generation of uischema - return Generate.uiSchema(schema, fallbackLayoutType); - } - } else if (typeof control.options.detail === 'object') { - // check if detail is a valid uischema - if ( - control.options.detail.type && - typeof control.options.detail.type === 'string' - ) { - return control.options.detail as UISchemaElement; - } - } - } - // default - const uiSchema = findMatchingUISchema(uischemas)(schema, schemaPath, path); - if (uiSchema === undefined) { - return Generate.uiSchema(schema, fallbackLayoutType, '#', rootSchema); - } - return uiSchema; -}; - -export const getErrorAt = (instancePath: string, schema: JsonSchema) => ( - state: JsonFormsState -) => { - return errorAt(instancePath, schema)(state.jsonforms.core); -}; - -export { errorsAt }; - -export const getSubErrorsAt = (instancePath: string, schema: JsonSchema) => ( - state: JsonFormsState -) => subErrorsAt(instancePath, schema)(state.jsonforms.core); - -export const getConfig = (state: JsonFormsState) => state.jsonforms.config; - -export const getLocale = (state: JsonFormsState) => - fetchLocale(get(state, 'jsonforms.i18n')); - -export const getLocalizedSchema = (locale: string) => ( - state: JsonFormsState -): JsonSchema => findLocalizedSchema(locale)(get(state, 'jsonforms.i18n')); - -export const getLocalizedUISchema = (locale: string) => ( - state: JsonFormsState -): UISchemaElement => - findLocalizedUISchema(locale)(get(state, 'jsonforms.i18n')); +export * from './cells'; +export * from './config'; +export * from './core'; +export * from './default-data'; +export * from './i18n'; +export * from './reducers'; +export * from './renderers'; +export * from './selectors'; +export * from './uischemas'; diff --git a/packages/core/src/reducers/reducers.ts b/packages/core/src/reducers/reducers.ts new file mode 100644 index 000000000..084f46794 --- /dev/null +++ b/packages/core/src/reducers/reducers.ts @@ -0,0 +1,147 @@ +/* + The MIT License + + Copyright (c) 2017-2019 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import { ControlElement, UISchemaElement } from '../models'; +import { + coreReducer, + errorAt, + errorsAt, + JsonFormsCore, + subErrorsAt, + ValidationMode +} from './core'; +import { defaultDataReducer } from './default-data'; +import { rendererReducer } from './renderers'; +import { JsonFormsState } from '../store'; +import { + findMatchingUISchema, + JsonFormsUISchemaRegistryEntry, + uischemaRegistryReducer, + UISchemaTester +} from './uischemas'; +import { + fetchLocale, + findLocalizedSchema, + findLocalizedUISchema, + i18nReducer +} from './i18n'; + +import { Generate } from '../generators'; +import { JsonSchema } from '../models/jsonSchema'; + +import { cellReducer } from './cells'; +import { configReducer } from './config'; +import get from 'lodash/get'; + +export { + rendererReducer, + cellReducer, + coreReducer, + i18nReducer, + configReducer, + UISchemaTester, + uischemaRegistryReducer, + findMatchingUISchema, + JsonFormsUISchemaRegistryEntry +}; +export { JsonFormsCore, ValidationMode }; + +export const jsonFormsReducerConfig = { + core: coreReducer, + renderers: rendererReducer, + cells: cellReducer, + config: configReducer, + uischemas: uischemaRegistryReducer, + defaultData: defaultDataReducer, + i18n: i18nReducer +}; + +/** + * Finds a registered UI schema to use, if any. + * @param schema the JSON schema describing the data to be rendered + * @param schemaPath the according schema path + * @param path the instance path + * @param fallbackLayoutType the type of the layout to use + * @param control may be checked for embedded inline uischema options + */ +export const findUISchema = ( + uischemas: JsonFormsUISchemaRegistryEntry[], + schema: JsonSchema, + schemaPath: string, + path: string, + fallbackLayoutType = 'VerticalLayout', + control?: ControlElement, + rootSchema?: JsonSchema +): UISchemaElement => { + // handle options + if (control && control.options && control.options.detail) { + if (typeof control.options.detail === 'string') { + if (control.options.detail.toUpperCase() === 'GENERATE') { + // force generation of uischema + return Generate.uiSchema(schema, fallbackLayoutType); + } + } else if (typeof control.options.detail === 'object') { + // check if detail is a valid uischema + if ( + control.options.detail.type && + typeof control.options.detail.type === 'string' + ) { + return control.options.detail as UISchemaElement; + } + } + } + // default + const uiSchema = findMatchingUISchema(uischemas)(schema, schemaPath, path); + if (uiSchema === undefined) { + return Generate.uiSchema(schema, fallbackLayoutType, '#', rootSchema); + } + return uiSchema; +}; + +export const getErrorAt = (instancePath: string, schema: JsonSchema) => ( + state: JsonFormsState +) => { + return errorAt(instancePath, schema)(state.jsonforms.core); +}; + +export { errorsAt }; + +export const getSubErrorsAt = (instancePath: string, schema: JsonSchema) => ( + state: JsonFormsState +) => subErrorsAt(instancePath, schema)(state.jsonforms.core); + +export const getConfig = (state: JsonFormsState) => state.jsonforms.config; + +export const getLocale = (state: JsonFormsState) => + fetchLocale(get(state, 'jsonforms.i18n')); + +export const getLocalizedSchema = (locale: string) => ( + state: JsonFormsState +): JsonSchema => findLocalizedSchema(locale)(get(state, 'jsonforms.i18n')); + +export const getLocalizedUISchema = (locale: string) => ( + state: JsonFormsState +): UISchemaElement => + findLocalizedUISchema(locale)(get(state, 'jsonforms.i18n')); diff --git a/packages/core/src/reducers/renderers.ts b/packages/core/src/reducers/renderers.ts index 9902283a8..643f61698 100644 --- a/packages/core/src/reducers/renderers.ts +++ b/packages/core/src/reducers/renderers.ts @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import { RankedTester } from '../testers'; import { ADD_RENDERER, @@ -29,7 +30,7 @@ import { REMOVE_RENDERER, RemoveRendererAction } from '../actions'; -import { Reducer } from '../util/type'; +import { Reducer } from '../util'; export interface JsonFormsRendererRegistryEntry { tester: RankedTester; diff --git a/packages/core/src/reducers/selectors.ts b/packages/core/src/reducers/selectors.ts new file mode 100644 index 000000000..5927180fb --- /dev/null +++ b/packages/core/src/reducers/selectors.ts @@ -0,0 +1,65 @@ +/* + The MIT License + + Copyright (c) 2017-2019 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import get from 'lodash/get'; +import { Ajv} from 'ajv'; +import { JsonFormsState } from '../store'; +import { JsonSchema, UISchemaElement } from '../models'; +import { + extractAjv, + extractData, + extractSchema, + extractUiSchema, +} from './core'; +import { + extractDefaultData, + JsonFormsDefaultDataRegistryEntry +} from './default-data'; +import { JsonFormsRendererRegistryEntry } from './renderers'; +import { JsonFormsCellRendererRegistryEntry } from './cells'; +import { JsonFormsUISchemaRegistryEntry } from './uischemas'; + +export const getData = (state: JsonFormsState) => + extractData(get(state, 'jsonforms.core')); +export const getSchema = (state: JsonFormsState): JsonSchema => + extractSchema(get(state, 'jsonforms.core')); +export const getUiSchema = (state: JsonFormsState): UISchemaElement => + extractUiSchema(get(state, 'jsonforms.core')); +export const getAjv = ( + state: JsonFormsState +): Ajv => extractAjv(get(state, 'jsonforms.core')); +export const getDefaultData = ( + state: JsonFormsState +): JsonFormsDefaultDataRegistryEntry[] => + extractDefaultData(get(state, 'jsonforms.defaultData')); +export const getRenderers = ( + state: JsonFormsState +): JsonFormsRendererRegistryEntry[] => get(state, 'jsonforms.renderers'); +export const getCells = ( + state: JsonFormsState +): JsonFormsCellRendererRegistryEntry[] => get(state, 'jsonforms.cells'); +export const getUISchemas = ( + state: JsonFormsState +): JsonFormsUISchemaRegistryEntry[] => get(state, 'jsonforms.uischemas') diff --git a/packages/core/src/reducers/uischemas.ts b/packages/core/src/reducers/uischemas.ts index f738bfbf6..3ebe2e230 100644 --- a/packages/core/src/reducers/uischemas.ts +++ b/packages/core/src/reducers/uischemas.ts @@ -22,11 +22,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import maxBy from 'lodash/maxBy'; import remove from 'lodash/remove'; import { ADD_UI_SCHEMA, REMOVE_UI_SCHEMA, UISchemaActions } from '../actions'; -import { JsonSchema, NOT_APPLICABLE, UISchemaElement } from '..'; -import { Reducer } from '../util/type'; +import { NOT_APPLICABLE } from '../testers'; +import { JsonSchema, UISchemaElement } from '../models'; +import { Reducer } from '../util'; export type UISchemaTester = ( schema: JsonSchema, diff --git a/packages/core/src/store.ts b/packages/core/src/store.ts index fe870aea2..6b8a18204 100644 --- a/packages/core/src/store.ts +++ b/packages/core/src/store.ts @@ -22,12 +22,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import { Store } from './util'; -import { JsonFormsCore } from './reducers/core'; -import { JsonFormsCellRendererRegistryEntry } from './reducers/cells'; -import { JsonFormsRendererRegistryEntry } from './reducers/renderers'; -import { JsonFormsLocaleState } from './reducers/i18n'; -import { JsonFormsUISchemaRegistryEntry } from './reducers/uischemas'; +import { + JsonFormsCore, + JsonFormsCellRendererRegistryEntry, + JsonFormsRendererRegistryEntry, + JsonFormsLocaleState, + JsonFormsUISchemaRegistryEntry +} from './reducers'; /** * JSONForms store. diff --git a/packages/core/src/testers/index.ts b/packages/core/src/testers/index.ts index 8dd957401..12b9f6871 100644 --- a/packages/core/src/testers/index.ts +++ b/packages/core/src/testers/index.ts @@ -22,520 +22,5 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import isEmpty from 'lodash/isEmpty'; -import get from 'lodash/get'; -import endsWith from 'lodash/endsWith'; -import last from 'lodash/last'; -import isArray from 'lodash/isArray'; -import reduce from 'lodash/reduce'; -import toPairs from 'lodash/toPairs'; -import includes from 'lodash/includes'; -import { JsonSchema } from '../models/jsonSchema'; -import { - Categorization, - ControlElement, - UISchemaElement -} from '../models/uischema'; -import { resolveSchema } from '../util/resolvers'; -import { deriveTypes, hasType } from '../util'; -/** - * Constant that indicates that a tester is not capable of handling - * a combination of schema/data. - * @type {number} - */ -export const NOT_APPLICABLE = -1; -/** - * A tester is a function that receives an UI schema and a JSON schema and returns a boolean. - */ -export type Tester = (uischema: UISchemaElement, schema: JsonSchema) => boolean; - -/** - * A ranked tester associates a tester with a number. - */ -export type RankedTester = ( - uischema: UISchemaElement, - schema: JsonSchema -) => number; - -export const isControl = (uischema: any): uischema is ControlElement => - !isEmpty(uischema) && uischema.scope !== undefined; - -/** - * Only applicable for Controls. - * - * This function checks whether the given UI schema is of type Control - * and if so, resolves the sub-schema referenced by the control and applies - * the given predicate - * - * @param {(JsonSchema) => boolean} predicate the predicate that should be - * applied to the resolved sub-schema - */ -export const schemaMatches = ( - predicate: (schema: JsonSchema) => boolean -): Tester => (uischema: UISchemaElement, schema: JsonSchema): boolean => { - if (isEmpty(uischema) || !isControl(uischema)) { - return false; - } - if (isEmpty(schema)) { - return false; - } - const schemaPath = uischema.scope; - if (isEmpty(schemaPath)) { - return false; - } - let currentDataSchema = schema; - if (hasType(schema, 'object')) { - currentDataSchema = resolveSchema(schema, schemaPath); - } - if (currentDataSchema === undefined) { - return false; - } - - return predicate(currentDataSchema); -}; - -export const schemaSubPathMatches = ( - subPath: string, - predicate: (schema: JsonSchema) => boolean -): Tester => (uischema: UISchemaElement, schema: JsonSchema): boolean => { - if (isEmpty(uischema) || !isControl(uischema)) { - return false; - } - const schemaPath = uischema.scope; - let currentDataSchema: JsonSchema = schema; - if (hasType(schema, 'object')) { - currentDataSchema = resolveSchema(schema, schemaPath); - } - currentDataSchema = get(currentDataSchema, subPath); - - if (currentDataSchema === undefined) { - return false; - } - - return predicate(currentDataSchema); -}; - -/** - * Only applicable for Controls. - * - * This function checks whether the given UI schema is of type Control - * and if so, resolves the sub-schema referenced by the control and checks - * whether the type of the sub-schema matches the expected one. - * - * @param {string} expectedType the expected type of the resolved sub-schema - */ -export const schemaTypeIs = (expectedType: string): Tester => - schemaMatches(schema => !isEmpty(schema) && hasType(schema, expectedType)); - -/** - * Only applicable for Controls. - * - * This function checks whether the given UI schema is of type Control - * and if so, resolves the sub-schema referenced by the control and checks - * whether the format of the sub-schema matches the expected one. - * - * @param {string} expectedFormat the expected format of the resolved sub-schema - */ -export const formatIs = (expectedFormat: string): Tester => - schemaMatches( - schema => - !isEmpty(schema) && - schema.format === expectedFormat && - schema.type === 'string' - ); - -/** - * Checks whether the given UI schema has the expected type. - * - * @param {string} expected the expected UI schema type - */ -export const uiTypeIs = (expected: string): Tester => ( - uischema: UISchemaElement -): boolean => !isEmpty(uischema) && uischema.type === expected; - -/** - * Checks whether the given UI schema has an option with the given - * name and whether it has the expected value. If no options property - * is set, returns false. - * - * @param {string} optionName the name of the option to check - * @param {any} optionValue the expected value of the option - */ -export const optionIs = (optionName: string, optionValue: any): Tester => ( - uischema: UISchemaElement -): boolean => { - if (isEmpty(uischema)) { - return false; - } - - const options = uischema.options; - return !isEmpty(options) && options[optionName] === optionValue; -}; - -/** - * Only applicable for Controls. - * - * Checks whether the scope of a control ends with the expected string. - * - * @param {string} expected the expected ending of the reference - */ -export const scopeEndsWith = (expected: string): Tester => ( - uischema: UISchemaElement -): boolean => { - if (isEmpty(expected) || !isControl(uischema)) { - return false; - } - - return endsWith(uischema.scope, expected); -}; - -/** - * Only applicable for Controls. - * - * Checks whether the last segment of the scope matches the expected string. - * - * @param {string} expected the expected ending of the reference - */ -export const scopeEndIs = (expected: string): Tester => ( - uischema: UISchemaElement -): boolean => { - if (isEmpty(expected) || !isControl(uischema)) { - return false; - } - const schemaPath = uischema.scope; - - return !isEmpty(schemaPath) && last(schemaPath.split('/')) === expected; -}; - -/** - * A tester that allow composing other testers by && them. - * - * @param {Array} testers the testers to be composed - */ -export const and = (...testers: Tester[]): Tester => ( - uischema: UISchemaElement, - schema: JsonSchema -) => testers.reduce((acc, tester) => acc && tester(uischema, schema), true); - -/** - * A tester that allow composing other testers by || them. - * - * @param {Array} testers the testers to be composed - */ -export const or = (...testers: Tester[]): Tester => ( - uischema: UISchemaElement, - schema: JsonSchema -) => testers.reduce((acc, tester) => acc || tester(uischema, schema), false); -/** - * Create a ranked tester that will associate a number with a given tester, if the - * latter returns true. - * - * @param {number} rank the rank to be returned in case the tester returns true - * @param {Tester} tester a tester - */ -export const rankWith = (rank: number, tester: Tester) => ( - uischema: UISchemaElement, - schema: JsonSchema -): number => { - if (tester(uischema, schema)) { - return rank; - } - - return NOT_APPLICABLE; -}; - -export const withIncreasedRank = (by: number, rankedTester: RankedTester) => ( - uischema: UISchemaElement, - schema: JsonSchema -): number => { - const rank = rankedTester(uischema, schema); - if (rank === NOT_APPLICABLE) { - return NOT_APPLICABLE; - } - - return rank + by; -}; - -/** - * Default tester for boolean. - * @type {RankedTester} - */ -export const isBooleanControl = and( - uiTypeIs('Control'), - schemaTypeIs('boolean') -); - -// TODO: rather check for properties property -export const isObjectControl = and(uiTypeIs('Control'), schemaTypeIs('object')); - -export const isAllOfControl = and( - uiTypeIs('Control'), - schemaMatches(schema => schema.hasOwnProperty('allOf')) -); - -export const isAnyOfControl = and( - uiTypeIs('Control'), - schemaMatches(schema => schema.hasOwnProperty('anyOf')) -); - -export const isOneOfControl = and( - uiTypeIs('Control'), - schemaMatches(schema => schema.hasOwnProperty('oneOf')) -); - -/** - * Tests whether the given UI schema is of type Control and if the schema - * has a 'date' format. - * @type {Tester} - */ -export const isDateControl = and(uiTypeIs('Control'), formatIs('date')); - -/** - * Tests whether the given UI schema is of type Control and if the schema - * has an enum. - * @type {Tester} - */ -export const isEnumControl = and( - uiTypeIs('Control'), - or( - schemaMatches(schema => schema.hasOwnProperty('enum')), - schemaMatches(schema => schema.hasOwnProperty('const')) - ) -); - -/** - * Tests whether the given UI schema is of type Control and if the schema - * has an enum based on oneOf. - * @type {Tester} - */ -export const isOneOfEnumControl = and( - uiTypeIs('Control'), - schemaMatches(schema => - schema.hasOwnProperty('oneOf') && - (schema.oneOf as JsonSchema[]).every(s => s.const !== undefined) - ) -); - -/** - * Tests whether the given UI schema is of type Control and if the schema - * is of type integer - * @type {Tester} - */ -export const isIntegerControl = and( - uiTypeIs('Control'), - schemaTypeIs('integer') -); - -/** - * Tests whether the given UI schema is of type Control and if the schema - * is of type number - * @type {Tester} - */ -export const isNumberControl = and(uiTypeIs('Control'), schemaTypeIs('number')); - -/** - * Tests whether the given UI schema is of type Control and if the schema - * is of type string - * @type {Tester} - */ -export const isStringControl = and(uiTypeIs('Control'), schemaTypeIs('string')); - -/** - * Tests whether the given UI schema is of type Control and if is has - * a 'multi' option. - * @type {Tester} - */ -export const isMultiLineControl = and( - uiTypeIs('Control'), - optionIs('multi', true) -); - -/** - * Tests whether the given UI schema is of type Control and if the schema - * has a 'time' format. - * @type {Tester} - */ -export const isTimeControl = and(uiTypeIs('Control'), formatIs('time')); - -/** - * Tests whether the given UI schema is of type Control and if the schema - * has a 'date-time' format. - * @type {Tester} - */ -export const isDateTimeControl = and( - uiTypeIs('Control'), - formatIs('date-time') -); - -/** - * Tests whether the given schema is an array of objects. - * @type {Tester} - */ -export const isObjectArray = and( - schemaMatches( - schema => hasType(schema, 'array') && !Array.isArray(schema.items) // we don't care about tuples - ), - schemaSubPathMatches('items', schema => hasType(schema, 'object')) -); - -/** - * Tests whether the given UI schema is of type Control and if the schema - * is an array of objects. - * @type {Tester} - */ -export const isObjectArrayControl = and(uiTypeIs('Control'), isObjectArray); - -const traverse = ( - any: JsonSchema | JsonSchema[], - pred: (obj: JsonSchema) => boolean -): boolean => { - if (isArray(any)) { - return reduce(any, (acc, el) => acc || traverse(el, pred), false); - } - - if (pred(any)) { - return true; - } - if (any.items) { - return traverse(any.items, pred); - } - if (any.properties) { - return reduce( - toPairs(any.properties), - (acc, [_key, val]) => acc || traverse(val, pred), - false - ); - } - - return false; -}; - -export const isObjectArrayWithNesting = ( - uischema: UISchemaElement, - schema: JsonSchema -): boolean => { - if (!uiTypeIs('Control')(uischema, schema)) { - return false; - } - const schemaPath = (uischema as ControlElement).scope; - const resolvedSchema = resolveSchema(schema, schemaPath); - const wantedNestingByType: { [key: string]: number } = { - object: 2, - array: 1 - }; - if (resolvedSchema !== undefined && resolvedSchema.items !== undefined) { - // check if nested arrays - if ( - traverse(resolvedSchema.items, val => { - if (val === schema) { - return false; - } - // we don't support multiple types - if (typeof val.type !== 'string') { - return true; - } - const typeCount = wantedNestingByType[val.type]; - if (typeCount === undefined) { - return false; - } - wantedNestingByType[val.type] = typeCount - 1; - return wantedNestingByType[val.type] === 0; - }) - ) { - return true; - } - // check if uischema options detail is set - if (uischema.options && uischema.options.detail) { - if (typeof uischema.options.detail === 'string') { - return uischema.options.detail.toUpperCase() !== 'DEFAULT'; - } else if ( - typeof uischema.options.detail === 'object' && - uischema.options.detail.type - ) { - return true; - } - } - } - return false; -}; - -/** - * Synonym for isObjectArrayControl - */ -export const isArrayObjectControl = isObjectArrayControl; - -/** - * Tests whether the given UI schema is of type Control and if the schema - * is an array of a primitive type. - * @type {Tester} - */ -export const isPrimitiveArrayControl = and( - uiTypeIs('Control'), - schemaMatches( - schema => deriveTypes(schema).length !== 0 && !Array.isArray(schema.items) // we don't care about tuples - ), - schemaSubPathMatches('items', schema => { - const types = deriveTypes(schema); - return ( - types.length === 1 && - includes(['integer', 'number', 'boolean', 'string'], types[0]) - ); - }) -); - -/** - * Tests whether a given UI schema is of type Control, - * if the schema is of type number or integer and - * whether the schema defines a numerical range with a default value. - * @type {Tester} - */ -export const isRangeControl = and( - uiTypeIs('Control'), - or(schemaTypeIs('number'), schemaTypeIs('integer')), - schemaMatches( - schema => - schema.hasOwnProperty('maximum') && - schema.hasOwnProperty('minimum') && - schema.hasOwnProperty('default') - ), - optionIs('slider', true) -); - -/** - * Tests whether the given UI schema is of type Control, if the schema - * is of type string and has option format - * @type {Tester} - */ -export const isNumberFormatControl = and( - uiTypeIs('Control'), - schemaTypeIs('integer'), - optionIs('format', true) -); - -export const isCategorization = ( - category: UISchemaElement -): category is Categorization => category.type === 'Categorization'; - -export const isCategory = (uischema: UISchemaElement): boolean => - uischema.type === 'Category'; - -export const hasCategory = (categorization: Categorization): boolean => { - if (isEmpty(categorization.elements)) { - return false; - } - // all children of the categorization have to be categories - return categorization.elements - .map(elem => - isCategorization(elem) ? hasCategory(elem) : isCategory(elem) - ) - .reduce((prev, curr) => prev && curr, true); -}; - -export const categorizationHasCategory = (uischema: UISchemaElement) => - hasCategory(uischema as Categorization); - -export const not = (tester: Tester): Tester => ( - uischema: UISchemaElement, - schema: JsonSchema -) => !tester(uischema, schema); +export * from './testers'; diff --git a/packages/core/src/testers/testers.ts b/packages/core/src/testers/testers.ts new file mode 100644 index 000000000..3cd9c157e --- /dev/null +++ b/packages/core/src/testers/testers.ts @@ -0,0 +1,541 @@ +/* + The MIT License + + Copyright (c) 2017-2019 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import isEmpty from 'lodash/isEmpty'; +import get from 'lodash/get'; +import endsWith from 'lodash/endsWith'; +import last from 'lodash/last'; +import isArray from 'lodash/isArray'; +import reduce from 'lodash/reduce'; +import toPairs from 'lodash/toPairs'; +import includes from 'lodash/includes'; +import { + Categorization, + ControlElement, + JsonSchema, + UISchemaElement +} from '../models'; +import { deriveTypes, hasType, resolveSchema } from '../util'; + +/** + * Constant that indicates that a tester is not capable of handling + * a combination of schema/data. + * @type {number} + */ +export const NOT_APPLICABLE = -1; +/** + * A tester is a function that receives an UI schema and a JSON schema and returns a boolean. + */ +export type Tester = (uischema: UISchemaElement, schema: JsonSchema) => boolean; + +/** + * A ranked tester associates a tester with a number. + */ +export type RankedTester = ( + uischema: UISchemaElement, + schema: JsonSchema +) => number; + +export const isControl = (uischema: any): uischema is ControlElement => + !isEmpty(uischema) && uischema.scope !== undefined; + +/** + * Only applicable for Controls. + * + * This function checks whether the given UI schema is of type Control + * and if so, resolves the sub-schema referenced by the control and applies + * the given predicate + * + * @param {(JsonSchema) => boolean} predicate the predicate that should be + * applied to the resolved sub-schema + */ +export const schemaMatches = ( + predicate: (schema: JsonSchema) => boolean +): Tester => (uischema: UISchemaElement, schema: JsonSchema): boolean => { + if (isEmpty(uischema) || !isControl(uischema)) { + return false; + } + if (isEmpty(schema)) { + return false; + } + const schemaPath = uischema.scope; + if (isEmpty(schemaPath)) { + return false; + } + let currentDataSchema = schema; + if (hasType(schema, 'object')) { + currentDataSchema = resolveSchema(schema, schemaPath); + } + if (currentDataSchema === undefined) { + return false; + } + + return predicate(currentDataSchema); +}; + +export const schemaSubPathMatches = ( + subPath: string, + predicate: (schema: JsonSchema) => boolean +): Tester => (uischema: UISchemaElement, schema: JsonSchema): boolean => { + if (isEmpty(uischema) || !isControl(uischema)) { + return false; + } + const schemaPath = uischema.scope; + let currentDataSchema: JsonSchema = schema; + if (hasType(schema, 'object')) { + currentDataSchema = resolveSchema(schema, schemaPath); + } + currentDataSchema = get(currentDataSchema, subPath); + + if (currentDataSchema === undefined) { + return false; + } + + return predicate(currentDataSchema); +}; + +/** + * Only applicable for Controls. + * + * This function checks whether the given UI schema is of type Control + * and if so, resolves the sub-schema referenced by the control and checks + * whether the type of the sub-schema matches the expected one. + * + * @param {string} expectedType the expected type of the resolved sub-schema + */ +export const schemaTypeIs = (expectedType: string): Tester => + schemaMatches(schema => !isEmpty(schema) && hasType(schema, expectedType)); + +/** + * Only applicable for Controls. + * + * This function checks whether the given UI schema is of type Control + * and if so, resolves the sub-schema referenced by the control and checks + * whether the format of the sub-schema matches the expected one. + * + * @param {string} expectedFormat the expected format of the resolved sub-schema + */ +export const formatIs = (expectedFormat: string): Tester => + schemaMatches( + schema => + !isEmpty(schema) && + schema.format === expectedFormat && + schema.type === 'string' + ); + +/** + * Checks whether the given UI schema has the expected type. + * + * @param {string} expected the expected UI schema type + */ +export const uiTypeIs = (expected: string): Tester => ( + uischema: UISchemaElement +): boolean => !isEmpty(uischema) && uischema.type === expected; + +/** + * Checks whether the given UI schema has an option with the given + * name and whether it has the expected value. If no options property + * is set, returns false. + * + * @param {string} optionName the name of the option to check + * @param {any} optionValue the expected value of the option + */ +export const optionIs = (optionName: string, optionValue: any): Tester => ( + uischema: UISchemaElement +): boolean => { + if (isEmpty(uischema)) { + return false; + } + + const options = uischema.options; + return !isEmpty(options) && options[optionName] === optionValue; +}; + +/** + * Only applicable for Controls. + * + * Checks whether the scope of a control ends with the expected string. + * + * @param {string} expected the expected ending of the reference + */ +export const scopeEndsWith = (expected: string): Tester => ( + uischema: UISchemaElement +): boolean => { + if (isEmpty(expected) || !isControl(uischema)) { + return false; + } + + return endsWith(uischema.scope, expected); +}; + +/** + * Only applicable for Controls. + * + * Checks whether the last segment of the scope matches the expected string. + * + * @param {string} expected the expected ending of the reference + */ +export const scopeEndIs = (expected: string): Tester => ( + uischema: UISchemaElement +): boolean => { + if (isEmpty(expected) || !isControl(uischema)) { + return false; + } + const schemaPath = uischema.scope; + + return !isEmpty(schemaPath) && last(schemaPath.split('/')) === expected; +}; + +/** + * A tester that allow composing other testers by && them. + * + * @param {Array} testers the testers to be composed + */ +export const and = (...testers: Tester[]): Tester => ( + uischema: UISchemaElement, + schema: JsonSchema +) => testers.reduce((acc, tester) => acc && tester(uischema, schema), true); + +/** + * A tester that allow composing other testers by || them. + * + * @param {Array} testers the testers to be composed + */ +export const or = (...testers: Tester[]): Tester => ( + uischema: UISchemaElement, + schema: JsonSchema +) => testers.reduce((acc, tester) => acc || tester(uischema, schema), false); +/** + * Create a ranked tester that will associate a number with a given tester, if the + * latter returns true. + * + * @param {number} rank the rank to be returned in case the tester returns true + * @param {Tester} tester a tester + */ +export const rankWith = (rank: number, tester: Tester) => ( + uischema: UISchemaElement, + schema: JsonSchema +): number => { + if (tester(uischema, schema)) { + return rank; + } + + return NOT_APPLICABLE; +}; + +export const withIncreasedRank = (by: number, rankedTester: RankedTester) => ( + uischema: UISchemaElement, + schema: JsonSchema +): number => { + const rank = rankedTester(uischema, schema); + if (rank === NOT_APPLICABLE) { + return NOT_APPLICABLE; + } + + return rank + by; +}; + +/** + * Default tester for boolean. + * @type {RankedTester} + */ +export const isBooleanControl = and( + uiTypeIs('Control'), + schemaTypeIs('boolean') +); + +// TODO: rather check for properties property +export const isObjectControl = and(uiTypeIs('Control'), schemaTypeIs('object')); + +export const isAllOfControl = and( + uiTypeIs('Control'), + schemaMatches(schema => schema.hasOwnProperty('allOf')) +); + +export const isAnyOfControl = and( + uiTypeIs('Control'), + schemaMatches(schema => schema.hasOwnProperty('anyOf')) +); + +export const isOneOfControl = and( + uiTypeIs('Control'), + schemaMatches(schema => schema.hasOwnProperty('oneOf')) +); + +/** + * Tests whether the given UI schema is of type Control and if the schema + * has a 'date' format. + * @type {Tester} + */ +export const isDateControl = and(uiTypeIs('Control'), formatIs('date')); + +/** + * Tests whether the given UI schema is of type Control and if the schema + * has an enum. + * @type {Tester} + */ +export const isEnumControl = and( + uiTypeIs('Control'), + or( + schemaMatches(schema => schema.hasOwnProperty('enum')), + schemaMatches(schema => schema.hasOwnProperty('const')) + ) +); + +/** + * Tests whether the given UI schema is of type Control and if the schema + * has an enum based on oneOf. + * @type {Tester} + */ +export const isOneOfEnumControl = and( + uiTypeIs('Control'), + schemaMatches(schema => + schema.hasOwnProperty('oneOf') && + (schema.oneOf as JsonSchema[]).every(s => s.const !== undefined) + ) +); + +/** + * Tests whether the given UI schema is of type Control and if the schema + * is of type integer + * @type {Tester} + */ +export const isIntegerControl = and( + uiTypeIs('Control'), + schemaTypeIs('integer') +); + +/** + * Tests whether the given UI schema is of type Control and if the schema + * is of type number + * @type {Tester} + */ +export const isNumberControl = and(uiTypeIs('Control'), schemaTypeIs('number')); + +/** + * Tests whether the given UI schema is of type Control and if the schema + * is of type string + * @type {Tester} + */ +export const isStringControl = and(uiTypeIs('Control'), schemaTypeIs('string')); + +/** + * Tests whether the given UI schema is of type Control and if is has + * a 'multi' option. + * @type {Tester} + */ +export const isMultiLineControl = and( + uiTypeIs('Control'), + optionIs('multi', true) +); + +/** + * Tests whether the given UI schema is of type Control and if the schema + * has a 'time' format. + * @type {Tester} + */ +export const isTimeControl = and(uiTypeIs('Control'), formatIs('time')); + +/** + * Tests whether the given UI schema is of type Control and if the schema + * has a 'date-time' format. + * @type {Tester} + */ +export const isDateTimeControl = and( + uiTypeIs('Control'), + formatIs('date-time') +); + +/** + * Tests whether the given schema is an array of objects. + * @type {Tester} + */ +export const isObjectArray = and( + schemaMatches( + schema => hasType(schema, 'array') && !Array.isArray(schema.items) // we don't care about tuples + ), + schemaSubPathMatches('items', schema => hasType(schema, 'object')) +); + +/** + * Tests whether the given UI schema is of type Control and if the schema + * is an array of objects. + * @type {Tester} + */ +export const isObjectArrayControl = and(uiTypeIs('Control'), isObjectArray); + +const traverse = ( + any: JsonSchema | JsonSchema[], + pred: (obj: JsonSchema) => boolean +): boolean => { + if (isArray(any)) { + return reduce(any, (acc, el) => acc || traverse(el, pred), false); + } + + if (pred(any)) { + return true; + } + if (any.items) { + return traverse(any.items, pred); + } + if (any.properties) { + return reduce( + toPairs(any.properties), + (acc, [_key, val]) => acc || traverse(val, pred), + false + ); + } + + return false; +}; + +export const isObjectArrayWithNesting = ( + uischema: UISchemaElement, + schema: JsonSchema +): boolean => { + if (!uiTypeIs('Control')(uischema, schema)) { + return false; + } + const schemaPath = (uischema as ControlElement).scope; + const resolvedSchema = resolveSchema(schema, schemaPath); + const wantedNestingByType: { [key: string]: number } = { + object: 2, + array: 1 + }; + if (resolvedSchema !== undefined && resolvedSchema.items !== undefined) { + // check if nested arrays + if ( + traverse(resolvedSchema.items, val => { + if (val === schema) { + return false; + } + // we don't support multiple types + if (typeof val.type !== 'string') { + return true; + } + const typeCount = wantedNestingByType[val.type]; + if (typeCount === undefined) { + return false; + } + wantedNestingByType[val.type] = typeCount - 1; + return wantedNestingByType[val.type] === 0; + }) + ) { + return true; + } + // check if uischema options detail is set + if (uischema.options && uischema.options.detail) { + if (typeof uischema.options.detail === 'string') { + return uischema.options.detail.toUpperCase() !== 'DEFAULT'; + } else if ( + typeof uischema.options.detail === 'object' && + uischema.options.detail.type + ) { + return true; + } + } + } + return false; +}; + +/** + * Synonym for isObjectArrayControl + */ +export const isArrayObjectControl = isObjectArrayControl; + +/** + * Tests whether the given UI schema is of type Control and if the schema + * is an array of a primitive type. + * @type {Tester} + */ +export const isPrimitiveArrayControl = and( + uiTypeIs('Control'), + schemaMatches( + schema => deriveTypes(schema).length !== 0 && !Array.isArray(schema.items) // we don't care about tuples + ), + schemaSubPathMatches('items', schema => { + const types = deriveTypes(schema); + return ( + types.length === 1 && + includes(['integer', 'number', 'boolean', 'string'], types[0]) + ); + }) +); + +/** + * Tests whether a given UI schema is of type Control, + * if the schema is of type number or integer and + * whether the schema defines a numerical range with a default value. + * @type {Tester} + */ +export const isRangeControl = and( + uiTypeIs('Control'), + or(schemaTypeIs('number'), schemaTypeIs('integer')), + schemaMatches( + schema => + schema.hasOwnProperty('maximum') && + schema.hasOwnProperty('minimum') && + schema.hasOwnProperty('default') + ), + optionIs('slider', true) +); + +/** + * Tests whether the given UI schema is of type Control, if the schema + * is of type string and has option format + * @type {Tester} + */ +export const isNumberFormatControl = and( + uiTypeIs('Control'), + schemaTypeIs('integer'), + optionIs('format', true) +); + +export const isCategorization = ( + category: UISchemaElement +): category is Categorization => category.type === 'Categorization'; + +export const isCategory = (uischema: UISchemaElement): boolean => + uischema.type === 'Category'; + +export const hasCategory = (categorization: Categorization): boolean => { + if (isEmpty(categorization.elements)) { + return false; + } + // all children of the categorization have to be categories + return categorization.elements + .map(elem => + isCategorization(elem) ? hasCategory(elem) : isCategory(elem) + ) + .reduce((prev, curr) => prev && curr, true); +}; + +export const categorizationHasCategory = (uischema: UISchemaElement) => + hasCategory(uischema as Categorization); + +export const not = (tester: Tester): Tester => ( + uischema: UISchemaElement, + schema: JsonSchema +) => !tester(uischema, schema); diff --git a/packages/core/src/util/Formatted.ts b/packages/core/src/util/Formatted.ts index 2897b06af..be0505a16 100644 --- a/packages/core/src/util/Formatted.ts +++ b/packages/core/src/util/Formatted.ts @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + /** * Interface for mapping a given type to a formatted string and back. */ diff --git a/packages/core/src/util/array.ts b/packages/core/src/util/array.ts index b5aa46ce8..e2ed48155 100644 --- a/packages/core/src/util/array.ts +++ b/packages/core/src/util/array.ts @@ -1,3 +1,28 @@ +/* + The MIT License + + Copyright (c) 2017-2019 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + const move = (array: any[], index: number, delta: number) => { const newIndex: number = index + delta; if (newIndex < 0 || newIndex >= array.length) { diff --git a/packages/core/src/util/cell.ts b/packages/core/src/util/cell.ts index 18bc19aa5..805fe23e4 100644 --- a/packages/core/src/util/cell.ts +++ b/packages/core/src/util/cell.ts @@ -22,23 +22,38 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import isEmpty from 'lodash/isEmpty'; import union from 'lodash/union'; -import { getConfig, getData, getErrorAt, getSchema, getAjv } from '../reducers'; +import { + getConfig, + getData, + getErrorAt, + getSchema, + getAjv, + JsonFormsCellRendererRegistryEntry +} from '../reducers'; +import { AnyAction, Dispatch } from './type'; +import { + formatErrorMessage, + Resolve, +} from './util'; +import { + isInherentlyEnabled, + isVisible, +} from './runtime'; import { DispatchPropsOfControl, EnumOption, enumToEnumOptionMapper, mapDispatchToControlProps, + oneOfToEnumOptionMapper, OwnPropsOfControl, OwnPropsOfEnum, - StatePropsOfScopedRenderer + StatePropsOfScopedRenderer, } from './renderer'; import { JsonFormsState } from '../store'; -import { JsonFormsCellRendererRegistryEntry } from '../reducers/cells'; -import { JsonSchema } from '../models/jsonSchema'; -import { isInherentlyEnabled, isVisible } from './runtime'; -import { AnyAction, Dispatch, formatErrorMessage, oneOfToEnumOptionMapper, Resolve } from '.'; +import { JsonSchema } from '../models'; export { JsonFormsCellRendererRegistryEntry }; diff --git a/packages/core/src/util/combinators.ts b/packages/core/src/util/combinators.ts index 3c35fd83b..c89496f5d 100644 --- a/packages/core/src/util/combinators.ts +++ b/packages/core/src/util/combinators.ts @@ -23,11 +23,9 @@ THE SOFTWARE. */ -import { JsonSchema } from '../models/jsonSchema'; -import { ControlElement, UISchemaElement } from '../models/uischema'; +import { ControlElement, JsonSchema, UISchemaElement } from '../models'; import { resolveSchema } from './resolvers'; -import { findUISchema } from '../reducers'; -import { JsonFormsUISchemaRegistryEntry } from '../reducers/uischemas'; +import { findUISchema, JsonFormsUISchemaRegistryEntry } from '../reducers'; export interface CombinatorSubSchemaRenderInfo { schema: JsonSchema; diff --git a/packages/core/src/util/ids.ts b/packages/core/src/util/ids.ts index c9de094df..4914754f4 100644 --- a/packages/core/src/util/ids.ts +++ b/packages/core/src/util/ids.ts @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + const usedIds: Set = new Set(); const makeId = (idBase: string, iteration: number) => diff --git a/packages/core/src/util/index.ts b/packages/core/src/util/index.ts index 024dd9495..489ac18f1 100644 --- a/packages/core/src/util/index.ts +++ b/packages/core/src/util/index.ts @@ -22,131 +22,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import isEmpty from 'lodash/isEmpty'; -import isArray from 'lodash/isArray'; -import includes from 'lodash/includes'; -import find from 'lodash/find'; -import { JsonSchema, Scopable, UISchemaElement } from '../'; -import { resolveData, resolveSchema } from './resolvers'; -import { - compose as composePaths, - composeWithUi, - toDataPath, - toDataPathSegments -} from './path'; -import { isEnabled, isVisible } from './runtime'; -import { Ajv } from 'ajv'; -export { createCleanLabel, createLabelDescriptionFrom } from './label'; - -/** - * Escape the given string such that it can be used as a class name, - * i.e. hashes and slashes will be replaced. - * - * @param {string} s the string that should be converted to a valid class name - * @returns {string} the escaped string - */ -export const convertToValidClassName = (s: string): string => - s.replace('#', 'root').replace(new RegExp('/', 'g'), '_'); - -export const formatErrorMessage = (errors: string[]) => { - if (errors === undefined || errors === null) { - return ''; - } - - return errors.join('\n'); -}; - -const hasType = (jsonSchema: JsonSchema, expected: string): boolean => { - return includes(deriveTypes(jsonSchema), expected); -}; - -/** - * Derives the type of the jsonSchema element - */ -const deriveTypes = (jsonSchema: JsonSchema): string[] => { - if (isEmpty(jsonSchema)) { - return []; - } - if (!isEmpty(jsonSchema.type) && typeof jsonSchema.type === 'string') { - return [jsonSchema.type]; - } - if (isArray(jsonSchema.type)) { - return jsonSchema.type; - } - if ( - !isEmpty(jsonSchema.properties) || - !isEmpty(jsonSchema.additionalProperties) - ) { - return ['object']; - } - if (!isEmpty(jsonSchema.items)) { - return ['array']; - } - - if (!isEmpty(jsonSchema.allOf)) { - const allOfType = find( - jsonSchema.allOf, - (schema: JsonSchema) => deriveTypes(schema).length !== 0 - ); - - if (allOfType) { - return deriveTypes(allOfType); - } - } - // ignore all remaining cases - return []; -}; - -/** - * Convenience wrapper around resolveData and resolveSchema. - */ -const Resolve: { - schema( - schema: JsonSchema, - schemaPath: string, - rootSchema?: JsonSchema - ): JsonSchema; - data(data: any, path: string): any; -} = { - schema: resolveSchema, - data: resolveData -}; -export { - resolveData, - resolveSchema, -} from './resolvers'; -export { Resolve }; - -// Paths -- -const fromScopable = (scopable: Scopable) => - toDataPathSegments(scopable.scope).join('.'); - -const Paths = { - compose: composePaths, - fromScopable -}; -export { composePaths, composeWithUi, Paths, toDataPath }; - -// Runtime -- -const Runtime = { - isEnabled(uischema: UISchemaElement, data: any, ajv: Ajv): boolean { - return isEnabled(uischema, data,undefined, ajv); - }, - isVisible(uischema: UISchemaElement, data: any, ajv: Ajv): boolean { - return isVisible(uischema, data, undefined, ajv); - } -}; -export { isEnabled, isVisible, Runtime, deriveTypes, hasType }; - -export * from './renderer'; +export * from './array'; export * from './cell'; -export * from './runtime'; +export * from './combinators'; export * from './Formatted'; export * from './ids'; -export * from './validator'; -export * from './combinators'; -export * from './uischema'; -export * from './array'; -export * from './type'; +export * from './label'; +export * from './path'; +export * from './renderer'; +export * from './resolvers'; +export * from './runtime'; export * from './schema'; +export * from './type'; +export * from './uischema'; +export * from './util'; +export * from './validator'; diff --git a/packages/core/src/util/label.ts b/packages/core/src/util/label.ts index 267d439c6..05f2dced8 100644 --- a/packages/core/src/util/label.ts +++ b/packages/core/src/util/label.ts @@ -22,10 +22,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import startCase from 'lodash/startCase'; -import { ControlElement, LabelDescription } from '../models/uischema'; -import { JsonSchema } from '../models/jsonSchema'; +import { ControlElement, JsonSchema, LabelDescription } from '../models'; const deriveLabel = ( controlElement: ControlElement, diff --git a/packages/core/src/util/path.ts b/packages/core/src/util/path.ts index c9004ddc8..24b4ebd8a 100644 --- a/packages/core/src/util/path.ts +++ b/packages/core/src/util/path.ts @@ -22,9 +22,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import isEmpty from 'lodash/isEmpty'; import range from 'lodash/range'; -import { Scopable } from '../'; +import { Scopable } from '../models'; export const compose = (path1: string, path2: string) => { let p1 = path1; @@ -41,6 +42,8 @@ export const compose = (path1: string, path2: string) => { } }; +export { compose as composePaths }; + /** * Convert a schema path (i.e. JSON pointer) to an array by splitting * at the '/' character and removing all schema-specific keywords. diff --git a/packages/core/src/util/renderer.ts b/packages/core/src/util/renderer.ts index f6e2c5d08..cd43d3ee5 100644 --- a/packages/core/src/util/renderer.ts +++ b/packages/core/src/util/renderer.ts @@ -22,8 +22,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import get from 'lodash/get'; -import { ControlElement, UISchemaElement } from '../models/uischema'; +import { ControlElement, JsonSchema, UISchemaElement } from '../models'; import union from 'lodash/union'; import find from 'lodash/find'; import { @@ -36,32 +37,23 @@ import { getRenderers, getSchema, getSubErrorsAt, - getUiSchema + getUiSchema, + JsonFormsCellRendererRegistryEntry, + JsonFormsRendererRegistryEntry, + JsonFormsUISchemaRegistryEntry, } from '../reducers'; import { RankedTester } from '../testers'; -import { JsonSchema } from '../models/jsonSchema'; -import { - AnyAction, - CombinatorKeyword, - composePaths, - composeWithUi, - createLabelDescriptionFrom, - Dispatch, - formatErrorMessage, - hasShowRule, - isInherentlyEnabled, - isVisible, - moveDown, - moveUp, - Resolve, - resolveSubSchemas -} from '../util'; +import { isInherentlyEnabled, hasShowRule } from './runtime'; +import { createLabelDescriptionFrom } from './label'; +import { CombinatorKeyword, resolveSubSchemas } from './combinators'; +import { moveDown, moveUp } from './array'; +import { AnyAction, Dispatch } from './type'; +import { formatErrorMessage, Resolve } from './util'; +import { composePaths, composeWithUi } from './path'; +import { isVisible } from './runtime'; import { CoreActions, update } from '../actions'; import { ErrorObject } from 'ajv'; import { JsonFormsState } from '../store'; -import { JsonFormsRendererRegistryEntry } from '../reducers/renderers'; -import { JsonFormsCellRendererRegistryEntry } from '../reducers/cells'; -import { JsonFormsUISchemaRegistryEntry } from '../reducers/uischemas'; export { JsonFormsRendererRegistryEntry, JsonFormsCellRendererRegistryEntry }; diff --git a/packages/core/src/util/resolvers.ts b/packages/core/src/util/resolvers.ts index ae1554146..9be6c1f30 100644 --- a/packages/core/src/util/resolvers.ts +++ b/packages/core/src/util/resolvers.ts @@ -22,9 +22,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import isEmpty from 'lodash/isEmpty'; import get from 'lodash/get'; -import { JsonSchema } from '../models/jsonSchema'; +import { JsonSchema } from '../models'; /** * Map for storing refs and the respective schemas they are pointing to. diff --git a/packages/core/src/util/runtime.ts b/packages/core/src/util/runtime.ts index 6160ede16..c5567fe87 100644 --- a/packages/core/src/util/runtime.ts +++ b/packages/core/src/util/runtime.ts @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import has from 'lodash/has'; import { AndCondition, @@ -32,7 +33,7 @@ import { SchemaBasedCondition, Scopable, UISchemaElement -} from '../models/uischema'; +} from '../models'; import { resolveData } from './resolvers'; import { composeWithUi } from './path'; import { Ajv } from 'ajv'; diff --git a/packages/core/src/util/schema.ts b/packages/core/src/util/schema.ts index 7a189926c..9ade7803b 100644 --- a/packages/core/src/util/schema.ts +++ b/packages/core/src/util/schema.ts @@ -1,4 +1,29 @@ -import find from "lodash/find"; +/* + The MIT License + + Copyright (c) 2017-2019 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import find from 'lodash/find'; export const getFirstPrimitiveProp = (schema: any) => { if (schema.properties) { diff --git a/packages/core/src/util/uischema.ts b/packages/core/src/util/uischema.ts index bdfcfaf2e..0d409fe97 100644 --- a/packages/core/src/util/uischema.ts +++ b/packages/core/src/util/uischema.ts @@ -22,8 +22,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import isEmpty from 'lodash/isEmpty'; -import { isLayout, UISchemaElement } from '../models/uischema'; +import { isLayout, UISchemaElement } from '../models'; export type IterateCallback = (uischema: UISchemaElement) => void; diff --git a/packages/core/src/util/util.ts b/packages/core/src/util/util.ts new file mode 100644 index 000000000..3d5a86ad5 --- /dev/null +++ b/packages/core/src/util/util.ts @@ -0,0 +1,127 @@ +/* + The MIT License + + Copyright (c) 2017-2019 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import isEmpty from 'lodash/isEmpty'; +import isArray from 'lodash/isArray'; +import includes from 'lodash/includes'; +import find from 'lodash/find'; +import { JsonSchema, Scopable, UISchemaElement } from '..'; +import { resolveData, resolveSchema } from './resolvers'; +import { composePaths, toDataPathSegments } from './path'; +import { isEnabled, isVisible } from './runtime'; +import { Ajv } from 'ajv'; + +/** + * Escape the given string such that it can be used as a class name, + * i.e. hashes and slashes will be replaced. + * + * @param {string} s the string that should be converted to a valid class name + * @returns {string} the escaped string + */ +export const convertToValidClassName = (s: string): string => +s.replace('#', 'root').replace(new RegExp('/', 'g'), '_'); + +export const formatErrorMessage = (errors: string[]) => { + if (errors === undefined || errors === null) { + return ''; + } + + return errors.join('\n'); +}; + +export const hasType = (jsonSchema: JsonSchema, expected: string): boolean => { + return includes(deriveTypes(jsonSchema), expected); +}; + +/** +* Derives the type of the jsonSchema element +*/ +export const deriveTypes = (jsonSchema: JsonSchema): string[] => { + if (isEmpty(jsonSchema)) { + return []; + } + if (!isEmpty(jsonSchema.type) && typeof jsonSchema.type === 'string') { + return [jsonSchema.type]; + } + if (isArray(jsonSchema.type)) { + return jsonSchema.type; + } + if ( + !isEmpty(jsonSchema.properties) || + !isEmpty(jsonSchema.additionalProperties) + ) { + return ['object']; + } + if (!isEmpty(jsonSchema.items)) { + return ['array']; + } + + if (!isEmpty(jsonSchema.allOf)) { + const allOfType = find( + jsonSchema.allOf, + (schema: JsonSchema) => deriveTypes(schema).length !== 0 + ); + + if (allOfType) { + return deriveTypes(allOfType); + } + } + // ignore all remaining cases + return []; +}; + +/** +* Convenience wrapper around resolveData and resolveSchema. +*/ +export const Resolve: { + schema( + schema: JsonSchema, + schemaPath: string, + rootSchema?: JsonSchema + ): JsonSchema; + data(data: any, path: string): any; +} = { + schema: resolveSchema, + data: resolveData +}; + +// Paths -- +const fromScopable = (scopable: Scopable) => + toDataPathSegments(scopable.scope).join('.'); + +export const Paths = { + compose: composePaths, + fromScopable +}; + +// Runtime -- +export const Runtime = { + isEnabled(uischema: UISchemaElement, data: any, ajv: Ajv): boolean { + return isEnabled(uischema, data,undefined, ajv); + }, + isVisible(uischema: UISchemaElement, data: any, ajv: Ajv): boolean { + return isVisible(uischema, data, undefined, ajv); + } +}; diff --git a/packages/core/src/util/validator.ts b/packages/core/src/util/validator.ts index 4c6964ca7..2d3a3e42c 100644 --- a/packages/core/src/util/validator.ts +++ b/packages/core/src/util/validator.ts @@ -22,9 +22,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + import AJV from 'ajv'; import { Options } from 'ajv'; -import { Draft4 } from '../models/draft4'; +import { Draft4 } from '../models'; export const createAjv = (options?: Options) => { const ajv = new AJV({