Skip to content

Commit

Permalink
Merge pull request #4036 from open-formulieren/feature/4019-map-file-…
Browse files Browse the repository at this point in the history
…fields

[#4019] Mapping file uploads in Object API registration (frontend)
  • Loading branch information
sergei-maertens authored Mar 22, 2024
2 parents d6d682c + a5a4d5d commit 1d922e5
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]));
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -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']},
},
],
}),
],
},
},
Expand All @@ -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: [
Expand Down Expand Up @@ -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']},
},
],
}),
],
},
},
Expand Down

0 comments on commit 1d922e5

Please sign in to comment.