diff --git a/src/openforms/authentication/static_variables/static_variables.py b/src/openforms/authentication/static_variables/static_variables.py
index 535f7bdead..77cab2b858 100644
--- a/src/openforms/authentication/static_variables/static_variables.py
+++ b/src/openforms/authentication/static_variables/static_variables.py
@@ -92,7 +92,7 @@ def as_json_schema(self):
return {
"title": "Authentication type",
"type": "string",
- "enum": AuthAttribute.values,
+ "enum": [*AuthAttribute.values, ""],
}
diff --git a/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js b/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js
index bdd11f7084..ba1002ae1f 100644
--- a/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js
+++ b/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js
@@ -771,6 +771,8 @@ export const ConfiguredBackends = {
service: 1,
path: 'example/endpoint',
variables: [],
+ requiredMetadataVariables: [],
+ additionalMetadataVariables: [],
},
},
],
@@ -1030,6 +1032,8 @@ export const JSONDump = {
service: 1,
path: 'example/endpoint',
variables: [],
+ requiredMetadataVariables: ["form_name"],
+ additionalMetadataVariables: ["auth_bsn"],
},
},
],
@@ -1089,7 +1093,7 @@ export const JSONDump = {
formDefinition: null,
name: 'BSN',
key: 'auth_bsn',
- source: 'static',
+ source: '',
prefillPlugin: '',
prefillAttribute: '',
prefillIdentifierRole: '',
@@ -1100,6 +1104,22 @@ export const JSONDump = {
serviceFetchConfiguration: undefined,
initialValue: '',
},
+ {
+ form: null,
+ formDefinition: null,
+ name: 'Form name',
+ key: 'form_name',
+ source: '',
+ prefillPlugin: '',
+ prefillAttribute: '',
+ prefillIdentifierRole: '',
+ prefillOptions: {},
+ dataType: 'string',
+ dataFormat: '',
+ isSensitiveData: false,
+ serviceFetchConfiguration: undefined,
+ initialValue: '',
+ }
],
},
diff --git a/src/openforms/js/components/admin/form_design/registrations/json_dump/JSONDumpOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/json_dump/JSONDumpOptionsForm.js
index 987e8e09c0..27008db564 100644
--- a/src/openforms/js/components/admin/form_design/registrations/json_dump/JSONDumpOptionsForm.js
+++ b/src/openforms/js/components/admin/form_design/registrations/json_dump/JSONDumpOptionsForm.js
@@ -11,7 +11,7 @@ import {
} from 'components/admin/forms/ValidationErrors';
import {getChoicesFromSchema} from 'utils/json-schema';
-import {Path, ServiceSelect, Variables} from './fields';
+import {MetadataVariables, Path, ServiceSelect, Variables} from './fields';
const JSONDumpOptionsForm = ({name, label, schema, formData, onChange}) => {
const validationErrors = useContext(ValidationErrorContext);
@@ -38,6 +38,8 @@ const JSONDumpOptionsForm = ({name, label, schema, formData, onChange}) => {
service: null,
path: '',
variables: [],
+ requiredMetadataVariables: [],
+ additionalMetadataVariables: [],
...formData,
}}
onSubmit={values => onChange({formData: values})}
@@ -48,6 +50,7 @@ const JSONDumpOptionsForm = ({name, label, schema, formData, onChange}) => {
+
@@ -69,6 +72,8 @@ JSONDumpOptionsForm.propTypes = {
service: PropTypes.number,
path: PropTypes.string,
variables: PropTypes.arrayOf(PropTypes.string),
+ requiredMetadataVariables: PropTypes.arrayOf(PropTypes.string),
+ additionalMetadataVariables: PropTypes.arrayOf(PropTypes.string)
}),
onChange: PropTypes.func.isRequired,
};
diff --git a/src/openforms/js/components/admin/form_design/registrations/json_dump/JSONDumpSummaryHandler.js b/src/openforms/js/components/admin/form_design/registrations/json_dump/JSONDumpSummaryHandler.js
index 0c74f9e4d8..f39a31dd49 100644
--- a/src/openforms/js/components/admin/form_design/registrations/json_dump/JSONDumpSummaryHandler.js
+++ b/src/openforms/js/components/admin/form_design/registrations/json_dump/JSONDumpSummaryHandler.js
@@ -2,19 +2,39 @@ import PropTypes from 'prop-types';
import React from 'react';
import {IconNo, IconYes} from 'components/admin/BooleanIcons';
+import {FormattedMessage} from 'react-intl';
const JSONDumpSummaryHandler = ({variable, backendOptions}) => {
- const isIncluded = backendOptions.variables.includes(variable.key);
+ const isIncludedInVariables = backendOptions.variables.includes(variable.key)
+ const isIncludedInMetadata = backendOptions.requiredMetadataVariables.includes(variable.key)
+ || backendOptions.additionalMetadataVariables.includes(variable.key)
- return isIncluded ? : ;
+ return (
+ <>
+
+ {isIncludedInVariables ? : }
+ {/*TODO-5012: maybe exclude this for non static variables?*/}
+
+
+ {isIncludedInMetadata ? : }
+ >
+ );
};
JSONDumpSummaryHandler.propTypes = {
variable: PropTypes.shape({
- key: PropTypes.string.isRequired,
+ source: PropTypes.string.isRequired,
}).isRequired,
backendOptions: PropTypes.shape({
variables: PropTypes.arrayOf(PropTypes.string).isRequired,
+ requiredMetadataVariables: PropTypes.arrayOf(PropTypes.string).isRequired,
+ additionalMetadataVariables: PropTypes.arrayOf(PropTypes.string).isRequired,
}).isRequired,
};
diff --git a/src/openforms/js/components/admin/form_design/registrations/json_dump/JSONDumpVariableConfigurationEditor.js b/src/openforms/js/components/admin/form_design/registrations/json_dump/JSONDumpVariableConfigurationEditor.js
index 75f5b4077c..d34017a628 100644
--- a/src/openforms/js/components/admin/form_design/registrations/json_dump/JSONDumpVariableConfigurationEditor.js
+++ b/src/openforms/js/components/admin/form_design/registrations/json_dump/JSONDumpVariableConfigurationEditor.js
@@ -9,39 +9,72 @@ import {Checkbox} from 'components/admin/forms/Inputs';
const JSONDumpVariableConfigurationEditor = ({variable}) => {
const {
- values: {variables = []},
+ values: {variables = [], additionalMetadataVariables = [], requiredMetadataVariables = []},
setFieldValue,
} = useFormikContext();
const isIncluded = variables.includes(variable.key);
+ const isRequiredInMetadata = requiredMetadataVariables.includes(variable.key)
+ const isInAdditionalMetadata = additionalMetadataVariables.includes(variable.key)
return (
-
-
-
- }
- helpText={
-
- }
- checked={isIncluded}
- onChange={event => {
- const shouldBeIncluded = event.target.checked;
- const newVariables = shouldBeIncluded
- ? [...variables, variable.key] // add the variable to the array
- : variables.filter(key => key !== variable.key); // remove the variable from the array
- setFieldValue('variables', newVariables);
- }}
- />
-
-
+ <>
+
+
+
+ }
+ helpText={
+
+ }
+ checked={isIncluded}
+ onChange={event => {
+ const shouldBeIncluded = event.target.checked;
+ const newVariables = shouldBeIncluded
+ ? [...variables, variable.key] // add the variable to the array
+ : variables.filter(key => key !== variable.key); // remove the variable from the array
+ setFieldValue('variables', newVariables);
+ }}
+ />
+
+
+
+
+
+
+ }
+ helpText={
+
+ }
+ checked={isInAdditionalMetadata || isRequiredInMetadata}
+ onChange={event => {
+ const shouldBeIncluded = event.target.checked;
+ const newVariables = shouldBeIncluded
+ ? [...additionalMetadataVariables, variable.key] // add the variable to the array
+ : additionalMetadataVariables.filter(key => key !== variable.key); // remove the variable from the array
+ setFieldValue('additionalMetadataVariables', newVariables);
+ }}
+ disabled={isRequiredInMetadata || variable.source !== ""} // disable if it is not required or the variable is not a static variable
+ />
+
+
+ >
);
};
diff --git a/src/openforms/js/components/admin/form_design/registrations/json_dump/fields/MetadataVariables.js b/src/openforms/js/components/admin/form_design/registrations/json_dump/fields/MetadataVariables.js
new file mode 100644
index 0000000000..8c0c6c3f11
--- /dev/null
+++ b/src/openforms/js/components/admin/form_design/registrations/json_dump/fields/MetadataVariables.js
@@ -0,0 +1,48 @@
+import { useField } from 'formik';
+import React from 'react';
+import { FormattedMessage } from 'react-intl';
+
+
+
+import Field from 'components/admin/forms/Field';
+import FormRow from 'components/admin/forms/FormRow';
+import VariableSelection from 'components/admin/forms/VariableSelection';
+
+
+// TODO-5012: rename to additionalMetadataVariables?
+const MetadataVariables = () => {
+ const [fieldProps] = useField('additionalMetadataVariables');
+ const [, {value}] = useField('requiredMetadataVariables');
+
+ return (
+
+
+ }
+ helpText={
+
+ }
+ noManageChildProps
+ >
+ variable.source === "" && !value.includes(variable.key)} // Only show static variables and variables which are not already required
+ />
+
+
+ );
+};
+
+export default MetadataVariables;
diff --git a/src/openforms/js/components/admin/form_design/registrations/json_dump/fields/index.js b/src/openforms/js/components/admin/form_design/registrations/json_dump/fields/index.js
index ab0ba696a9..e13d87de14 100644
--- a/src/openforms/js/components/admin/form_design/registrations/json_dump/fields/index.js
+++ b/src/openforms/js/components/admin/form_design/registrations/json_dump/fields/index.js
@@ -1,5 +1,6 @@
+import MetadataVariables from './MetadataVariables';
import Path from './Path';
import ServiceSelect from './ServiceSelect';
import Variables from './Variables';
-export {Path, ServiceSelect, Variables};
+export {MetadataVariables, Path, ServiceSelect, Variables};
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 ff3f45280a..22ba1f0b57 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
@@ -664,10 +664,35 @@ export const WithJSONDumpRegistrationBackend = {
options: {
service: 2,
path: 'test',
- variables: ['aSingleFile', 'now'],
+ variables: ['aSingleFile'],
+ requiredMetadataVariables: ['public_registration_reference'],
+ additionalMetadataVariables: ['now']
},
},
],
+ registrationPluginsVariables: [
+ {
+ pluginIdentifier: 'json_dump',
+ pluginVerboseName: 'JSON dump registration',
+ pluginVariables: [
+ {
+ form: null,
+ formDefinition: null,
+ name: 'Public registration reference',
+ key: 'public_registration_reference',
+ source: '',
+ prefillPlugin: '',
+ prefillAttribute: '',
+ prefillIdentifierRole: 'main',
+ dataType: 'string',
+ dataFormat: '',
+ isSensitiveData: false,
+ serviceFetchConfiguration: undefined,
+ initialValue: '',
+ },
+ ],
+ },
+ ],
},
play: async ({canvasElement, step}) => {
const canvas = within(canvasElement);
diff --git a/src/openforms/js/components/admin/forms/VariableSelection.js b/src/openforms/js/components/admin/forms/VariableSelection.js
index b4e2bf3959..f778581f42 100644
--- a/src/openforms/js/components/admin/forms/VariableSelection.js
+++ b/src/openforms/js/components/admin/forms/VariableSelection.js
@@ -37,6 +37,11 @@ const VariableOption = props => {
const allowAny = () => true;
+// TODO-5012: can we add the registrationPluginsVariables as well? Seems difficult, as there is no
+// guarantee that the names of the variables are unique across different registration plugins.
+// They would all be added under the static variables label, as they don't have a source (not
+// user-defined nor component), which means they might get overwritten. I could change the grouping
+// behaviour, but not sure if this is something we should be doing
const VariableSelection = ({
id,
name,
@@ -46,7 +51,7 @@ const VariableSelection = ({
filter = allowAny,
...props
}) => {
- const {formSteps, formVariables, staticVariables} = useContext(FormContext);
+ const {formSteps, formVariables, staticVariables, registrationPluginsVariables, registrationBackends} = useContext(FormContext);
const intl = useIntl();
let formDefinitionsNames = {};
diff --git a/src/openforms/registrations/contrib/json_dump/config.py b/src/openforms/registrations/contrib/json_dump/config.py
index 724ec4e19b..de3d355089 100644
--- a/src/openforms/registrations/contrib/json_dump/config.py
+++ b/src/openforms/registrations/contrib/json_dump/config.py
@@ -47,6 +47,26 @@ class JSONDumpOptionsSerializer(JsonSchemaSerializerMixin, serializers.Serialize
required=True,
min_length=1,
)
+ required_metadata_variables = serializers.ReadOnlyField(
+ default=[
+ "public_reference",
+ "form_name",
+ "form_version",
+ "registration_timestamp",
+ "auth_type",
+ ],
+ label=_("Required metadata variable key list"),
+ help_text=_(
+ "A list of required variables to use in the metadata. These include "
+ "the registration variables of the JSON dump plugin"
+ ),
+ )
+ additional_metadata_variables = serializers.ListField(
+ child=FormioVariableKeyField(),
+ label=_("Additional metadata variable key list"),
+ help_text=_("A list of additional variables to use in the metadata"),
+ required=True,
+ )
class JSONDumpOptions(TypedDict):
@@ -60,3 +80,5 @@ class JSONDumpOptions(TypedDict):
service: Service
path: str
variables: list[str]
+ required_metadata_variables: list[str]
+ additional_metadata_variables: list[str]
diff --git a/src/openforms/registrations/contrib/json_dump/plugin.py b/src/openforms/registrations/contrib/json_dump/plugin.py
index b377d0f6a4..5465efea42 100644
--- a/src/openforms/registrations/contrib/json_dump/plugin.py
+++ b/src/openforms/registrations/contrib/json_dump/plugin.py
@@ -1,5 +1,6 @@
import base64
import json
+from json import JSONDecodeError
from typing import cast
from django.core.exceptions import SuspiciousOperation
@@ -8,6 +9,8 @@
from zgw_consumers.client import build_client
+# TODO-5012: include in openforms.formio.service?
+from openforms.formio.datastructures import FormioConfigurationWrapper
from openforms.formio.service import rewrite_formio_components
from openforms.formio.typing import (
FileComponent,
@@ -16,15 +19,18 @@
SelectComponent,
)
from openforms.forms.json_schema import generate_json_schema
+from openforms.forms.models import FormVariable
from openforms.submissions.models import Submission, SubmissionFileAttachment
from openforms.submissions.service import DataContainer
from openforms.typing import JSONObject
from openforms.utils.json_schema import to_multiple
from openforms.variables.constants import FormVariableSources
+from openforms.variables.service import get_static_variables
from ...base import BasePlugin # openforms.registrations.base
from ...registry import register # openforms.registrations.registry
from .config import JSONDumpOptions, JSONDumpOptionsSerializer
+from .registration_variables import register as variables_registry
@register("json_dump")
@@ -45,20 +51,47 @@ def register_submission(
**state.get_static_data(),
**state.get_data(), # dynamic values from user input
}
+
+ # Update config wrapper. This is necessary to update the options for Select,
+ # SelectBoxes, and Radio components that get their options from another form
+ # variable.
+ data = DataContainer(state=state)
+ configuration_wrapper = rewrite_formio_components(
+ submission.total_configuration_wrapper,
+ submission=submission,
+ data=data.data,
+ )
+
+ # Values
values = {
key: value
for key, value in all_values.items()
if key in options["variables"]
}
+ values_schema = generate_json_schema(submission.form, options["variables"])
+ post_process(values, values_schema, submission, configuration_wrapper)
- # Generate schema
- schema = generate_json_schema(submission.form, options["variables"])
-
- # Post-processing
- post_process(values, schema, submission)
+ # Metadata
+ metadata = {
+ key: value
+ for key, value in all_values.items()
+ if key in options["metadata_variables"]
+ }
+ metadata_schema = generate_json_schema(
+ submission.form, options["metadata_variables"]
+ )
+ post_process(metadata, metadata_schema, submission, configuration_wrapper)
# Send to the service
- data = json.dumps({"values": values, "schema": schema}, cls=DjangoJSONEncoder)
+ data = json.dumps(
+ {
+ "values": values,
+ "values_schema": values_schema,
+ "metadata": metadata,
+ "metadata_schema": metadata_schema,
+ },
+ cls=DjangoJSONEncoder
+ )
service = options["service"]
with build_client(service) as client:
if ".." in (path := options["path"]):
@@ -67,15 +100,26 @@ def register_submission(
result = client.post(path, json=data)
result.raise_for_status()
- return {"api_response": result.json()}
+ try:
+ result_json = result.json()
+ except JSONDecodeError:
+ result_json = None
+
+ return {"api_response": result_json}
def check_config(self) -> None:
# Config checks are not really relevant for this plugin right now
pass
+ def get_variables(self) -> list[FormVariable]:
+ return get_static_variables(variables_registry=variables_registry)
+
def post_process(
- values: JSONObject, schema: JSONObject, submission: Submission
+ values: JSONObject,
+ schema: JSONObject,
+ submission: Submission,
+ configuration_wrapper: FormioConfigurationWrapper,
) -> None:
"""Post-process the values and schema.
@@ -87,19 +131,10 @@ def post_process(
:param values: JSONObject
:param schema: JSONObject
:param submission: Submission
+ :param configuration_wrapper: FormioConfigurationWrapper
"""
state = submission.load_submission_value_variables_state()
- # Update config wrapper. This is necessary to update the options for Select,
- # SelectBoxes, and Radio components that get their options from another form
- # variable.
- data = DataContainer(state=state)
- configuration_wrapper = rewrite_formio_components(
- submission.total_configuration_wrapper,
- submission=submission,
- data=data.data,
- )
-
for key in values.keys():
variable = state.variables.get(key)
if (
diff --git a/src/openforms/registrations/contrib/json_dump/registration_variables.py b/src/openforms/registrations/contrib/json_dump/registration_variables.py
new file mode 100644
index 0000000000..6d1b2afc1b
--- /dev/null
+++ b/src/openforms/registrations/contrib/json_dump/registration_variables.py
@@ -0,0 +1,52 @@
+
+from django.utils.translation import gettext_lazy as _
+
+from openforms.plugins.registry import BaseRegistry
+from openforms.submissions.models import Submission
+from openforms.variables.base import BaseStaticVariable
+from openforms.variables.constants import FormVariableDataTypes
+from openforms.variables.static_variables.static_variables import Now
+
+
+class Registry(BaseRegistry[BaseStaticVariable]):
+ """
+ A registry for the JSON Dump registration variables.
+ """
+
+ module = "json_dump"
+
+
+register = Registry()
+"""The JSON Dump registration variables registry."""
+
+
+@register("public_reference")
+class PublicReference(BaseStaticVariable):
+ name = _("Public reference")
+ data_type = FormVariableDataTypes.string
+
+ def get_initial_value(self, submission: Submission | None = None) -> str:
+ if submission is None:
+ return ""
+ return submission.public_registration_reference
+
+
+@register("form_version")
+class FormVersion(BaseStaticVariable):
+ name = _("Form version")
+ data_type = FormVariableDataTypes.string
+
+ def get_initial_value(self, submission: Submission | None = None) -> str:
+ if submission is None:
+ return ""
+
+ form_version = submission.form.formversion_set.order_by("created").last()
+ return form_version.description if form_version else ""
+
+ def as_json_schema(self):
+ return {"title": "Form version", "type": "string"}
+
+
+@register("registration_timestamp")
+class RegistrationTimestamp(Now):
+ name = _("Registration timestamp")
diff --git a/src/openforms/registrations/contrib/json_dump/tests/test_backend.py b/src/openforms/registrations/contrib/json_dump/tests/test_backend.py
index 03809cbfdc..7c8898bc82 100644
--- a/src/openforms/registrations/contrib/json_dump/tests/test_backend.py
+++ b/src/openforms/registrations/contrib/json_dump/tests/test_backend.py
@@ -8,6 +8,7 @@
from requests import RequestException
from zgw_consumers.test.factories import ServiceFactory
+from openforms.forms.tests.factories import FormVersionFactory
from openforms.submissions.tests.factories import (
FormVariableFactory,
SubmissionFactory,
@@ -22,7 +23,7 @@
VCR_TEST_FILES = Path(__file__).parent / "files"
-class JSONDumpBackendTests(OFVCRMixin, TestCase):
+class JSONDumpBackendTests(TestCase):
VCR_TEST_FILES = VCR_TEST_FILES
@classmethod
@@ -69,6 +70,7 @@ def test_submission_happy_flow(self):
"service": self.service,
"path": "json_plugin",
"variables": ["firstName", "file", "auth_bsn"],
+ "metadata_variables": [],
}
json_plugin = JSONDumpRegistration("json_registration_plugin")
@@ -724,3 +726,37 @@ def test_nested_component_key(self):
result["api_response"]["data"]["schema"]["properties"]["foo.bar"]["type"],
"string",
)
+
+ def test_metadata(self):
+ submission = SubmissionFactory.from_components(
+ [
+ {"key": "firstName", "type": "textfield"},
+ {"key": "lastName", "type": "textfield"},
+ ],
+ completed=True,
+ submitted_data={
+ "firstName": "We Are",
+ "lastName": "Checking",
+ },
+ bsn="123456789",
+ with_public_registration_reference=True,
+ )
+ FormVersionFactory.create(form=submission.form, description="Version 1.0")
+
+ json_plugin = JSONDumpRegistration("json_registration_plugin")
+ options: JSONDumpOptions = {
+ "service": self.service,
+ "path": "json_plugin",
+ "variables": [],
+ "metadata_variables": [
+ "form_name",
+ "form_version",
+ "now",
+ "public_reference",
+ "auth_type",
+ ],
+ }
+ result = json_plugin.register_submission(submission, options)
+
+ from pprint import pprint
+ pprint(result["api_response"]["data"])
diff --git a/src/openforms/variables/tests/test_static_variables.py b/src/openforms/variables/tests/test_static_variables.py
index 05210280ca..b8f87533cd 100644
--- a/src/openforms/variables/tests/test_static_variables.py
+++ b/src/openforms/variables/tests/test_static_variables.py
@@ -6,6 +6,7 @@
from freezegun import freeze_time
from jsonschema import Draft202012Validator
+from openforms.forms.tests.factories import FormVersionFactory
from openforms.submissions.tests.factories import SubmissionFactory
from ..registry import register_static_variable as register
@@ -93,6 +94,11 @@ def test_without_submission(self):
self.assertEqual(variable.initial_value, "")
+ with self.subTest("form version"):
+ variable = _get_variable("form_version")
+
+ self.assertEqual(variable.initial_value, "")
+
def test_with_submission(self):
submission = SubmissionFactory.create(
form__name="Public form name",
@@ -112,6 +118,16 @@ def test_with_submission(self):
variable.initial_value, "f5ea7397-65c3-4ce0-b955-9b8f408e0ae0"
)
+ with self.subTest("form version"):
+ FormVersionFactory.create(form=submission.form, description="Version 1")
+ FormVersionFactory.create(form=submission.form, description="Version 2")
+
+ variable = _get_variable("form_version", submission=submission)
+
+ self.assertEqual(
+ variable.initial_value, "Version 2"
+ )
+
class TodayTests(TestCase):
@freeze_time("2022-11-24T00:30:00+01:00")
@@ -123,6 +139,19 @@ def test_date_has_the_right_day(self):
self.assertEqual(variable.initial_value.day, 24)
+class PublicReferenceTests(TestCase):
+ def test_without_submission(self):
+ variable = _get_variable("public_reference")
+ self.assertEqual(variable.initial_value, "")
+
+ def test_with_submission(self):
+ submission = SubmissionFactory.create(public_registration_reference="OF-ABC123")
+
+ variable = _get_variable("public_reference", submission=submission)
+
+ self.assertEqual(variable.initial_value, "OF-ABC123")
+
+
class StaticVariablesValidJsonSchemaTests(TestCase):
validator = Draft202012Validator