Skip to content

Commit

Permalink
Merge pull request #1529 from maykinmedia/task/2940-openklant2-config
Browse files Browse the repository at this point in the history
[#2940] Add OpenKlant2 configuration model
  • Loading branch information
alextreme authored Dec 18, 2024
2 parents 0d4c736 + 09dc738 commit 2429bd2
Show file tree
Hide file tree
Showing 20 changed files with 191 additions and 136 deletions.
9 changes: 3 additions & 6 deletions src/open_inwoner/accounts/signals.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging

from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.core.exceptions import ImproperlyConfigured
from django.dispatch import receiver
from django.urls import reverse
from django.utils.translation import gettext as _
Expand Down Expand Up @@ -39,17 +38,15 @@ def update_user_from_klant_on_login(sender, user, request, *args, **kwargs):
# OpenKlant2
try:
service = OpenKlant2Service()
except ImproperlyConfigured:
logger.error("OpenKlant2 configuration missing")
except Exception:
logger.error("OpenKlant2 service failed to build")
else:
_update_user_from_openklant2(user=user, service=service, request=request)

# eSuite
try:
service = eSuiteKlantenService()
except ImproperlyConfigured:
logger.error("eSuiteKlantenService missing configuration")
except RuntimeError:
except Exception:
logger.error("eSuiteKlantenService failed to build")
else:
_update_user_from_esuite(user=user, service=service, request=request)
Expand Down
4 changes: 0 additions & 4 deletions src/open_inwoner/accounts/tests/test_profile_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from django import forms
from django.conf import settings
from django.contrib.auth import signals
from django.template.defaultfilters import date as django_date
from django.test import TestCase, override_settings, tag
from django.urls import reverse, reverse_lazy
Expand All @@ -16,7 +15,6 @@
from webtest import Upload

from open_inwoner.accounts.choices import NotificationChannelChoice, StatusChoices
from open_inwoner.accounts.signals import update_user_from_klant_on_login
from open_inwoner.cms.profile.cms_appconfig import ProfileConfig
from open_inwoner.configurations.models import SiteConfiguration
from open_inwoner.haalcentraal.tests.mixins import HaalCentraalMixin
Expand Down Expand Up @@ -318,8 +316,6 @@ def test_messages_enabled_disabled(self):
)
class EditProfileTests(AssertTimelineLogMixin, WebTest):
def setUp(self):
signals.user_logged_in.disconnect(receiver=update_user_from_klant_on_login)

self.url = reverse("profile:edit")
self.return_url = reverse("profile:detail")
self.user = UserFactory()
Expand Down
47 changes: 21 additions & 26 deletions src/open_inwoner/accounts/views/signals.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import logging

from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver

from open_inwoner.accounts.models import User
from open_inwoner.openklant.models import OpenKlant2Config
from open_inwoner.openklant.services import OpenKlant2Service, eSuiteKlantenService

