diff --git a/src/openforms/contrib/brk/checks.py b/src/openforms/contrib/brk/checks.py
index 255eba53e6..7490d07fb2 100644
--- a/src/openforms/contrib/brk/checks.py
+++ b/src/openforms/contrib/brk/checks.py
@@ -21,7 +21,7 @@ def check_config():
{"postcode": "1234AB", "huisnummer": "1"}
)
except NoServiceConfigured as exc:
- msg = _("{api_name} endpoint is not configured.").format(api_name="KVK")
+ msg = _("{api_name} endpoint is not configured").format(api_name="KVK")
raise InvalidPluginConfiguration(msg) from exc
except requests.RequestException as exc:
raise InvalidPluginConfiguration(
diff --git a/src/openforms/contrib/brk/service.py b/src/openforms/contrib/brk/service.py
new file mode 100644
index 0000000000..2d1d49056f
--- /dev/null
+++ b/src/openforms/contrib/brk/service.py
@@ -0,0 +1,16 @@
+from openforms.forms.models.form import Form
+from openforms.plugins.exceptions import InvalidPluginConfiguration
+
+from .checks import BRKValidatorCheck
+
+
+def check_brk_config_for_addressNL() -> str:
+ live_forms = Form.objects.live()
+
+ if any(form.has_component("addressNL") for form in live_forms):
+ try:
+ BRKValidatorCheck.check_config()
+ except InvalidPluginConfiguration as e:
+ return e.args[0]
+
+ return ""
diff --git a/src/openforms/logging/service.py b/src/openforms/emails/digest.py
similarity index 87%
rename from src/openforms/logging/service.py
rename to src/openforms/emails/digest.py
index d950abfa79..f67af0549d 100644
--- a/src/openforms/logging/service.py
+++ b/src/openforms/emails/digest.py
@@ -6,13 +6,16 @@
from django.contrib.contenttypes.models import ContentType
from django.urls import reverse
+from django.utils.translation import gettext_lazy as _
from django_yubin.models import Message
from furl import furl
+from openforms.contrib.brk.service import check_brk_config_for_addressNL
from openforms.logging.models import TimelineLogProxy
from openforms.submissions.models.submission import Submission
from openforms.submissions.utils import get_filtered_submission_admin_url
+from openforms.typing import StrOrPromise
@dataclass
@@ -60,6 +63,12 @@ def failed_submissions_counter(self) -> int:
return len(self.submission_ids)
+@dataclass
+class BrokenConfiguration:
+ config_name: StrOrPromise
+ exception_message: str
+
+
def collect_failed_emails(since: datetime) -> Iterable[FailedEmail]:
logs = TimelineLogProxy.objects.filter(
timestamp__gt=since,
@@ -156,3 +165,17 @@ def collect_failed_prefill_plugins(since: datetime) -> list[FailedPrefill]:
)
return failed_prefill_plugins
+
+
+def collect_broken_configurations() -> list[BrokenConfiguration]:
+ check_brk_configuration = check_brk_config_for_addressNL()
+
+ broken_configurations = []
+ if check_brk_configuration:
+ broken_configurations.append(
+ BrokenConfiguration(
+ config_name=_("BRK Client"), exception_message=check_brk_configuration
+ )
+ )
+
+ return broken_configurations
diff --git a/src/openforms/emails/tasks.py b/src/openforms/emails/tasks.py
index dd9c48cc70..2963fad61c 100644
--- a/src/openforms/emails/tasks.py
+++ b/src/openforms/emails/tasks.py
@@ -8,12 +8,13 @@
from openforms.celery import app
from openforms.config.models import GlobalConfiguration
-from openforms.logging.service import (
+
+from .digest import (
+ collect_broken_configurations,
collect_failed_emails,
collect_failed_prefill_plugins,
collect_failed_registrations,
)
-
from .utils import send_mail_html
@@ -25,11 +26,13 @@ def get_context_data(self) -> dict[str, Any]:
failed_emails = collect_failed_emails(self.since)
failed_registrations = collect_failed_registrations(self.since)
failed_prefill_plugins = collect_failed_prefill_plugins(self.since)
+ broken_configurations = collect_broken_configurations()
return {
"failed_emails": failed_emails,
"failed_registrations": failed_registrations,
"failed_prefill_plugins": failed_prefill_plugins,
+ "broken_configurations": broken_configurations,
}
def render(self) -> str:
diff --git a/src/openforms/emails/templates/emails/admin_digest.html b/src/openforms/emails/templates/emails/admin_digest.html
index d27d33f5fe..474eee6f89 100644
--- a/src/openforms/emails/templates/emails/admin_digest.html
+++ b/src/openforms/emails/templates/emails/admin_digest.html
@@ -63,3 +63,17 @@
{% trans "Prefill plugins" %}
{% endfor %}
{% endif %}
+
+{% if broken_configurations %}
+ {% trans "Configuration problems" %}
+
+ {% for config in broken_configurations %}
+ -
+ {% blocktranslate with name=config.config_name message=config.exception_message trimmed %}
+ The configuration for '{{ name }}' is invalid ({{ message }}).
+ Components or plugins which make use of this will not work properly.
+ {% endblocktranslate %}
+
+ {% endfor %}
+
+{% endif %}
diff --git a/src/openforms/emails/tests/test_tasks.py b/src/openforms/emails/tests/test_tasks.py
index 425d419b0f..fb10c93023 100644
--- a/src/openforms/emails/tests/test_tasks.py
+++ b/src/openforms/emails/tests/test_tasks.py
@@ -2,12 +2,20 @@
from django.core import mail
from django.test import TestCase, override_settings
+from django.utils.translation import gettext as _
+import requests_mock
from django_yubin.models import Message
from freezegun import freeze_time
from openforms.config.models import GlobalConfiguration
-from openforms.forms.tests.factories import FormFactory
+from openforms.contrib.brk.models import BRKConfig
+from openforms.contrib.brk.tests.base import BRK_SERVICE
+from openforms.forms.tests.factories import (
+ FormDefinitionFactory,
+ FormFactory,
+ FormStepFactory,
+)
from openforms.logging import logevent
from openforms.prefill.registry import register
from openforms.registrations.exceptions import RegistrationFailed
@@ -260,3 +268,134 @@ def test_prefill_plugin_failures_are_sent(self):
self.assertIn(str(submission_1.id), sent_email.body)
self.assertIn(str(submission_2.id), sent_email.body)
self.assertIn(str(submission.id), sent_email.body)
+
+ @requests_mock.Mocker()
+ def test_no_email_sent_when_brk_congiguration_is_valid_for_addressNL(self, m):
+ form = FormFactory.create()
+ form_definition = FormDefinitionFactory.create(
+ configuration={
+ "display": "form",
+ "components": [
+ {
+ "key": "addressNl",
+ "type": "addressNL",
+ "label": "AddressNL",
+ "defaultValue": {
+ "postcode": "",
+ "houseLetter": "",
+ "houseNumber": "",
+ "houseNumberAddition": "",
+ },
+ }
+ ],
+ }
+ )
+ FormStepFactory.create(form=form, form_definition=form_definition)
+
+ with (
+ freeze_time("2023-01-03T01:00:00+01:00"),
+ patch(
+ "openforms.emails.tasks.GlobalConfiguration.get_solo",
+ return_value=GlobalConfiguration(
+ recipients_email_digest=["user@example.com"]
+ ),
+ ),
+ patch(
+ "openforms.contrib.brk.client.BRKConfig.get_solo",
+ return_value=BRKConfig(service=BRK_SERVICE),
+ ),
+ ):
+
+ m.get(
+ "https://api.brk.kadaster.nl/esd-eto-apikey/bevragen/v2/kadastraalonroerendezaken?postcode=1234AB&huisnummer=1",
+ json={"_embedded": {}},
+ )
+
+ send_email_digest()
+
+ self.assertEqual(0, len(mail.outbox))
+
+ def test_broken_brk_configuration_for_addressNL_is_sent(self):
+ form = FormFactory.create()
+ form_definition = FormDefinitionFactory.create(
+ configuration={
+ "display": "form",
+ "components": [
+ {
+ "key": "addressNl",
+ "type": "addressNL",
+ "label": "AddressNL",
+ "defaultValue": {
+ "postcode": "",
+ "houseLetter": "",
+ "houseNumber": "",
+ "houseNumberAddition": "",
+ },
+ }
+ ],
+ }
+ )
+ FormStepFactory.create(form=form, form_definition=form_definition)
+
+ with (
+ freeze_time("2023-01-03T01:00:00+01:00"),
+ patch(
+ "openforms.emails.tasks.GlobalConfiguration.get_solo",
+ return_value=GlobalConfiguration(
+ recipients_email_digest=["user@example.com"]
+ ),
+ ),
+ patch(
+ "openforms.contrib.brk.client.BRKConfig.get_solo",
+ return_value=BRKConfig(service=None),
+ ),
+ ):
+
+ send_email_digest()
+
+ sent_email = mail.outbox[0]
+
+ self.assertEqual(
+ sent_email.subject, "[Open Forms] Daily summary of detected problems"
+ )
+ self.assertEqual(sent_email.recipients(), ["user@example.com"])
+ self.assertIn(
+ _(
+ "The configuration for 'BRK Client' is invalid (KVK endpoint is not configured)."
+ ),
+ sent_email.body,
+ )
+
+ def test_no_email_sent_when_brk_congiguration_is_invalid_but_unused(self):
+ form = FormFactory.create()
+ form_definition = FormDefinitionFactory.create(
+ configuration={
+ "display": "form",
+ "components": [
+ {
+ "key": "textField",
+ "type": "textfield",
+ "label": "Text Field",
+ }
+ ],
+ }
+ )
+ FormStepFactory.create(form=form, form_definition=form_definition)
+
+ with (
+ freeze_time("2023-01-03T01:00:00+01:00"),
+ patch(
+ "openforms.emails.tasks.GlobalConfiguration.get_solo",
+ return_value=GlobalConfiguration(
+ recipients_email_digest=["user@example.com"]
+ ),
+ ),
+ patch(
+ "openforms.contrib.brk.client.BRKConfig.get_solo",
+ return_value=BRKConfig(service=None),
+ ),
+ ):
+
+ send_email_digest()
+
+ self.assertEqual(0, len(mail.outbox))
diff --git a/src/openforms/forms/models/form.py b/src/openforms/forms/models/form.py
index 7c8e8abb33..3304a1666e 100644
--- a/src/openforms/forms/models/form.py
+++ b/src/openforms/forms/models/form.py
@@ -603,6 +603,11 @@ def deactivate(self):
logger.debug("Deactivating form %s", self.admin_name)
self.save(update_fields=["active", "deactivate_on"])
+ def has_component(self, component_type: str) -> bool:
+ return any(
+ component["type"] == component_type for component in self.iter_components()
+ )
+
class FormsExportQuerySet(DeleteFilesQuerySetMixin, models.QuerySet):
pass