Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Packages/utils/schema #2877

Merged
merged 23 commits into from
Jun 24, 2022
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
29e4736
Implemented the utils package
heath-freenome Apr 27, 2022
48cf1f1
- More changes
heath-freenome Apr 28, 2022
66803a2
- Added all of the non-validation-based utilities and many of the tests
heath-freenome May 19, 2022
18ec36f
- removed schema related files temporarily
heath-freenome May 19, 2022
8b2a3a1
- revert `package-lock.json` for antd, core and playground
heath-freenome May 20, 2022
8b5220e
- The schema utilities that require validation
heath-freenome May 20, 2022
b0e9010
- More changes
heath-freenome May 20, 2022
150ff09
- completed conversion to Typescript with some changes that were needed
heath-freenome May 24, 2022
c8ad992
- Updated various files to make some parameters optional, to add gene…
heath-freenome May 24, 2022
dca2450
- Implemented createSchemaUtils
heath-freenome May 27, 2022
cbb27aa
- More fixes and adding missing generics
heath-freenome May 31, 2022
39d9d44
- Converted all the schema based tests over still missing 100% coverage
heath-freenome Jun 7, 2022
51dbca6
- Completed 100% unit testing
heath-freenome Jun 7, 2022
1ead960
- Added documentation for most of the files and cleaned up some optio…
heath-freenome Jun 8, 2022
bbb3de2
- Completed all of the documentation of the schema-based utils
heath-freenome Jun 9, 2022
251f850
- Changed the `_NAME` constants to `_KEY`
heath-freenome Jun 9, 2022
7c6a348
- Added missing generics on a few types
heath-freenome Jun 16, 2022
fed0877
- Added `ERRORS_KEY` constant for use in validation
heath-freenome Jun 16, 2022
f3ef93a
- Fixed types based on implementation of validator
heath-freenome Jun 21, 2022
97f40bc
- Added missing generic to the `ValidationData` type for `ErrorSchema`
heath-freenome Jun 22, 2022
55ae809
- Fixed tests for the missing generic type
heath-freenome Jun 23, 2022
5249a66
- Incorporated #2876 by making props for UISchemaSubmitButtonOptions …
heath-freenome Jun 23, 2022
c6217d3
- Added generic `<T>` to the return value of `toIdSchema()` and `toPa…
heath-freenome Jun 24, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
"jest-expect-message"
],
"testMatch": [
"**/test/**/*.test.[jt]s?(x)"
"**/test/**/*.test.ts?(x)"
],
"coverageDirectory": "<rootDir>/coverage/",
"collectCoverage": true,
Expand Down
26 changes: 15 additions & 11 deletions packages/utils/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
export const ADDITIONAL_PROPERTY_FLAG = '__additional_property';
export const ADDITIONAL_PROPERTIES_NAME = 'additionalProperties';
export const ALL_OF_NAME = 'allOf';
export const CONST_NAME = 'const';
export const DEPENDENCIES_NAME = 'dependencies';
export const ITEMS_NAME = 'items';
export const PROPERTIES_NAME = 'properties';
export const SUBMIT_BTN_OPTIONS_NAME = 'submitButtonOptions';
export const REF_NAME = '$ref';
export const UI_FIELD_NAME = 'ui:field';
export const UI_WIDGET_NAME = 'ui:widget';
export const UI_OPTIONS_NAME = 'ui:options';
export const ADDITIONAL_PROPERTIES_KEY = 'additionalProperties';
export const ALL_OF_KEY = 'allOf';
export const ANY_OF_KEY = 'anyOf';
export const CONST_KEY = 'const';
export const DEFAULT_KEY = 'default';
export const DEPENDENCIES_KEY = 'dependencies';
export const ERRORS_KEY = '__errors';
export const ITEMS_KEY = 'items';
export const ONE_OF_KEY = 'oneOf';
export const PROPERTIES_KEY = 'properties';
export const SUBMIT_BTN_OPTIONS_KEY = 'submitButtonOptions';
export const REF_KEY = '$ref';
export const UI_FIELD_KEY = 'ui:field';
export const UI_WIDGET_KEY = 'ui:widget';
export const UI_OPTIONS_KEY = 'ui:options';
152 changes: 152 additions & 0 deletions packages/utils/src/createSchemaUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { RJSFSchema, SchemaUtilsType, UiSchema, ValidatorType } from './types';
import {
getDefaultFormState,
getDisplayLabel,
getMatchingOption,
isFilesArray,
isMultiSelect,
isSelect,
retrieveSchema,
stubExistingAdditionalProperties,
toIdSchema,
toPathSchema,
} from './schema';

/** The `SchemaUtils` class provides a wrapper around the publically exported APIs in the `utils/schema` directory such
* that one does not have to explicitly pass the `validator` or `rootSchema` to each method. Since both the `validator`
* and `rootSchema` generally does not change across a `Form`, this allows for providing a simplified set of APIs to the
* `@rjsf/core` components and the various themes as well. This class implements the `SchemaUtilsType` interface.
*/
class SchemaUtils<T = any> implements SchemaUtilsType<T> {
rootSchema: RJSFSchema;
validator: ValidatorType;

/** Constructs the `SchemaUtils` instance with the given `validator` and `rootSchema` stored as instance variables
*
* @param validator - An implementation of the `ValidatorType` interface that will be forwarded to all the APIs
* @param rootSchema - The root schema that will be forwarded to all the APIs
*/
constructor(validator: ValidatorType, rootSchema: RJSFSchema) {
this.rootSchema = rootSchema;
this.validator = validator;
}

/** Returns the superset of `formData` that includes the given set updated to include any missing fields that have
* computed to have defaults provided in the `schema`.
*
* @param schema - The schema for which the default state is desired
* @param [formData] - The current formData, if any, onto which to provide any missing defaults
* @param [includeUndefinedValues=false] - Optional flag, if true, cause undefined values to be added as defaults
* @returns - The resulting `formData` with all the defaults provided
*/
getDefaultFormState(schema: RJSFSchema, formData?: T, includeUndefinedValues = false): T | T[] | undefined {
return getDefaultFormState<T>(this.validator, schema, formData, this.rootSchema, includeUndefinedValues);
}

/** Determines whether the combination of `schema` and `uiSchema` properties indicates that the label for the `schema`
* should be displayed in a UI.
*
* @param schema - The schema for which the display label flag is desired
* @param uiSchema - The UI schema from which to derive potentially displayable information
* @returns - True if the label should be displayed or false if it should not
*/
getDisplayLabel<F = any>(schema: RJSFSchema, uiSchema: UiSchema<T, F>) {
return getDisplayLabel<T, F>(this.validator, schema, uiSchema, this.rootSchema);
}

/** Given the `formData` and list of `options`, attempts to find the index of the option that best matches the data.
*
* @param formData - The current formData, if any, onto which to provide any missing defaults
* @param options - The list of options to find a matching options from
* @returns - The index of the matched option or 0 if none is available
*/
getMatchingOption(formData: T, options: RJSFSchema[]) {
return getMatchingOption<T>(this.validator, formData, options, this.rootSchema);
}

/** Checks to see if the `schema` and `uiSchema` combination represents an array of files
*
* @param schema - The schema for which check for array of files flag is desired
* @param uiSchema - The UI schema from which to check the widget
* @returns - True if schema/uiSchema contains an array of files, otherwise false
*/
isFilesArray<F = any>(schema: RJSFSchema, uiSchema: UiSchema<T, F>) {
return isFilesArray<T, F>(this.validator, schema, uiSchema, this.rootSchema);
}

/** Checks to see if the `schema` combination represents a multi-select
*
* @param schema - The schema for which check for a multi-select flag is desired
* @returns - True if schema contains a multi-select, otherwise false
*/
isMultiSelect(schema: RJSFSchema) {
return isMultiSelect<T>(this.validator, schema, this.rootSchema);
}

/** Checks to see if the `schema` combination represents a select
*
* @param schema - The schema for which check for a select flag is desired
* @returns - True if schema contains a select, otherwise false
*/
isSelect(schema: RJSFSchema) {
return isSelect<T>(this.validator, schema, this.rootSchema);
}

/** Retrieves an expanded schema that has had all of its conditions, additional properties, references and
* dependencies resolved and merged into the `schema` given a `rawFormData` that is used to do the potentially
* recursive resolution.
*
* @param schema - The schema for which retrieving a schema is desired
* @param [rawFormData] - The current formData, if any, to assist retrieving a schema
* @returns - The schema having its conditions, additional properties, references and dependencies resolved
*/
retrieveSchema(schema: RJSFSchema, rawFormData: T) {
return retrieveSchema<T>(this.validator, schema, this.rootSchema, rawFormData);
}

/** Creates new 'properties' items for each key in the `formData`
*
* @param schema - The schema for which the existing additional properties is desired
* @param [formData] - The current formData, if any, to assist retrieving a schema
* @returns - The updated schema with additional properties stubbed
*/
stubExistingAdditionalProperties(schema: RJSFSchema, formData?: T) {
return stubExistingAdditionalProperties<T>(this.validator, schema, this.rootSchema, formData);
}

/** Generates an `IdSchema` object for the `schema`, recursively
*
* @param schema - The schema for which the display label flag is desired
* @param [id] - The base id for the schema
* @param [formData] - The current formData, if any, onto which to provide any missing defaults
* @param [idPrefix='root'] - The prefix to use for the id
* @param [idSeparator='_'] - The separator to use for the path segments in the id
* @returns - The `IdSchema` object for the `schema`
*/
toIdSchema(schema: RJSFSchema, id?: string | null, formData?: T, idPrefix = 'root', idSeparator = '_') {
return toIdSchema<T>(this.validator, schema, id, this.rootSchema, formData, idPrefix, idSeparator);
}

/** Generates an `PathSchema` object for the `schema`, recursively
*
* @param schema - The schema for which the display label flag is desired
* @param [name] - The base name for the schema
* @param [formData] - The current formData, if any, onto which to provide any missing defaults
* @returns - The `PathSchema` object for the `schema`
*/
toPathSchema(schema: RJSFSchema, name?: string, formData?: T) {
return toPathSchema<T>(this.validator, schema, name, this.rootSchema, formData);
}
}

/** Creates a `SchemaUtilsType` interface that is based around the given `validator` and `rootSchema` parameters. The
* resulting interface implementation will forward the `validator` and `rootSchema` to all the wrapped APIs.
*
* @param validator - an implementation of the `ValidatorType` interface that will be forwarded to all the APIs
* @param rootSchema - The root schema that will be forwarded to all the APIs
*/
export default function createSchemaUtils<T = any>(
validator: ValidatorType, rootSchema: RJSFSchema
): SchemaUtilsType<T> {
return new SchemaUtils<T>(validator, rootSchema);
}
27 changes: 21 additions & 6 deletions packages/utils/src/findSchemaDefinition.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import jsonpointer from 'jsonpointer';
import omit from 'lodash/omit';

import { REF_NAME } from './constants';
import { RJSFSchema } from './types';
import { REF_KEY } from './constants';
import { GenericObjectType, RJSFSchema } from './types';

/** Splits out the value at the `key` in `object` from the `object`, returning an array that contains in the first
* location, the `object` minus the `key: value` and in the second location the `value`.
*
* @param key - The key from the object to extract
* @param object - The object from which to extract the element
* @returns - An array with the first value being the object minus the `key` element and the second element being the
* value from `object[key]`
*/
export function splitKeyElementFromObject(key: string, object: GenericObjectType) {
const value = object[key];
const remaining = omit(object, [key]);
return [remaining, value];
}

/** Given the name of a `$ref` from within a schema, using the `rootSchema`, look up and return the sub-schema using the
* path provided by that reference. If `#` is not the first character of the reference, or the path does not exist in
Expand All @@ -24,10 +38,11 @@ export default function findSchemaDefinition($ref?: string, rootSchema: RJSFSche
if (current === undefined) {
throw new Error(`Could not find a definition for ${$ref}.`);
}
if (current[REF_NAME]) {
const subSchema = findSchemaDefinition(current[REF_NAME]!, rootSchema);
if (Object.keys(current).length > 1) {
return { ...omit(current, [REF_NAME]), ...subSchema };
if (current[REF_KEY]) {
const [remaining, theRef] = splitKeyElementFromObject(REF_KEY, current);
const subSchema = findSchemaDefinition(theRef, rootSchema);
if (Object.keys(remaining).length > 0) {
return { ...remaining, ...subSchema };
}
return subSchema;
}
Expand Down
6 changes: 3 additions & 3 deletions packages/utils/src/getSubmitButtonOptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SUBMIT_BTN_OPTIONS_NAME } from './constants';
import { SUBMIT_BTN_OPTIONS_KEY } from './constants';
import getUiOptions from './getUiOptions';
import { UiSchema, UISchemaSubmitButtonOptions } from './types';

Expand All @@ -19,8 +19,8 @@ export const DEFAULT_OPTIONS = {
*/
export default function getSubmitButtonOptions<T = any, F = any>(uiSchema: UiSchema<T, F>) {
const uiOptions = getUiOptions<T, F>(uiSchema);
if (uiOptions && uiOptions[SUBMIT_BTN_OPTIONS_NAME]) {
const options = uiOptions[SUBMIT_BTN_OPTIONS_NAME] as UISchemaSubmitButtonOptions;
if (uiOptions && uiOptions[SUBMIT_BTN_OPTIONS_KEY]) {
const options = uiOptions[SUBMIT_BTN_OPTIONS_KEY] as UISchemaSubmitButtonOptions;
return { ...DEFAULT_OPTIONS, ...options };
}

Expand Down
6 changes: 3 additions & 3 deletions packages/utils/src/getUiOptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UI_OPTIONS_NAME, UI_WIDGET_NAME } from './constants';
import { UI_OPTIONS_KEY, UI_WIDGET_KEY } from './constants';
import isObject from './isObject';
import { UIOptionsType, UiSchema } from './types';

Expand All @@ -13,11 +13,11 @@ export default function getUiOptions<T = any, F = any>(uiSchema: UiSchema<T, F>)
.filter(key => key.indexOf('ui:') === 0)
.reduce((options, key) => {
const value = uiSchema[key];
if (key === UI_WIDGET_NAME && isObject(value)) {
if (key === UI_WIDGET_KEY && isObject(value)) {
console.error('Setting options via ui:widget object is no longer supported, use ui:options instead');
return options;
}
if (key === UI_OPTIONS_NAME && isObject(value)) {
if (key === UI_OPTIONS_KEY && isObject(value)) {
return { ...options, ...value };
}
return { ...options, [key.substring(3)]: value };
Expand Down
83 changes: 4 additions & 79 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import allowAdditionalItems from './allowAdditionalItems';
import asNumber from './asNumber';
import canExpand from './canExpand';
import createSchemaUtils from './createSchemaUtils';
import dataURItoBlob from './dataURItoBlob';
import deepEquals from './deepEquals';
import findSchemaDefinition from './findSchemaDefinition';
Expand Down Expand Up @@ -29,92 +30,16 @@ import toConstant from './toConstant';
import toDateString from './toDateString';
import utcToLocal from './utcToLocal';

import {
ArrayFieldTemplateItemType,
ArrayFieldTemplateProps,
CustomValidator,
DateObject,
DescriptionFieldProps,
ErrorListProps,
ErrorSchema,
ErrorTransformer,
Field,
FieldError,
FieldErrors,
FieldId,
FieldPath,
FieldProps,
FieldTemplateProps,
FieldValidation,
FormValidation,
GenericObjectType,
IChangeEvent,
IdSchema,
ObjectFieldTemplatePropertyType,
ObjectFieldTemplateProps,
PathSchema,
RangeSpecType,
Registry,
RegistryFieldsType,
RegistryWidgetsType,
RJSFSchema,
RJSFValidationError,
TitleFieldProps,
UiSchema,
UIOptionsType,
UISchemaSubmitButtonOptions,
ValidationData,
ValidatorType,
Widget,
WidgetProps,
} from './types';

export type {
ArrayFieldTemplateItemType,
ArrayFieldTemplateProps,
CustomValidator,
DateObject,
DescriptionFieldProps,
ErrorListProps,
ErrorSchema,
ErrorTransformer,
Field,
FieldError,
FieldErrors,
FieldId,
FieldPath,
FieldProps,
FieldTemplateProps,
FieldValidation,
FormValidation,
GenericObjectType,
IChangeEvent,
IdSchema,
ObjectFieldTemplatePropertyType,
ObjectFieldTemplateProps,
PathSchema,
RangeSpecType,
Registry,
RegistryFieldsType,
RegistryWidgetsType,
RJSFSchema,
RJSFValidationError,
TitleFieldProps,
UiSchema,
UIOptionsType,
UISchemaSubmitButtonOptions,
ValidationData,
ValidatorType,
Widget,
WidgetProps,
};
export * from './types';

export * from './constants';
export * from './schema';

export {
allowAdditionalItems,
asNumber,
canExpand,
createSchemaUtils,
dataURItoBlob,
deepEquals,
findSchemaDefinition,
Expand Down
4 changes: 2 additions & 2 deletions packages/utils/src/isConstant.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CONST_NAME } from './constants';
import { CONST_KEY } from './constants';
import { RJSFSchema } from './types';

/**
Expand All @@ -9,5 +9,5 @@ import { RJSFSchema } from './types';
* @returns - True if the `schema` has a single constant value, false otherwise
*/
export default function isConstant(schema: RJSFSchema) {
return (Array.isArray(schema.enum) && schema.enum.length === 1) || schema.hasOwnProperty(CONST_NAME);
return (Array.isArray(schema.enum) && schema.enum.length === 1) || schema.hasOwnProperty(CONST_KEY);
}
Loading