Skip to content

Commit 6d72916

Browse files
committed
[#5074] Transform selectboxes data to list for Objects API
1 parent a517a16 commit 6d72916

File tree

8 files changed

+103
-42
lines changed

8 files changed

+103
-42
lines changed

src/openforms/js/components/admin/form_design/registrations/objectsapi/AddressNlObjectsApiVariableConfigurationEditor.js

+1-23
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@ import {FormattedMessage} from 'react-intl';
55
import {useAsync, useToggle} from 'react-use';
66

77
import {APIContext} from 'components/admin/form_design/Context';
8-
import {REGISTRATION_OBJECTS_TARGET_PATHS} from 'components/admin/form_design/constants';
98
import Field from 'components/admin/forms/Field';
109
import Fieldset from 'components/admin/forms/Fieldset';
1110
import FormRow from 'components/admin/forms/FormRow';
1211
import {Checkbox} from 'components/admin/forms/Inputs';
1312
import {TargetPathSelect} from 'components/admin/forms/objects_api';
1413
import ErrorMessage from 'components/errors/ErrorMessage';
15-
import {post} from 'utils/fetch';
1614

1715
import {MappedVariableTargetPathSelect} from './GenericObjectsApiVariableConfigurationEditor';
16+
import {fetchTargetPaths} from './utils';
1817

1918
const ADDRESSNL_NESTED_PROPERTIES = {
2019
postcode: {type: 'string'},
@@ -25,27 +24,6 @@ const ADDRESSNL_NESTED_PROPERTIES = {
2524
streetName: {type: 'string'},
2625
};
2726

28-
const fetchTargetPaths = async (
29-
csrftoken,
30-
objectsApiGroup,
31-
objecttype,
32-
objecttypeVersion,
33-
schemaType
34-
) => {
35-
const response = await post(REGISTRATION_OBJECTS_TARGET_PATHS, csrftoken, {
36-
objectsApiGroup,
37-
objecttype,
38-
objecttypeVersion,
39-
variableJsonSchema: schemaType,
40-
});
41-
42-
if (!response.ok) {
43-
throw new Error(`Error when loading target paths for type: ${schemaType}`);
44-
}
45-
46-
return response.data;
47-
};
48-
4927
export const AddressNlEditor = ({
5028
variable,
5129
components,

src/openforms/js/components/admin/form_design/registrations/objectsapi/GenericObjectsApiVariableConfigurationEditor.js

+54-18
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ import {FormattedMessage} from 'react-intl';
66
import {useAsync, useToggle} from 'react-use';
77

88
import {APIContext} from 'components/admin/form_design/Context';
9-
import {REGISTRATION_OBJECTS_TARGET_PATHS} from 'components/admin/form_design/constants';
109
import Field from 'components/admin/forms/Field';
1110
import FormRow from 'components/admin/forms/FormRow';
1211
import {Checkbox} from 'components/admin/forms/Inputs';
1312
import {TargetPathSelect} from 'components/admin/forms/objects_api';
1413
import ErrorMessage from 'components/errors/ErrorMessage';
15-
import {post} from 'utils/fetch';
1614

15+
import {CUSTOM_COMPONENTS_SCHEMA} from '../utils';
1716
import {asJsonSchema} from './utils';
17+
import {fetchTargetPaths} from './utils';
1818

1919
/**
2020
* Hack-ish way to manage the variablesMapping state for one particular entry.
@@ -83,6 +83,7 @@ export const GenericEditor = ({
8383
components,
8484
namePrefix,
8585
isGeometry,
86+
transformToList,
8687
index,
8788
mappedVariable,
8889
objecttype,
@@ -93,27 +94,32 @@ export const GenericEditor = ({
9394
const [jsonSchemaVisible, toggleJsonSchemaVisible] = useToggle(false);
9495
const {setFieldValue} = useFormikContext();
9596

97+
const componentType = components[variable?.key]?.type;
98+
99+
// Load all the possible target paths in parallel depending on if the data should be
100+
// transformed or not
96101
const {
97102
loading,
98103
value: targetPaths,
99104
error,
100-
} = useAsync(
101-
async () => {
102-
const response = await post(REGISTRATION_OBJECTS_TARGET_PATHS, csrftoken, {
103-
objectsApiGroup,
104-
objecttype,
105-
objecttypeVersion,
106-
variableJsonSchema: asJsonSchema(variable, components),
107-
});
108-
if (!response.ok) {
109-
throw new Error('Error when loading target paths');
110-
}
105+
} = useAsync(async () => {
106+
const promises = transformToList?.[variable.key]
107+
? CUSTOM_COMPONENTS_SCHEMA[componentType].map(type =>
108+
fetchTargetPaths(csrftoken, objectsApiGroup, objecttype, objecttypeVersion, type)
109+
)
110+
: [
111+
fetchTargetPaths(
112+
csrftoken,
113+
objectsApiGroup,
114+
objecttype,
115+
objecttypeVersion,
116+
asJsonSchema(variable, components)
117+
),
118+
];
111119

112-
return response.data;
113-
},
114-
// Load only once:
115-
[]
116-
);
120+
const results = await Promise.all(promises);
121+
return results.flat(1);
122+
}, [transformToList?.[variable.key]]);
117123

118124
const getTargetPath = pathSegment => targetPaths.find(t => isEqual(t.targetPath, pathSegment));
119125

@@ -153,6 +159,36 @@ export const GenericEditor = ({
153159
/>
154160
</Field>
155161
</FormRow>
162+
{componentType in CUSTOM_COMPONENTS_SCHEMA && (
163+
<FormRow>
164+
<Field name={`transformToList.${variable.key}`}>
165+
<Checkbox
166+
name="transformToListCheckbox"
167+
label={
168+
<FormattedMessage
169+
defaultMessage="Transform to list"
170+
description="'Transform to list' checkbox label"
171+
/>
172+
}
173+
helpText={
174+
<FormattedMessage
175+
description="'Transform to list' checkbox help text"
176+
defaultMessage="Whether to transform the data of the component to a list or not (depends on the component type)"
177+
/>
178+
}
179+
checked={transformToList.includes(variable.key) || false}
180+
onChange={event => {
181+
const shouldBeTransformed = event.target.checked;
182+
const newTransformToList = shouldBeTransformed
183+
? [...transformToList, variable.key]
184+
: transformToList.filter(key => key !== variable.key);
185+
setFieldValue('transformToList', newTransformToList);
186+
setFieldValue(`transformToList.${variable.key}`, event.target.checked);
187+
}}
188+
/>
189+
</Field>
190+
</FormRow>
191+
)}
156192
<FormRow>
157193
<Field
158194
name={`${namePrefix}.targetPath`}

src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiVariableConfigurationEditor.js

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const ObjectsApiVariableConfigurationEditor = ({variable}) => {
4545
objecttype,
4646
objecttypeVersion,
4747
geometryVariableKey,
48+
transformToList,
4849
variablesMapping,
4950
version,
5051
} = backendOptions;
@@ -99,6 +100,7 @@ const ObjectsApiVariableConfigurationEditor = ({variable}) => {
99100
components={components}
100101
namePrefix={namePrefix}
101102
isGeometry={isGeometry}
103+
transformToList={transformToList}
102104
index={index}
103105
mappedVariable={mappedVariable}
104106
objecttype={objecttype}

src/openforms/js/components/admin/form_design/registrations/objectsapi/utils.js

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import {REGISTRATION_OBJECTS_TARGET_PATHS} from 'components/admin/form_design/constants';
2+
import {post} from 'utils/fetch';
3+
14
const VARIABLE_TYPE_MAP = {
25
boolean: 'boolean',
36
int: 'integer',
@@ -57,4 +60,25 @@ const asJsonSchema = (variable, components) => {
5760
};
5861
};
5962

60-
export {asJsonSchema};
63+
const fetchTargetPaths = async (
64+
csrftoken,
65+
objectsApiGroup,
66+
objecttype,
67+
objecttypeVersion,
68+
schemaType
69+
) => {
70+
const response = await post(REGISTRATION_OBJECTS_TARGET_PATHS, csrftoken, {
71+
objectsApiGroup,
72+
objecttype,
73+
objecttypeVersion,
74+
variableJsonSchema: schemaType,
75+
});
76+
77+
if (!response.ok) {
78+
throw new Error(`Error when loading target paths for type: ${schemaType}`);
79+
}
80+
81+
return response.data;
82+
};
83+
84+
export {asJsonSchema, fetchTargetPaths};

src/openforms/registrations/contrib/objects_api/config.py

+10
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,16 @@ class ObjectsAPIOptionsSerializer(JsonSchemaSerializerMixin, serializers.Seriali
256256
required=False,
257257
allow_blank=True,
258258
)
259+
transform_to_list = serializers.ListField(
260+
child=FormioVariableKeyField(),
261+
label=_("transform to list"),
262+
required=False,
263+
default=list,
264+
help_text=_(
265+
"The components which need special handling concerning the shape of the data "
266+
"and need to be transformed to a list."
267+
),
268+
)
259269

260270
def validate(self, attrs: RegistrationOptions) -> RegistrationOptions:
261271
v1_only_fields = {

src/openforms/registrations/contrib/objects_api/handlers/v2.py

+8
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def process_mapped_variable(
5858
value: (
5959
JSONValue | date | datetime
6060
), # can't narrow it down yet, as the type depends on the component type
61+
transform_to_list: list | None = None,
6162
component: Component | None = None,
6263
attachment_urls: dict[str, list[str]] | None = None,
6364
) -> AssignmentSpec | Sequence[AssignmentSpec]:
@@ -73,6 +74,8 @@ def process_mapped_variable(
7374
:arg value: The raw value of the form variable for the submission being processed.
7475
The type/shape of the value depends on the variable/component data type being
7576
processed and even the component configuration (such as multiple True/False).
77+
:arg transform_to_list: The list of the variables(keys) which need special handling
78+
concerning the shape of the data (need transformation to a list).
7679
:arg component: If the variable corresponds to a Formio component, the component
7780
definition is provided, otherwise ``None``.
7881
:arg attachment_urls: The registration plugin uploads attachments to a Documents API
@@ -139,6 +142,11 @@ def process_mapped_variable(
139142
attachment_urls=attachment_urls,
140143
key_prefix=variable_key,
141144
)
145+
case {"type": "selectboxes"}:
146+
assert isinstance(value, dict)
147+
if transform_to_list and variable_key in transform_to_list:
148+
value = [option for option, is_selected in value.items() if is_selected]
149+
142150
# not a component or standard behaviour where no transformation is necessary
143151
case None | _:
144152
pass

src/openforms/registrations/contrib/objects_api/submission_registration.py

+2
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ def get_record_data(
521521
urls_map = self.get_attachment_urls_by_key(submission)
522522

523523
variables_mapping = options["variables_mapping"]
524+
transform_to_list = options.get("transform_to_list")
524525

525526
# collect all the assignments to be done to the object
526527
assignment_specs: list[AssignmentSpec] = []
@@ -571,6 +572,7 @@ def get_record_data(
571572
value=value,
572573
component=component,
573574
attachment_urls=urls_map,
575+
transform_to_list=transform_to_list,
574576
)
575577
if isinstance(assignment_spec, AssignmentSpec):
576578
assignment_specs.append(assignment_spec)

src/openforms/registrations/contrib/objects_api/typing.py

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class RegistrationOptionsV2(_BaseRegistrationOptions, total=False):
7070
version: Required[Literal[2]]
7171
variables_mapping: Required[list[ObjecttypeVariableMapping]]
7272
geometry_variable_key: str
73+
transform_to_list: NotRequired[list]
7374

7475

7576
type RegistrationOptions = RegistrationOptionsV1 | RegistrationOptionsV2

0 commit comments

Comments
 (0)