logger = logging.getLogger(__name__)
Expand All @@ -23,37 +21,34 @@ def get_or_create_klant_for_new_user(
user = instance

# OpenKlant2
# TODO: replace with proper config and refactor branching
use_ok2 = getattr(settings, "OPENKLANT2_CONFIG", None)
if use_ok2 and (openklant2_config := OpenKlant2Config.from_django_settings()):
try:
service = OpenKlant2Service(config=openklant2_config)
except RuntimeError:
logger.error("OpenKlant2 service failed to build")
return

if not (
fetch_params := service.get_fetch_parameters(
user=user, use_vestigingsnummer=True
)
):
return
try:
service = OpenKlant2Service()
except Exception:
logger.error("OpenKlant2 service failed to build")
return

partij, created = service.get_or_create_partij_for_user(
fetch_params=fetch_params, user=user
if not (
fetch_params := service.get_fetch_parameters(
user=user, use_vestigingsnummer=True
)
if not partij:
logger.error("Failed to create partij for new user %s", user)
return
elif not created:
service.update_user_from_partij(partij_uuid=partij["uuid"], user=user)
):
return

partij, created = service.get_or_create_partij_for_user(
fetch_params=fetch_params, user=user
)
if not partij:
logger.error("Failed to create partij for new user %s", user)
return
elif not created:
service.update_user_from_partij(partij_uuid=partij["uuid"], user=user)

logger.info("Created partij %s for new user %s", partij, user)
logger.info("Created partij %s for new user %s", partij, user)

# eSuite
try:
service = eSuiteKlantenService()
except RuntimeError:
except Exception:
logger.error("eSuiteKlantenService failed to build")
return

Expand Down
2 changes: 1 addition & 1 deletion src/open_inwoner/cms/cases/tests/test_contactform.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class CasesContactFormTestCase(AssertMockMatchersMixin, ClearCachesMixin, WebTes
def setUp(self):
super().setUp()

self.user = DigidUserFactory(bsn="900222086")
self.user = DigidUserFactory(bsn="900222086", email="[email protected]")

# services
self.api_group = ZGWApiGroupConfigFactory(
Expand Down
14 changes: 4 additions & 10 deletions src/open_inwoner/cms/cases/views/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@

from django.conf import settings
from django.contrib import messages
from django.core.exceptions import (
ImproperlyConfigured,
ObjectDoesNotExist,
PermissionDenied,
)
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.http import (
Http404,
HttpRequest,
Expand Down Expand Up @@ -130,14 +126,12 @@ def get_service(self, service_type: KlantenServiceType) -> VragenService | None:
if service_type == KlantenServiceType.OPENKLANT2:
try:
return OpenKlant2Service()
except ImproperlyConfigured:
logger.error("OpenKlant2 configuration missing")
except Exception:
logger.error("Failed to build OpenKlant2 service")
if service_type == KlantenServiceType.ESUITE:
try:
return eSuiteVragenService()
except ImproperlyConfigured:
logger.error("eSuiteVragenService configuration missing")
except RuntimeError:
except Exception:
logger.error("Failed to build eSuiteVragenService")

def store_statustype_mapping(self, zaaktype_identificatie):
Expand Down
75 changes: 75 additions & 0 deletions src/open_inwoner/openklant/migrations/0015_openklant2config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Generated by Django 4.2.16 on 2024-12-18 13:21

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("zgw_consumers", "0022_set_default_service_slug"),
("openklant", "0014_contactformconfig"),
]

operations = [
migrations.CreateModel(
name="OpenKlant2Config",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"mijn_vragen_kanaal",
models.CharField(
blank=True, default="", verbose_name="Mijn vragen kanaal"
),
),
(
"mijn_vragen_organisatie_naam",
models.CharField(
blank=True,
default="",
verbose_name="Mijn vragen organisatie naam",
),
),
(
"mijn_vragen_actor",
models.CharField(
blank=True, default="", verbose_name="Mijn vragen actor"
),
),
(
"interne_taak_gevraagde_handeling",
models.CharField(
blank=True,
default="",
verbose_name="Interne taak gevraagde handeling",
),
),
(
"interne_taak_toelichting",
models.CharField(
blank=True, default="", verbose_name="Interne taak toelichting"
),
),
(
"service",
models.OneToOneField(
on_delete=django.db.models.deletion.PROTECT,
related_name="+",
to="zgw_consumers.service",
verbose_name="Klanten API",
),
),
],
options={
"verbose_name": "OpenKlant2 configuration",
},
),
]
85 changes: 45 additions & 40 deletions src/open_inwoner/openklant/models.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
import uuid
from dataclasses import dataclass
from typing import Self
from urllib.parse import urljoin, urlparse, urlunparse

from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.utils.translation import gettext_lazy as _

Expand Down Expand Up @@ -138,6 +132,51 @@ def has_api_configuration(self):
return all(getattr(self, f, "") for f in self.register_api_required_fields)


class OpenKlant2ConfigManager(models.Manager):
def get_queryset(self):
qs = super().get_queryset()
return qs.select_related("service")


class OpenKlant2Config(models.Model):
service = models.OneToOneField(
"zgw_consumers.Service",
verbose_name=_("Klanten API"),
on_delete=models.PROTECT,
related_name="+",
)

# Vragen
mijn_vragen_kanaal = models.CharField(
verbose_name=_("Mijn vragen kanaal"),
default="",
blank=True,
)
mijn_vragen_organisatie_naam = models.CharField(
verbose_name=_("Mijn vragen organisatie naam"),
default="",
blank=True,
)
mijn_vragen_actor = models.CharField(
verbose_name=_("Mijn vragen actor"),
default="",
blank=True,
)
interne_taak_gevraagde_handeling = models.CharField(
verbose_name=_("Interne taak gevraagde handeling"),
default="",
blank=True,
)
interne_taak_toelichting = models.CharField(
verbose_name=_("Interne taak toelichting"),
default="",
blank=True,
)

class Meta:
verbose_name = _("OpenKlant2 configuration")


class ContactFormSubject(OrderedModel):
subject = models.CharField(
verbose_name=_("Onderwerp"),
Expand Down Expand Up @@ -190,37 +229,3 @@ class Meta:
verbose_name = _("KlantContactMoment")
verbose_name_plural = _("KlantContactMomenten")
unique_together = [["user", "contactmoment_url"]]


@dataclass
class OpenKlant2Config:
api_root: str
api_path: str
api_token: str

# Question/Answer settings
mijn_vragen_kanaal: str
mijn_vragen_organisatie_naam: str
mijn_vragen_actor: str | uuid.UUID | None
interne_taak_gevraagde_handeling: str
interne_taak_toelichting: str

@property
def api_url(self):
joined = urljoin(self.api_root, self.api_path)
scheme, netloc, path, params, query, fragment = urlparse(joined)
path = path.replace("//", "/")
return urlunparse((scheme, netloc, path, params, query, fragment))

@classmethod
def from_django_settings(cls) -> Self:
from django.conf import settings

if not (config := getattr(settings, "OPENKLANT2_CONFIG", None)):
raise ImproperlyConfigured(
"Please set OPENKLANT2_CONFIG in your settings to configure OpenKlant2"
)

return cls(**config)

# TODO: add from_openklant_config_model or similar
17 changes: 9 additions & 8 deletions src/open_inwoner/openklant/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -823,16 +823,17 @@ class OpenKlant2Service(KlantenService):
client: OpenKlant2Client

def __init__(self, config: OpenKlant2Config | None = None):
self.config = config or OpenKlant2Config.from_django_settings()
if not self.config:
raise ImproperlyConfigured(
"Please set OPENKLANT2_CONFIG in your settings to configure OpenKlant2"
)
try:
self.config = config or OpenKlant2Config.objects.get()
except OpenKlant2Config.DoesNotExist:
raise ImproperlyConfigured("OpenKlant2Config not configured")
except OpenKlant2Config.MultipleObjectsReturned:
raise ImproperlyConfigured("Found multiple instances of OpenKlant2Config")

self.client = OpenKlant2Client(
base_url=self.config.api_url,
base_url=self.config.service.api_root,
request_kwargs={
"headers": {"Authorization": f"Token {self.config.api_token}"}
"headers": {"Authorization": f"Token {self.config.service.secret}"}
},
)

Expand Down Expand Up @@ -1133,7 +1134,7 @@ def create_question(
if len(question.rstrip()) == 0:
raise ValueError("You must provide a question")

if self.config.mijn_vragen_actor is None:
if not self.config.mijn_vragen_actor:
raise RuntimeError(
"You must define an actor to whom the question will be assigned. "
"Initialize the service with a value for `mijn_vragen_actor`."
Expand Down
1 change: 1 addition & 0 deletions src/open_inwoner/openklant/tests/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from open_inwoner.utils.test import paginated_response

KLANTEN_ROOT = "https://klanten.nl/api/v1/"
OPENKLANT2_ROOT = "http://localhost:8338/klantinteracties/api/v1"
CONTACTMOMENTEN_ROOT = "https://contactmomenten.nl/api/v1/"


Expand Down
Loading

0 comments on commit 2429bd2

Please sign in to comment.