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

[#4019] Mapping file uploads in Object API registration (frontend) #4036

Merged
merged 2 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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({
sergei-maertens marked this conversation as resolved.
Show resolved Hide resolved
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
Loading