Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ [open-zaak/open-notificaties#207] Update existing with register_kanalen #26

Merged
merged 2 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 78 additions & 41 deletions notifications_api_common/management/commands/register_kanalen.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
from typing import Optional

from django.contrib.sites.models import Site
from django.core.management.base import BaseCommand
from django.core.management.base import BaseCommand, CommandError
from django.urls import reverse

from ape_pie.client import APIClient
from requests import Response
from requests.exceptions import JSONDecodeError, RequestException
from zgw_consumers.models import Service

from ...kanalen import KANAAL_REGISTRY
from ...models import NotificationsConfig
Expand All @@ -19,59 +19,63 @@
class KanaalException(Exception):
kanaal: str
data: dict | list
service: Service
base_url: str

def __init__(
self, kanaal: str, service: Service, data: Optional[dict | list] = None
):
def __init__(self, kanaal: str, base_url: str, data: Optional[dict | list] = None):
super().__init__()

self.kanaal = kanaal
self.service = service
self.base_url = base_url
self.data = data or {}


class KanaalRequestException(KanaalException):
def __str__(self) -> str:
return (
f"Unable to retrieve kanaal {self.kanaal} from {self.service}: {self.data}"
f"Unable to retrieve kanaal {self.kanaal} from {self.base_url}: {self.data}"
)


class KanaalCreateException(KanaalException):
def __str__(self) -> str:
return f"Unable to create kanaal {self.kanaal} at {self.service}: {self.data}"
return f"Unable to create kanaal {self.kanaal} at {self.base_url}: {self.data}"


class KanaalExistsException(KanaalException):
class KanaalUpdateException(KanaalException):
def __str__(self) -> str:
return f"Kanaal '{self.kanaal}' already exists within {self.service}"

return f"Unable to update kanaal {self.kanaal} at {self.base_url}: {self.data}"

def create_kanaal(kanaal: str, service: Service) -> None:
"""
Create a kanaal, if it doesn't exist yet.
"""
client = NotificationsConfig.get_client()

assert client
class KanaalExistsException(KanaalException):
def __str__(self) -> str:
return f"Kanaal '{self.kanaal}' already exists within {self.base_url}"

# look up the exchange in the registry
_kanaal = next(k for k in KANAAL_REGISTRY if k.label == kanaal)

def get_kanaal(kanaal: str, client: APIClient) -> dict | None:
response_data = []

try:
response: Response = client.get("kanaal", params={"naam": kanaal})
kanalen: list[dict] = response.json() or []
kanalen: list = response.json()
response.raise_for_status()
except (RequestException, JSONDecodeError) as exception:
raise KanaalRequestException(
kanaal=kanaal, service=service, data=response_data
kanaal=kanaal, base_url=client.base_url, data=response_data
) from exception
else:
if kanalen:
# `Kanaal.naam` is unique in Open Notificaties, so there should only be one
if len(kanalen) > 1:
logger.error(
"Found more than one Kanaal with naam %s, this should not be possible",
kanaal,
)
return kanalen[0]
return None


if kanalen:
raise KanaalExistsException(kanaal=kanaal, service=service, data=response_data)
def construct_kanaal_request_data(kanaal: str):
_kanaal = next(k for k in KANAAL_REGISTRY if k.label == kanaal)

# build up own documentation URL
domain = Site.objects.get_current().domain
Expand All @@ -80,24 +84,50 @@ def create_kanaal(kanaal: str, service: Service) -> None:
f"{protocol}://{domain}{reverse('notifications:kanalen')}#{kanaal}"
)

data = {
"naam": kanaal,
"documentatieLink": documentation_url,
"filters": list(_kanaal.kenmerken),
}
return data


def create_kanaal(kanaal: str, client) -> None:
"""
Create a kanaal, if it doesn't exist yet.
"""
data = construct_kanaal_request_data(kanaal)

response_data = {}
try:
response: Response = client.post(
"kanaal",
json={
"naam": kanaal,
"documentatieLink": documentation_url,
"filters": list(_kanaal.kenmerken),
},
)
response: Response = client.post("kanaal", json=data)

response_data: dict = response.json() or {}
response_data: dict = response.json()
response.raise_for_status()
except (RequestException, JSONDecodeError) as exception:
raise KanaalCreateException(
kanaal=kanaal, service=service, data=response_data
kanaal=kanaal, base_url=client.base_url, data=response_data
) from exception


def replace_kanaal(kanaal: str, existing_kanaal: dict, client: APIClient) -> None:
"""
Fully update a kanaal, if it doesn't exist yet.
"""
data = construct_kanaal_request_data(kanaal)

response_data = {}
try:
response: Response = client.put(existing_kanaal["url"], json=data)
response_data: dict = response.json()
response.raise_for_status()
except (RequestException, JSONDecodeError) as exception:
raise KanaalUpdateException(
kanaal=kanaal, base_url=client.base_url, data=response_data
) from exception
return


class Command(BaseCommand):
help = "Create kanaal in notification component"

Expand All @@ -115,23 +145,30 @@ def handle(self, **options):
config = NotificationsConfig.get_solo()

if not config.notifications_api_service:
self.stderr.write(
raise CommandError(
"NotificationsConfig does not have a "
"`notifications_api_service` configured"
)

service = config.notifications_api_service

# use CLI arg or fall back to setting
kanalen = options["kanalen"] or sorted(
[kanaal.label for kanaal in KANAAL_REGISTRY]
)

client = NotificationsConfig.get_client()
assert client
stevenbal marked this conversation as resolved.
Show resolved Hide resolved

for kanaal in kanalen:
try:
create_kanaal(kanaal, service)
self.stdout.write(
f"Registered kanaal '{kanaal}' with {service.api_root}"
)
if existing_kanaal := get_kanaal(kanaal, client):
replace_kanaal(kanaal, existing_kanaal, client)
self.stdout.write(
f"Updated already existing kanaal '{kanaal}' with {client.base_url}"
)
else:
create_kanaal(kanaal, client)
self.stdout.write(
f"Registered kanaal '{kanaal}' with {client.base_url}"
)
except (KanaalException,) as exception:
self.stderr.write(f"{str(exception)} . Skipping..")
15 changes: 14 additions & 1 deletion testapp/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
from django.contrib import admin
from django.urls import include, path
from django.views import View

from .api import router

urlpatterns = [path("admin/", admin.site.urls), path("api/", include(router.urls))]
notifications_patterns = [
path("kanalen/", View.as_view(), name="kanalen"),
]


urlpatterns = [
path("admin/", admin.site.urls),
path("api/", include(router.urls)),
path(
"notifications/",
include((notifications_patterns, "notifications"), namespace="notifications"),
),
]
Loading
Loading