UNPKG

@croz/nrich-form-configuration-core

Version:

Contains core utilities related to the nrich-form-configuration module

1 lines 27.4 kB
{"version":3,"sources":["../../src/zod/store/form-configuration-store.ts","../../src/zod/hook/use-form-configuration.ts","../../src/zod/component/FormConfigurationZodProvider.tsx","../../src/zod/loader/fetch-form-configurations.ts","../../src/zod/converter/FormConfigurationValidationZodConverter.ts"],"sourcesContent":["/*\n * Copyright 2022 CROZ d.o.o, the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\nimport { create } from \"zustand\";\n\nimport { FormZodConfiguration } from \"../api\";\n\ninterface FormZodConfigurationState {\n\n /**\n * Array of current state form configurations.\n */\n zodFormConfigurations: FormZodConfiguration[];\n\n /**\n * Flag that indicates weather form configuration is fetched from API.\n */\n formConfigurationLoaded: boolean;\n\n /**\n * Sets form configurations to state.\n * Use on initial call to find-all endpoint.\n * @param formConfigurations formConfigurations to set\n */\n set: (zodFormConfigurations: FormZodConfiguration[]) => void;\n\n /**\n * Adds form configuration to state.\n * @param formConfiguration formConfiguration to add\n */\n add: (zodFormConfiguration: FormZodConfiguration) => void;\n\n /**\n * Removes form configuration to state.\n * @param formConfiguration formConfiguration to add\n */\n remove: (zodFormConfiguration: FormZodConfiguration) => void;\n\n /**\n * Sets form configuration loaded to state.\n * @param isLoaded loaded flag to set\n */\n setFormConfigurationLoaded: (formConfigurationLoaded: boolean) => void;\n}\n\n/**\n * Creation of the API for managing internal form configuration state.\n * Used internally in the {@link useFormConfiguration} hook.\n *\n * @returns A hook for managing form configuration state usable in a React environment.\n */\nexport const useZodFormConfigurationStore = create<FormZodConfigurationState>((set) => ({\n zodFormConfigurations: [],\n formConfigurationLoaded: false,\n set: ((zodFormConfigurations) => set((state) => ({\n ...state, zodFormConfigurations,\n }))),\n add: (zodFormConfigurations) => set((state) => ({\n zodFormConfigurations: [...state.zodFormConfigurations, zodFormConfigurations],\n })),\n remove: (zodFormConfigurations) => set((state) => ({\n zodFormConfigurations: state.zodFormConfigurations.filter((c) => c !== zodFormConfigurations),\n })),\n setFormConfigurationLoaded: (formConfigurationLoaded) => set({ formConfigurationLoaded }),\n}));\n","/*\n * Copyright 2022 CROZ d.o.o, the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\nimport { z } from \"zod\";\n\nimport { FormZodConfiguration } from \"../api\";\nimport { useZodFormConfigurationStore } from \"../store\";\n\nexport type ZodFormConfigurationOptions = {\n /**\n * Array of current state form configurations.\n */\n formZodConfigurations: FormZodConfiguration[],\n /**\n * Flag that indicates whether form configuration is fetched from API.\n */\n formConfigurationLoaded: boolean;\n /**\n * Sets form configurations to state.\n * Use on initial call to find-all endpoint.\n * @param formConfigurations formConfigurations to set\n */\n set: (formConfigurations: FormZodConfiguration[]) => void,\n\n /**\n * Adds form configuration to state.\n * @param formConfiguration formConfiguration to add\n */\n add: (formConfiguration: FormZodConfiguration) => void,\n /**\n * Removes form configuration to state.\n * @param formConfiguration formConfiguration to add\n */\n remove: (formConfiguration: FormZodConfiguration) => void\n /**\n * Sets form configuration loaded to state.\n * @param isLoaded loaded flag to set\n */\n setFormConfigurationLoaded: (formConfigurationLoaded: boolean) => void;\n};\n\nexport type UseZodFormConfiguration = () => ZodFormConfigurationOptions;\n\n/**\n * A hook which simplifies the usage of the form configuration state.\n * Uses the internal {@link useZodFormConfigurationStore} hook for managing the form configuration state.\n *\n * @returns An array of options to access and set the form configuration state and remove or add a single form configuration.\n */\nexport const useZodFormConfigurationState: UseZodFormConfiguration = () => {\n const formZodConfigurations = useZodFormConfigurationStore((state) => state.zodFormConfigurations);\n const formConfigurationLoaded = useZodFormConfigurationStore((state) => state.formConfigurationLoaded);\n const set = useZodFormConfigurationStore((state) => state.set);\n const add = useZodFormConfigurationStore((state) => state.add);\n const remove = useZodFormConfigurationStore((state) => state.remove);\n const setFormConfigurationLoaded = useZodFormConfigurationStore((state) => state.setFormConfigurationLoaded);\n\n return {\n formZodConfigurations, formConfigurationLoaded, set, add, remove, setFormConfigurationLoaded,\n };\n};\n\n/**\n * A hook which extracts a specific Zod configuration from the form configuration identified by the form id.\n * Uses the internal {@link useZodFormConfigurationStore} hook for managing the form configuration state.\n *\n * @param formId Registered form id for a specific form configuration.\n *\n * @returns Mapped Zod configuration from the form configuration identified by the form id, or undefined if no matching form configuration is found.\n */\nexport const useZodFormConfiguration = (formId: string) => {\n const { formZodConfigurations } = useZodFormConfigurationState();\n\n const searchedFormConfiguration = formZodConfigurations.find((formZodConfiguration) => formZodConfiguration.formId === formId);\n\n if (!searchedFormConfiguration) {\n return undefined;\n }\n\n return searchedFormConfiguration.zodSchema as z.ZodObject<any>;\n};\n","/*\n * Copyright 2022 CROZ d.o.o, the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\nimport React, { useEffect } from \"react\";\n\nimport { FormConfigurationConfiguration } from \"../../shared/api\";\nimport { useZodFormConfigurationState } from \"../hook\";\nimport { fetchZodFormConfigurations } from \"../loader\";\n\nexport type ZodProps = {\n\n /**\n * Content to show conditionally\n */\n children?: React.ReactNode;\n\n /**\n * Custom loader to show until content loads\n */\n loader?: React.ReactNode;\n} & FormConfigurationConfiguration;\n\n/**\n * Should be used to wrap the whole app that includes forms, so it doesn't render them without loading form configuration from API first.\n * @param children content to show conditionally\n * @param loader custom loader to show until content loads\n */\nexport const FormConfigurationZodProvider = ({ children, loader, ...fetchProps }: ZodProps) => {\n useEffect(() => {\n fetchZodFormConfigurations(fetchProps);\n }, []);\n\n const { formConfigurationLoaded } = useZodFormConfigurationState();\n\n return <div>{formConfigurationLoaded ? children : loader ?? null}</div>;\n};\n","/*\n * Copyright 2022 CROZ d.o.o, the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\nimport _uniqBy from \"lodash/uniqBy\";\n\nimport { FormConfiguration, FormConfigurationConfiguration } from \"../../shared/api\";\nimport { FormZodConfiguration } from \"../api\";\nimport { FormConfigurationValidationZodConverter } from \"../converter\";\nimport { useZodFormConfigurationStore } from \"../store\";\n\nconst mergeZodFormConfigurationsWithoutDuplicates = (oldFormConfiguration: FormZodConfiguration[], newFormConfiguration: FormZodConfiguration[]) => {\n const mergedZodFormConfigurations = [...oldFormConfiguration, ...newFormConfiguration];\n\n return _uniqBy(mergedZodFormConfigurations, \"formId\");\n};\n\nexport const fetchZodFormConfigurations = async ({ url, requestOptionsResolver, additionalValidatorConverters }: FormConfigurationConfiguration): Promise<FormConfiguration[]> => {\n const formConfigurationValidationConverter = new FormConfigurationValidationZodConverter(additionalValidatorConverters);\n const additionalOptions = requestOptionsResolver?.() || {};\n const finalUrl = url || \"/nrich/form/configuration\";\n\n const response = await fetch(`${finalUrl}/fetch-all`, {\n method: \"POST\",\n ...additionalOptions,\n });\n const body = await response.json() as FormConfiguration[];\n\n const zodFormConfigurations: FormZodConfiguration[] = [];\n\n // set the response to the form configuration store\n if (response.ok) {\n body.forEach((item) => {\n zodFormConfigurations.push({\n formId: item.formId,\n zodSchema: formConfigurationValidationConverter.convertFormConfigurationToZodSchema(item),\n });\n });\n useZodFormConfigurationStore.getState().set(mergeZodFormConfigurationsWithoutDuplicates(useZodFormConfigurationStore.getState().zodFormConfigurations, zodFormConfigurations));\n useZodFormConfigurationStore.getState().setFormConfigurationLoaded(true);\n }\n\n return body;\n};\n","/*\n * Copyright 2022 CROZ d.o.o, the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\nimport { z } from \"zod\";\n\nimport {\n ConstrainedPropertyClientValidatorConfiguration,\n ConstrainedPropertyConfiguration,\n FormConfiguration,\n ValidatorConverter,\n} from \"../../shared/api\";\n\n/**\n * Converter responsible for conversion between ConstrainedPropertyConfiguration array (that contains validations specified and received from the backend)\n * and Zod's schema that can be applied on the frontend. The list of supported conversions is in given in {@link FormConfigurationValidationZodConverter.DEFAULT_CONVERTER_LIST} but\n * users can also provide their own by using {@link ValidatorConverter} interface and supplying them in the constructor.\n * The last validator converter will match any backend constraint and try to map it directly to Zod.\n */\nexport class FormConfigurationValidationZodConverter {\n /**\n * Additional converters that can be registered for unsupported conversion or to change one of the existing conversions (they take precedence to builtin converters).\n * @private\n */\n private readonly additionalConverters: ValidatorConverter[];\n\n constructor(additionalConverters: ValidatorConverter[] = []) {\n this.additionalConverters = additionalConverters;\n }\n\n /**\n * Converts {@link FormConfiguration} to Zod's schema using builtin and provided converters.\n * @param formConfig Form configuration to convert\n */\n public convertFormConfigurationToZodSchema(formConfig: FormConfiguration): z.ZodObject<any> {\n const shape: Record<string, z.ZodTypeAny> = {};\n\n formConfig.constrainedPropertyConfigurationList.forEach(\n (property: ConstrainedPropertyConfiguration) => {\n let fieldSchema = FormConfigurationValidationZodConverter.getBaseSchema(property);\n\n let isRequired = false;\n let lastRequiredValidator: ConstrainedPropertyClientValidatorConfiguration | undefined;\n\n property.validatorList.forEach(\n (validator: ConstrainedPropertyClientValidatorConfiguration) => {\n if ([\"NotNull\", \"NotBlank\", \"NotEmpty\"].includes(validator.name)) {\n isRequired = true;\n lastRequiredValidator = validator;\n }\n fieldSchema = this.applyConverter(validator, fieldSchema);\n },\n );\n\n if (!isRequired) {\n fieldSchema = fieldSchema.nullish();\n }\n else {\n fieldSchema = fieldSchema\n .nullable()\n .or(z.undefined())\n .refine(\n (val: any) => val != null,\n { message: lastRequiredValidator!.errorMessage },\n );\n }\n\n FormConfigurationValidationZodConverter.setNestedField(shape, property.path.split(\".\"), fieldSchema);\n },\n );\n\n return FormConfigurationValidationZodConverter.buildZodObject(shape);\n }\n\n /**\n * Determines the base Zod schema for a constrained property based on\n * its declared JavaScript type.\n */\n private static getBaseSchema(property: ConstrainedPropertyConfiguration): z.ZodTypeAny {\n switch (property.javascriptType.toLowerCase()) {\n case \"string\":\n return z.string();\n case \"number\":\n return z.number();\n case \"boolean\":\n return z.boolean();\n case \"object\":\n return z.record(z.string(), z.any());\n case \"array\":\n return z.array(z.any());\n case \"date\":\n return z.date();\n default:\n return z.unknown();\n }\n }\n\n /**\n * Assigns a Zod schema to a nested path inside a JS object that represents the shape of the final Zod object.\n */\n private static setNestedField(\n shape: Record<string, any>,\n path: string[],\n schema: z.ZodTypeAny,\n ) {\n let current = shape;\n\n for (let i = 0; i < path.length - 1; i += 1) {\n const key = path[i];\n\n if (!(key in current)) {\n current[key] = {};\n }\n\n current = current[key];\n }\n\n current[path[path.length - 1]] = schema;\n }\n\n /**\n * Recursively converts a nested shape object into a real ZodObject.\n */\n private static buildZodObject(\n shape: Record<string, any>,\n ): z.ZodObject<any> {\n const zodShape: Record<string, z.ZodTypeAny> = {};\n\n Object.entries(shape).forEach(([key, value]) => {\n if (value instanceof z.ZodType) {\n zodShape[key] = value;\n }\n else {\n zodShape[key] = FormConfigurationValidationZodConverter.buildZodObject(value);\n }\n });\n\n return z.object(zodShape);\n }\n\n private applyConverter(\n config: ConstrainedPropertyClientValidatorConfiguration,\n schema: z.ZodTypeAny,\n ): z.ZodTypeAny {\n const converter = this.resolveConverter(config);\n if (converter) {\n try {\n return converter.convert(config, schema) as z.ZodTypeAny;\n }\n catch {\n // constraint is not registered so skip evaluation\n }\n }\n return schema;\n }\n\n private resolveConverter(\n config: ConstrainedPropertyClientValidatorConfiguration,\n ): ValidatorConverter | undefined {\n const allConverters = this.additionalConverters.concat(FormConfigurationValidationZodConverter.DEFAULT_CONVERTERS_LIST);\n return allConverters.find((additionalConverter) => additionalConverter.supports(config));\n }\n\n private static readonly DEFAULT_CONVERTERS_LIST: ValidatorConverter[] = [\n {\n supports: (configuration) => [\"NotNull\", \"NotBlank\", \"NotEmpty\"].includes(configuration.name),\n convert: (configuration, schema: any) => {\n if (configuration.name === \"NotBlank\" && schema instanceof z.ZodString) {\n return schema.refine(\n (val: any) => val == null || (typeof val === \"string\" && val.trim().length > 0),\n { message: configuration.errorMessage },\n );\n }\n if (configuration.name === \"NotEmpty\") {\n if (schema instanceof z.ZodString) {\n return schema.refine(\n (val: any) => val == null || (typeof val === \"string\" && val.length > 0),\n { message: configuration.errorMessage },\n );\n }\n if (schema instanceof z.ZodArray) {\n return schema.refine(\n (val: any) => val == null || val.length > 0,\n { message: configuration.errorMessage },\n );\n }\n }\n // NotNull - no additional check needed, handled at the end\n return schema;\n },\n },\n {\n supports: (configuration) => [\"Size\", \"Length\"].includes(configuration.name),\n convert: (configuration, schema: any) => {\n if (schema instanceof z.ZodString || schema instanceof z.ZodArray) {\n let s = schema;\n if (configuration.argumentMap.min != null) s = s.min(configuration.argumentMap.min as number, { message: configuration.errorMessage });\n if (configuration.argumentMap.max != null) s = s.max(configuration.argumentMap.max as number, { message: configuration.errorMessage });\n return s;\n }\n return schema;\n },\n },\n {\n supports: (configuration) => configuration.name === \"Pattern\",\n convert: (configuration, schema: any) => {\n if (!(schema instanceof z.ZodString)) {\n return schema;\n }\n\n const patternStr = configuration.argumentMap.pattern;\n if (typeof patternStr !== \"string\" || patternStr.trim() === \"\") {\n return schema;\n }\n\n let regex: RegExp;\n try {\n regex = new RegExp(patternStr);\n }\n catch {\n return schema;\n }\n\n return schema.regex(regex, { message: configuration.errorMessage });\n },\n },\n {\n supports: (configuration) => [\"Min\", \"Max\"].includes(configuration.name),\n convert: (configuration, schema: any) => {\n if (!(schema instanceof z.ZodNumber) || configuration.argumentMap.value == null) return schema;\n const value = configuration.argumentMap.value as number;\n return configuration.name === \"Min\"\n ? schema.min(value, { message: configuration.errorMessage })\n : schema.max(value, { message: configuration.errorMessage });\n },\n },\n {\n supports: (configuration) => configuration.name === \"InList\",\n convert: (config, schema: any) => {\n const values = config.argumentMap.value;\n if (Array.isArray(values)) {\n return schema.refine(\n (val: any) => values.includes(val),\n { message: config.errorMessage },\n );\n }\n return schema;\n },\n },\n {\n supports: (configuration) => configuration.name === \"Email\",\n convert: (configuration, schema: any) => {\n if (!(schema instanceof z.ZodString)) {\n return schema;\n }\n return z.email(configuration.errorMessage);\n },\n },\n {\n supports: () => true,\n convert: (_, schema: any) => schema,\n },\n ];\n}\n"],"mappings":"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","names":["_uniqBy"]}