From 676c260d28df95d31b3b9052331787b71ca809a2 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Fri, 8 Mar 2024 16:22:21 +0100 Subject: [PATCH] [#3688] Handle mutations/deletions of variables --- .../admin/form_design/form-creation-form.js | 30 +++++++++++++++ .../admin/form_design/registrations/index.js | 8 +++- .../{handlers.js => stepEditHandlers.js} | 34 ++++++++++++++++- .../userDefinedVariableEditHandlers.js | 38 +++++++++++++++++++ 4 files changed, 107 insertions(+), 3 deletions(-) rename src/openforms/js/components/admin/form_design/registrations/{handlers.js => stepEditHandlers.js} (69%) create mode 100644 src/openforms/js/components/admin/form_design/registrations/userDefinedVariableEditHandlers.js diff --git a/src/openforms/js/components/admin/form_design/form-creation-form.js b/src/openforms/js/components/admin/form_design/form-creation-form.js index 66c9fb83b2..a92ab85ccb 100644 --- a/src/openforms/js/components/admin/form_design/form-creation-form.js +++ b/src/openforms/js/components/admin/form_design/form-creation-form.js @@ -1,4 +1,5 @@ import {produce} from 'immer'; +import cloneDeep from 'lodash/cloneDeep'; import getObjectValue from 'lodash/get'; import groupBy from 'lodash/groupBy'; import set from 'lodash/set'; @@ -746,12 +747,29 @@ function reducer(draft, action) { } case 'DELETE_USER_DEFINED_VARIABLE': { const key = action.payload; + const deletedVariable = draft.formVariables.find(variable => variable.key === key); + + // Update registration backends that might depend on the variable: + draft.form.registrationBackends = draft.form.registrationBackends.map(configuredBackend => { + const {backend: registrationBackend, options} = configuredBackend; + + const handler = BACKEND_OPTIONS_FORMS[registrationBackend]?.onUserDefinedVariableEdit; + if (handler == null) return configuredBackend; + const updatedOptions = handler(options, deletedVariable, null); + if (!updatedOptions) return configuredBackend; + + return {...configuredBackend, options: updatedOptions}; + }); + draft.formVariables = draft.formVariables.filter(variable => variable.key !== key); break; } case 'CHANGE_USER_DEFINED_VARIABLE': { const {key, propertyName, propertyValue} = action.payload; + const originalVariable = cloneDeep( + draft.formVariables.find(variable => variable.key === key) + ); const index = draft.formVariables.findIndex(variable => variable.key === key); draft.formVariables[index][propertyName] = propertyValue; @@ -781,6 +799,18 @@ function reducer(draft, action) { draft.logicRules = updateKeyReferencesInLogic(draft.logicRules, key, propertyValue); } } + + // Update registration backends that might depend on the variable: + draft.form.registrationBackends = draft.form.registrationBackends.map(configuredBackend => { + const {backend: registrationBackend, options} = configuredBackend; + + const handler = BACKEND_OPTIONS_FORMS[registrationBackend]?.onUserDefinedVariableEdit; + if (handler == null) return configuredBackend; + const updatedOptions = handler(options, draft.formVariables[index], originalVariable); + if (!updatedOptions) return configuredBackend; + + return {...configuredBackend, options: updatedOptions}; + }); break; } diff --git a/src/openforms/js/components/admin/form_design/registrations/index.js b/src/openforms/js/components/admin/form_design/registrations/index.js index c362afd2df..5398921884 100644 --- a/src/openforms/js/components/admin/form_design/registrations/index.js +++ b/src/openforms/js/components/admin/form_design/registrations/index.js @@ -1,16 +1,19 @@ import {WysiwygWidget} from 'components/admin/RJSFWrapper'; import CamundaOptionsForm from './camunda'; -import {onCamundaStepEdit, onZGWStepEdit} from './handlers'; import ObjectsApiOptionsForm from './objectsapi/ObjectsApiOptionsForm'; import ObjectsApiSummaryHandler from './objectsapi/ObjectsApiSummaryHandler'; import ObjectsApiVariableConfigurationEditor from './objectsapi/ObjectsApiVariableConfigurationEditor'; +import {onCamundaStepEdit, onObjectsAPIStepEdit, onZGWStepEdit} from './stepEditHandlers'; +import {onObjectsAPIUserDefinedVariableEdit} from './userDefinedVariableEditHandlers'; import ZGWOptionsForm from './zgw'; /** * @typedef {{ * form?: React.FC, * uiSchema?: Object, + * onStepEdit?: (...args: any) => Object | null, + * onUserDefinedVariableEdit?: (...args: any) => Object | null, * configurableFromVariables?: boolean | (options: Object) => boolean, * summaryHandler?: React.FC * variableConfigurationEditor?: React.FC @@ -25,7 +28,8 @@ export const BACKEND_OPTIONS_FORMS = { }, objects_api: { form: ObjectsApiOptionsForm, - onStepEdit: null, + onStepEdit: onObjectsAPIStepEdit, + onUserDefinedVariableEdit: onObjectsAPIUserDefinedVariableEdit, configurableFromVariables: options => options.version === 2, summaryHandler: ObjectsApiSummaryHandler, variableConfigurationEditor: ObjectsApiVariableConfigurationEditor, diff --git a/src/openforms/js/components/admin/form_design/registrations/handlers.js b/src/openforms/js/components/admin/form_design/registrations/stepEditHandlers.js similarity index 69% rename from src/openforms/js/components/admin/form_design/registrations/handlers.js rename to src/openforms/js/components/admin/form_design/registrations/stepEditHandlers.js index 2884678b1a..d58b2eb344 100644 --- a/src/openforms/js/components/admin/form_design/registrations/handlers.js +++ b/src/openforms/js/components/admin/form_design/registrations/stepEditHandlers.js @@ -69,4 +69,36 @@ const onZGWStepEdit = (registrationBackendOptions, componentSchema, originalComp return registrationBackendOptions; }; -export {onCamundaStepEdit, onZGWStepEdit}; +/** + * Update the mapped properties after the form definitions change + * @param {Object} registrationBackendOptions The currently configured backend options, + * including the mapped properties. Note that this is + * an immer draft which can be mutated. + * @param {Object} componentSchema The Formio component (schema) that was mutated in some way + * @param {Object|null} originalComponent The component before it was mutated, null if the component is removed. + * @return {Object|null} The updated registrationBackendOptions draft. Return null to indicate + * no changes need to be made. + */ +const onObjectsAPIStepEdit = (registrationBackendOptions, componentSchema, originalComponent) => { + const removed = originalComponent == null; + + if (removed) { + const matchingMappingIndex = registrationBackendOptions.variablesMapping.findIndex( + mapping => mapping.variableKey === componentSchema.key + ); + + if (matchingMappingIndex === -1) return; + registrationBackendOptions.variablesMapping.splice(matchingMappingIndex, 1); + } else { + const keyChanged = componentSchema.key !== originalComponent.key; + if (!keyChanged) return null; + + for (const mapping of registrationBackendOptions.variablesMapping) { + if (mapping.variableKey === originalComponent.key) { + mapping.variableKey = componentSchema.key; + } + } + } +}; + +export {onCamundaStepEdit, onZGWStepEdit, onObjectsAPIStepEdit}; diff --git a/src/openforms/js/components/admin/form_design/registrations/userDefinedVariableEditHandlers.js b/src/openforms/js/components/admin/form_design/registrations/userDefinedVariableEditHandlers.js new file mode 100644 index 0000000000..6257d85e69 --- /dev/null +++ b/src/openforms/js/components/admin/form_design/registrations/userDefinedVariableEditHandlers.js @@ -0,0 +1,38 @@ +/** + * Update the mapped properties after the user defined variable change + * @param {Object} registrationBackendOptions The currently configured backend options, + * including the mapped properties. Note that this is + * an immer draft which can be mutated. + * @param {Object} variable The user defined variable that was mutated in some way + * @param {Object|null} originalVariable The user defined variable before it was mutated, null if it is removed. + * @return {Object|null} The updated registrationBackendOptions draft. Return null to indicate + * no changes need to be made. + */ +const onObjectsAPIUserDefinedVariableEdit = ( + registrationBackendOptions, + variable, + originalVariable +) => { + // If the data type has changed, the mapped target might not be compatible anymore: + const shouldRemove = originalVariable == null || variable.dataType !== originalVariable.dataType; + + if (shouldRemove) { + const matchingMappingIndex = registrationBackendOptions.variablesMapping.findIndex( + mapping => mapping.variableKey === variable.key + ); + + if (matchingMappingIndex === -1) return; + registrationBackendOptions.variablesMapping.splice(matchingMappingIndex, 1); + } else { + const keyChanged = variable.key !== originalVariable.key; + if (!keyChanged) return null; + + for (const mapping of registrationBackendOptions.variablesMapping) { + if (mapping.variableKey === originalVariable.key) { + mapping.variableKey = variable.key; + } + } + } +}; + +export {onObjectsAPIUserDefinedVariableEdit};