diff --git a/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiVariableConfigurationEditor.js b/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiVariableConfigurationEditor.js index 069a718f21..629027d90c 100644 --- a/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiVariableConfigurationEditor.js +++ b/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiVariableConfigurationEditor.js @@ -5,7 +5,7 @@ import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; import {useAsync, useToggle} from 'react-use'; -import {APIContext} from 'components/admin/form_design/Context'; +import {APIContext, FormContext} from 'components/admin/form_design/Context'; import {REGISTRATION_OBJECTS_TARGET_PATHS} from 'components/admin/form_design/constants'; import Field from 'components/admin/forms/Field'; import Fieldset from 'components/admin/forms/Fieldset'; @@ -34,7 +34,7 @@ import {asJsonSchema} from './utils'; */ const ObjectsApiVariableConfigurationEditor = ({variable}) => { const {csrftoken} = useContext(APIContext); - + const {components} = useContext(FormContext); const [jsonSchemaVisible, toggleJsonSchemaVisible] = useToggle(false); const {values: backendOptions, getFieldProps, setFieldValue} = useFormikContext(); @@ -73,7 +73,7 @@ const ObjectsApiVariableConfigurationEditor = ({variable}) => { const response = await post(REGISTRATION_OBJECTS_TARGET_PATHS, csrftoken, { objecttypeUrl: objecttype, objecttypeVersion, - variableJsonSchema: asJsonSchema(variable), + variableJsonSchema: asJsonSchema(variable, components), }); return response.data; diff --git a/src/openforms/js/components/admin/form_design/registrations/objectsapi/mocks.js b/src/openforms/js/components/admin/form_design/registrations/objectsapi/mocks.js index 603f8690cd..b0c1cc80ce 100644 --- a/src/openforms/js/components/admin/form_design/registrations/objectsapi/mocks.js +++ b/src/openforms/js/components/admin/form_design/registrations/objectsapi/mocks.js @@ -21,6 +21,12 @@ export const mockObjecttypesError = () => }); export const mockTargetPathsPost = paths => - rest.post(`${BASE_URL}/api/v2/registration/plugins/objects-api/target-paths`, (req, res, ctx) => { - return res(ctx.json(paths)); - }); + rest.post( + `${BASE_URL}/api/v2/registration/plugins/objects-api/target-paths`, + async (req, res, ctx) => { + const requestBody = await req.json(); + const variableJsonSchemaType = requestBody.variableJsonSchema.type; + + return res(ctx.json(paths[variableJsonSchemaType])); + } + ); diff --git a/src/openforms/js/components/admin/form_design/registrations/objectsapi/utils.js b/src/openforms/js/components/admin/form_design/registrations/objectsapi/utils.js index ed23097a14..1271f52538 100644 --- a/src/openforms/js/components/admin/form_design/registrations/objectsapi/utils.js +++ b/src/openforms/js/components/admin/form_design/registrations/objectsapi/utils.js @@ -48,9 +48,21 @@ const FORMAT_TYPE_MAP = { /** * Return a JSON Schema definition matching the provided variable. * @param {Object} variable - The current variable + * @param {Object} components - The components available in the form. The key is the component key, the value is the + * component definition. * @returns {Object} - The JSON Schema */ -const asJsonSchema = variable => { +const asJsonSchema = (variable, components) => { + // Figure out if the component is a file component (special case) + const componentDefinition = components[variable.key]; + if (componentDefinition && componentDefinition.type === 'file') { + // If it is, and it has multiple == True, then type is array + if (componentDefinition.multiple) + return {type: 'array', items: {type: 'string', format: 'uri'}}; + // Otherwise it's string (URL of the document) + return {type: 'string', format: 'uri'}; + } + if (VARIABLE_TYPE_MAP.hasOwnProperty(variable.dataType)) return {type: VARIABLE_TYPE_MAP[variable.dataType]}; return { diff --git a/src/openforms/js/components/admin/form_design/variables/VariablesEditor.stories.js b/src/openforms/js/components/admin/form_design/variables/VariablesEditor.stories.js index 8edc70c5f4..3da2e49126 100644 --- a/src/openforms/js/components/admin/form_design/variables/VariablesEditor.stories.js +++ b/src/openforms/js/components/admin/form_design/variables/VariablesEditor.stories.js @@ -1,5 +1,6 @@ import {expect} from '@storybook/jest'; -import {userEvent, within} from '@storybook/testing-library'; +import {userEvent, waitFor, within} from '@storybook/testing-library'; +import {screen} from '@storybook/testing-library'; import {BACKEND_OPTIONS_FORMS} from 'components/admin/form_design/registrations'; import {mockTargetPathsPost} from 'components/admin/form_design/registrations/objectsapi/mocks'; @@ -34,6 +35,36 @@ export default { serviceFetchConfiguration: undefined, initialValue: '', }, + { + form: 'http://localhost:8000/api/v2/forms/36612390', + formDefinition: 'http://localhost:8000/api/v2/form-definitions/6de1ea5a', + name: 'Single File', + key: 'aSingleFile', + source: 'component', + prefillPlugin: '', + prefillAttribute: '', + prefillIdentifierRole: 'main', + dataType: 'array', + dataFormat: undefined, + isSensitiveData: false, + serviceFetchConfiguration: undefined, + initialValue: [], + }, + { + form: 'http://localhost:8000/api/v2/forms/36612390', + formDefinition: 'http://localhost:8000/api/v2/form-definitions/6de1ea5a', + name: 'Multiple File', + key: 'aMultipleFile', + source: 'component', + prefillPlugin: '', + prefillAttribute: '', + prefillIdentifierRole: 'main', + dataType: 'array', + dataFormat: undefined, + isSensitiveData: false, + serviceFetchConfiguration: undefined, + initialValue: [], + }, { form: 'http://localhost:8000/api/v2/forms/36612390', formDefinition: undefined, @@ -67,6 +98,22 @@ export default { initialValue: '2024-02-27T16:44:22.170405Z', }, ], + availableComponents: { + aSingleFile: { + type: 'file', + multiple: false, + key: 'aSingleFile', + }, + aMultipleFile: { + type: 'file', + multiple: true, + key: 'aMultipleFile', + }, + formioComponent: { + key: 'formioComponent', + type: 'textfield', + }, + }, }, argTypes: { onChange: {action: true}, @@ -181,18 +228,22 @@ export const WithObjectsAPIRegistrationBackends = { parameters: { msw: { handlers: [ - mockTargetPathsPost([ - { - targetPath: ['path', 'to.the', 'target'], - isRequired: true, - jsonSchema: {type: 'string'}, - }, - { - targetPath: ['other', 'path'], - isRequired: false, - jsonSchema: {type: 'object', properties: {a: {type: 'string'}}, required: ['a']}, - }, - ]), + mockTargetPathsPost({ + string: [ + { + targetPath: ['path', 'to.the', 'target'], + isRequired: true, + jsonSchema: {type: 'string'}, + }, + ], + object: [ + { + targetPath: ['other', 'path'], + isRequired: false, + jsonSchema: {type: 'object', properties: {a: {type: 'string'}}, required: ['a']}, + }, + ], + }), ], }, }, @@ -211,6 +262,113 @@ export const WithObjectsAPIRegistrationBackends = { }, }; +export const FilesMappingAndObjectAPIRegistration = { + args: { + registrationBackends: [ + { + backend: 'objects_api', + key: 'objects_api_1', + name: 'Example Objects API reg.', + options: { + version: 2, + objecttype: + 'https://objecttypen.nl/api/v1/objecttypes/2c77babf-a967-4057-9969-0200320d23f1', + objecttypeVersion: 2, + variablesMapping: [ + { + variableKey: 'formioComponent', + targetPath: ['path', 'to.the', 'target'], + }, + { + variableKey: 'userDefined', + targetPath: ['other', 'path'], + }, + ], + }, + }, + ], + registrationPluginsVariables: [ + { + pluginIdentifier: 'objects_api', + pluginVerboseName: 'Objects API registration', + }, + ], + onFieldChange: data => { + console.log(data); + }, + }, + parameters: { + msw: { + handlers: [ + mockTargetPathsPost({ + string: [ + { + targetPath: ['path', 'to.the', 'target'], + isRequired: true, + jsonSchema: {type: 'string'}, + }, + { + targetPath: ['path', 'to', 'uri'], + isRequired: true, + jsonSchema: { + type: 'string', + format: 'uri', + }, + }, + ], + object: [ + { + targetPath: ['other', 'path'], + isRequired: false, + jsonSchema: {type: 'object', properties: {a: {type: 'string'}}, required: ['a']}, + }, + ], + array: [ + { + targetPath: ['path', 'to', 'array'], + isRequired: true, + jsonSchema: {type: 'array'}, + }, + ], + }), + ], + }, + }, + play: async ({canvasElement}) => { + const canvas = within(canvasElement); + + const editIcons = canvas.getAllByTitle('Registratie-instellingen bewerken'); + + // The second icon is for the single file upload component variable + userEvent.click(editIcons[1]); + + const targetSchemaDropdown = await screen.findByRole('combobox'); + + await expect(targetSchemaDropdown).toBeInTheDocument(); + + // Only the targets of type string should appear + await expect( + await screen.findByRole('option', {name: 'path > to.the > target (required)'}) + ).toBeVisible(); + await expect( + await screen.findByRole('option', {name: 'path > to > uri (required)'}) + ).toBeVisible(); + + const saveButton = screen.getByRole('button', {name: 'Opslaan'}); + userEvent.click(saveButton); + + // The third icon is for the multiple file upload component variable + userEvent.click(editIcons[2]); + + const dropdown = await screen.findByRole('combobox'); + + await expect(dropdown).toBeInTheDocument(); + await expect( + await screen.findByRole('option', {name: 'path > to > array (required)'}) + ).toBeVisible(); + }, +}; + export const WithObjectsAPIAndTestRegistrationBackends = { args: { registrationBackends: [ @@ -293,18 +451,22 @@ export const WithObjectsAPIAndTestRegistrationBackends = { parameters: { msw: { handlers: [ - mockTargetPathsPost([ - { - targetPath: ['path', 'to.the', 'target'], - isRequired: true, - jsonSchema: {type: 'string'}, - }, - { - targetPath: ['other', 'path'], - isRequired: false, - jsonSchema: {type: 'object', properties: {a: {type: 'string'}}, required: ['a']}, - }, - ]), + mockTargetPathsPost({ + string: [ + { + targetPath: ['path', 'to.the', 'target'], + isRequired: true, + jsonSchema: {type: 'string'}, + }, + ], + object: [ + { + targetPath: ['other', 'path'], + isRequired: false, + jsonSchema: {type: 'object', properties: {a: {type: 'string'}}, required: ['a']}, + }, + ], + }), ], }, },