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" %}
+ +{% 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