Skip to content

Commit

Permalink
✨ [#4993] Raise exception if fetching from Referentielijsten fails
Browse files Browse the repository at this point in the history
this should result in a hard crash of the form
  • Loading branch information
stevenbal committed Jan 13, 2025
1 parent d7de51d commit 85b6203
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 27 deletions.
14 changes: 13 additions & 1 deletion src/openforms/formio/dynamic_config/dynamic_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
from .referentielijsten import fetch_options_from_referentielijsten


class DynamicOptionException(Exception):
def __init__(self, msg: str, detail: str):
self.detail = detail

super().__init__(msg)


def normalise_option(option: JSONValue) -> JSONValue:
if not isinstance(option, list):
return [option, option]
Expand Down Expand Up @@ -56,7 +63,12 @@ def add_options_to_config(
component, submission, items_expression
)
if not items_array:
return
raise DynamicOptionException(
"Could not retrieve options from Referentielijsten API",
detail=_(
"Loading the form failed due to problems with an external system, please try again later"
),
)
elif data_src == "variable":
items_expression = glom(component, "openForms.itemsExpression")
items_array = jsonLogic(items_expression, data)
Expand Down
62 changes: 42 additions & 20 deletions src/openforms/formio/tests/test_dynamic_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
from openforms.utils.tests.cache import clear_caches
from openforms.utils.tests.vcr import OFVCRMixin

from ..dynamic_config.dynamic_options import DynamicOptionException

TESTS_DIR = Path(__file__).parent.resolve()
TEST_FILES = TESTS_DIR / "files"

Expand Down Expand Up @@ -351,7 +353,10 @@ def test_success(self):
def test_no_service_configured(self):
self.component["openForms"]["service"] = ""

Select("select").mutate_config_dynamically(self.component, self.submission, {})
with self.assertRaises(DynamicOptionException):
Select("select").mutate_config_dynamically(
self.component, self.submission, {}
)

self.assertNotIn("data", self.component)
log = TimelineLogProxy.objects.get(object_id=self.submission.id)
Expand All @@ -367,7 +372,10 @@ def test_no_service_configured(self):
def test_no_code_configured(self):
self.component["openForms"]["code"] = ""

Select("select").mutate_config_dynamically(self.component, self.submission, {})
with self.assertRaises(DynamicOptionException):
Select("select").mutate_config_dynamically(
self.component, self.submission, {}
)

self.assertNotIn("data", self.component)
log = TimelineLogProxy.objects.get(object_id=self.submission.id)
Expand All @@ -383,7 +391,10 @@ def test_no_code_configured(self):
def test_service_does_not_exist(self):
self.service.delete()

Select("select").mutate_config_dynamically(self.component, self.submission, {})
with self.assertRaises(DynamicOptionException):
Select("select").mutate_config_dynamically(
self.component, self.submission, {}
)

self.assertNotIn("data", self.component)
log = TimelineLogProxy.objects.get(object_id=self.submission.id)
Expand All @@ -399,7 +410,10 @@ def test_service_does_not_exist(self):
def test_items_not_found(self):
self.component["openForms"]["code"] = "non-existent"

Select("select").mutate_config_dynamically(self.component, self.submission, {})
with self.assertRaises(DynamicOptionException):
Select("select").mutate_config_dynamically(
self.component, self.submission, {}
)

self.assertNotIn("data", self.component)
log = TimelineLogProxy.objects.get(object_id=self.submission.id)
Expand All @@ -419,7 +433,10 @@ def test_request_exception(self, m):
exc=RequestException("something went wrong"),
)

Select("select").mutate_config_dynamically(self.component, self.submission, {})
with self.assertRaises(DynamicOptionException):
Select("select").mutate_config_dynamically(
self.component, self.submission, {}
)

self.assertNotIn("data", self.component)
log = TimelineLogProxy.objects.get(object_id=self.submission.id)
Expand Down Expand Up @@ -482,9 +499,10 @@ def test_success(self):
def test_no_service_configured(self):
self.component["openForms"]["service"] = ""

SelectBoxes("selectboxes").mutate_config_dynamically(
self.component, self.submission, {}
)
with self.assertRaises(DynamicOptionException):
SelectBoxes("selectboxes").mutate_config_dynamically(
self.component, self.submission, {}
)

self.assertNotIn("data", self.component)
log = TimelineLogProxy.objects.get(object_id=self.submission.id)
Expand All @@ -500,9 +518,10 @@ def test_no_service_configured(self):
def test_no_code_configured(self):
self.component["openForms"]["code"] = ""

SelectBoxes("selectboxes").mutate_config_dynamically(
self.component, self.submission, {}
)
with self.assertRaises(DynamicOptionException):
SelectBoxes("selectboxes").mutate_config_dynamically(
self.component, self.submission, {}
)

self.assertNotIn("data", self.component)
log = TimelineLogProxy.objects.get(object_id=self.submission.id)
Expand All @@ -518,9 +537,10 @@ def test_no_code_configured(self):
def test_service_does_not_exist(self):
self.service.delete()

SelectBoxes("selectboxes").mutate_config_dynamically(
self.component, self.submission, {}
)
with self.assertRaises(DynamicOptionException):
SelectBoxes("selectboxes").mutate_config_dynamically(
self.component, self.submission, {}
)

self.assertNotIn("data", self.component)
log = TimelineLogProxy.objects.get(object_id=self.submission.id)
Expand All @@ -536,9 +556,10 @@ def test_service_does_not_exist(self):
def test_items_not_found(self):
self.component["openForms"]["code"] = "non-existent"

SelectBoxes("selectboxes").mutate_config_dynamically(
self.component, self.submission, {}
)
with self.assertRaises(DynamicOptionException):
SelectBoxes("selectboxes").mutate_config_dynamically(
self.component, self.submission, {}
)

self.assertNotIn("data", self.component)
log = TimelineLogProxy.objects.get(object_id=self.submission.id)
Expand All @@ -558,9 +579,10 @@ def test_request_exception(self, m):
exc=RequestException("something went wrong"),
)

SelectBoxes("selectboxes").mutate_config_dynamically(
self.component, self.submission, {}
)
with self.assertRaises(DynamicOptionException):
SelectBoxes("selectboxes").mutate_config_dynamically(
self.component, self.submission, {}
)

self.assertNotIn("data", self.component)
log = TimelineLogProxy.objects.get(object_id=self.submission.id)
Expand Down
18 changes: 12 additions & 6 deletions src/openforms/submissions/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from rest_framework.exceptions import APIException
from rest_framework.reverse import reverse
from rest_framework_nested.serializers import NestedHyperlinkedModelSerializer

Expand Down Expand Up @@ -267,12 +268,17 @@ class Meta:
@elasticapm.capture_span(span_type="app.api.serialization")
def to_representation(self, instance):
# invoke the configured form logic to dynamically update the Formio.js configuration
new_configuration = evaluate_form_logic(
instance.submission,
instance,
instance.submission.data,
**self.context,
)
try:
new_configuration = evaluate_form_logic(
instance.submission,
instance,
instance.submission.data,
**self.context,
)
except Exception as e:
if detail := getattr(e, "detail", None):
raise APIException(detail)
raise e
# update the config for serialization
instance.form_step.form_definition.configuration = new_configuration
return super().to_representation(instance)
Expand Down

0 comments on commit 85b6203

Please sign in to comment.