diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb75489..038daa6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,14 +15,14 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['3.8', '3.9', '3.10'] - django: ['3.2', '4.1', '4.2'] + python: ['3.10', '3.11', '3.12'] + django: ['4.2'] name: Run the test suite (Python ${{ matrix.python }}, Django ${{ matrix.django }}) services: postgres: - image: docker.io/library/postgres:12 + image: docker.io/library/postgres:14 env: POSTGRES_HOST_AUTH_METHOD: trust ports: @@ -61,7 +61,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.10' - name: Build sdist and wheel run: | diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml index fe2eee8..f90a67d 100644 --- a/.github/workflows/code_quality.yml +++ b/.github/workflows/code_quality.yml @@ -27,7 +27,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: '3.9' + python-version: '3.10' - name: Install dependencies run: pip install tox - run: tox diff --git a/README.rst b/README.rst index 69b26ac..3f80b95 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ notifications-api-common :Version: 0.2.2 :Source: https://github.com/maykinmedia/notifications-api-common :Keywords: notifications, REST, API, Common Ground, ZGW -:PythonVersion: 3.9 +:PythonVersion: 3.10 |build-status| |code-quality| |black| |coverage| |docs| @@ -29,9 +29,9 @@ Installation Requirements ------------ -* Python 3.8 or above +* Python 3.10 or above * setuptools 30.3.0 or above -* Django 3.2 or newer +* Django 4.2 or newer * Celery 5.0 or newer setup with one worker deployed diff --git a/notifications_api_common/admin.py b/notifications_api_common/admin.py index de33ffa..5a36adf 100644 --- a/notifications_api_common/admin.py +++ b/notifications_api_common/admin.py @@ -3,7 +3,6 @@ from requests.exceptions import RequestException from solo.admin import SingletonModelAdmin -from zds_client.client import ClientError from .models import NotificationsConfig, Subscription @@ -20,7 +19,7 @@ def register_webhook(modeladmin, request, queryset): try: sub.register() - except (ClientError, RequestException) as e: + except RequestException as e: messages.error( request, _( diff --git a/notifications_api_common/kanalen.py b/notifications_api_common/kanalen.py index c89d9eb..e834592 100644 --- a/notifications_api_common/kanalen.py +++ b/notifications_api_common/kanalen.py @@ -1,6 +1,7 @@ """ Provide notifications kanaal/exchange classes. """ + from collections import defaultdict from typing import Dict, Tuple diff --git a/notifications_api_common/management/commands/register_kanalen.py b/notifications_api_common/management/commands/register_kanalen.py index 54c7bf3..4b65e36 100644 --- a/notifications_api_common/management/commands/register_kanalen.py +++ b/notifications_api_common/management/commands/register_kanalen.py @@ -21,10 +21,12 @@ def create_kanaal(kanaal: str) -> None: """ client = NotificationsConfig.get_client() + assert client + # look up the exchange in the registry _kanaal = next(k for k in KANAAL_REGISTRY if k.label == kanaal) - kanalen = client.list("kanaal", query_params={"naam": kanaal}) + kanalen = client.get("kanaal", params={"naam": kanaal}) if kanalen: raise KanaalExists() @@ -35,9 +37,9 @@ def create_kanaal(kanaal: str) -> None: f"{protocol}://{domain}{reverse('notifications:kanalen')}#{kanaal}" ) - client.create( + client.post( "kanaal", - { + json={ "naam": kanaal, "documentatieLink": documentation_url, "filters": list(_kanaal.kenmerken), diff --git a/notifications_api_common/models.py b/notifications_api_common/models.py index 7694601..0c5f177 100644 --- a/notifications_api_common/models.py +++ b/notifications_api_common/models.py @@ -4,8 +4,9 @@ from django.db import models from django.utils.translation import gettext_lazy as _ +from ape_pie.client import APIClient from solo.models import SingletonModel -from zds_client import Client, ClientAuth +from zgw_consumers.client import ZGWAuth, build_client from zgw_consumers.constants import APITypes from zgw_consumers.models import Service @@ -56,13 +57,15 @@ def __str__(self): ) @classmethod - def get_client(cls) -> Optional[Client]: + def get_client(cls) -> Optional[APIClient]: """ Construct a client, prepared with the required auth. """ config = cls.get_solo() if config.notifications_api_service: - return config.notifications_api_service.build_client() + return build_client( + config.notifications_api_service, client_factory=APIClient + ) return None @@ -110,23 +113,20 @@ def register(self) -> None: """ Registers the webhook with the notification component. """ - assert ( - NotificationsConfig.get_solo().notifications_api_service - ), "No service for Notifications API configured" + service = NotificationsConfig.get_solo().notifications_api_service + assert service, "No service for Notifications API configured" client = NotificationsConfig.get_client() + assert client # This authentication is for the NC to call us. Thus, it's *not* for # calling the NC to create a subscription. # TODO should be replaced with `TokenAuth` # see: maykinmedia/notifications-api-common/pull/1#discussion_r941450384 - self_auth = ClientAuth( - client_id=self.client_id, - secret=self.secret, - ) + self_auth = ZGWAuth(service) data = { "callbackUrl": self.callback_url, - "auth": self_auth.credentials()["Authorization"], + "auth": f"Bearer {self_auth._token}", "kanalen": [ { "naam": channel, @@ -138,7 +138,7 @@ def register(self) -> None: } # register the subscriber - subscriber = client.create("abonnement", data=data) + subscriber = client.post("abonnement", json=data).json() self._subscription = subscriber["url"] self.save(update_fields=["_subscription"]) diff --git a/notifications_api_common/tasks.py b/notifications_api_common/tasks.py index 65c11b6..ff1f918 100644 --- a/notifications_api_common/tasks.py +++ b/notifications_api_common/tasks.py @@ -2,7 +2,6 @@ import requests from celery import shared_task -from zds_client import ClientError from .autoretry import add_autoretry_behaviour from .models import NotificationsConfig @@ -27,13 +26,14 @@ def send_notification(self, message: dict) -> None: return try: - client.create("notificaties", message) + response = client.post("notificaties", json=message) + response.raise_for_status() # any unexpected errors should show up in error-monitoring, so we only - # catch ClientError exceptions - except (ClientError, requests.HTTPError) as exc: + # catch HTTPError exceptions + except requests.HTTPError as exc: logger.warning( "Could not deliver message to %s", - client.api_root, + client.base_url, exc_info=exc, extra={ "notification_msg": message, diff --git a/setup.cfg b/setup.cfg index f3320d2..df8b1b9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,16 +17,14 @@ keywords = notifications, REST, API, Common Ground, ZGW classifiers = Development Status :: 4 - Beta Framework :: Django - Framework :: Django :: 3.2 - Framework :: Django :: 4.1 Framework :: Django :: 4.2 Intended Audience :: Developers Operating System :: Unix Operating System :: MacOS Operating System :: Microsoft :: Windows - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Topic :: Software Development :: Libraries :: Python Modules [options] @@ -34,13 +32,13 @@ zip_safe = False include_package_data = True packages = find: install_requires = - django>=3.2.0 + django>=4.2.0 django-solo djangorestframework>=3.12.0 celery djangorestframework_camel_case>=1.2.0 - gemma-zds-client>=0.15.0 - zgw-consumers + zgw-consumers>=0.35.1 + ape-pie tests_require = psycopg2 pytest @@ -50,6 +48,7 @@ tests_require = isort black flake8 + zgw-consumers[testutils]>=0.35.1 [options.packages.find] include = @@ -68,6 +67,7 @@ tests = isort black flake8 + zgw-consumers[testutils]>=0.35.1 pep8 = flake8 coverage = pytest-cov docs = diff --git a/testapp/settings.py b/testapp/settings.py index b9ef8c8..cad3247 100644 --- a/testapp/settings.py +++ b/testapp/settings.py @@ -2,6 +2,8 @@ DEBUG = True +USE_TZ = False + BASE_DIR = os.path.abspath(os.path.dirname(__file__)) SECRET_KEY = "so-secret-i-cant-believe-you-are-looking-at-this" @@ -62,5 +64,3 @@ ] ROOT_URLCONF = "testapp.urls" - -ZGW_CONSUMERS_CLIENT_CLASS = "zgw_consumers.client.ZGWClient" diff --git a/tests/conftest.py b/tests/conftest.py index bfe9184..ac7ee5b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ import pytest from rest_framework.test import APIClient from zgw_consumers.constants import APITypes -from zgw_consumers.service import Service +from zgw_consumers.models import Service from notifications_api_common.models import NotificationsConfig from testapp import urls # noqa diff --git a/tests/schemas/nrc.yaml b/tests/schemas/nrc.yaml deleted file mode 100644 index b9ad57f..0000000 --- a/tests/schemas/nrc.yaml +++ /dev/null @@ -1,932 +0,0 @@ -openapi: 3.0.0 -info: - title: Open Notificaties API - description: | - Een API om een notificatierouteringscomponent te benaderen. - - Deze API voorziet in drie functionaliteiten voor notificaties: - - * registreren van kanalen (=exchanges) - * abonneren van consumers op kanalen - * ontvangen en routeren van berichten - - **Registreren van kanalen** - - Een component dekt een bepaald domein af, en heeft het recht om hiervoor een - kanaal te registeren waarop eigen notificaties verstuurd worden. Een kanaal - is uniek in naam. Een component dient dus te controleren of een kanaal al - bestaat voor het registreren. Bij het registeren van kanalen wordt een - documentatielink verwacht die beschrijft welke events en kenmerken van - toepassing zijn op het kanaal. - - **Abonneren** - - Consumers kunnen een abonnement aanmaken voor een of meerdere kanalen. Per - kanaal kan op de kenmerken van het kanaal gefilterd worden. Consumers dienen - zelf een endpoint te bouwen waarop berichten afgeleverd (kunnen) worden. - - **Routeren van berichten** - - Bronnen sturen berichten naar deze API, die vervolgens de berichten onveranderd - routeert naar alle abonnees. - - **Afhankelijkheden** - - Deze API is afhankelijk van: - - * Autorisaties API - - - ### Autorisatie - - Deze API vereist autorisatie. - - _Zelf een token genereren_ - - De tokens die gebruikt worden voor autorisatie zijn [jwt.io][JWT's] (JSON web - token). In de API calls moeten deze gebruikt worden in de `Authorization` - header: - - ``` - Authorization: Bearer - ``` - - Om een JWT te genereren heb je een `client ID` en een `secret` nodig. Het JWT - moet gebouwd worden volgens het `HS256` algoritme. De vereiste payload is: - - ```json - { - "iss": "", - "iat": 1572863906, - "client_id": "", - "user_id": "", - "user_representation": "" - } - ``` - - Als `issuer` gebruik je dus je eigen client ID. De `iat` timestamp is een - UNIX-timestamp die aangeeft op welk moment het token gegenereerd is. - - `user_id` en `user_representation` zijn nodig voor de audit trails. Het zijn - vrije velden met als enige beperking dat de lengte maximaal de lengte van - de overeenkomstige velden in de audit trail resources is (zie rest API spec). - - - **Handige links** - - * [API-documentatie](https://vng-realisatie.github.io/gemma-zaken/standaard/) - * [Open Notificaties documentatie](https://open-notificaties.readthedocs.io/en/latest/) - * [Zaakgericht werken](https://www.vngrealisatie.nl/producten/api-standaarden-zaakgericht-werken) - * [Open Notificaties GitHub](https://github.com/open-zaak/open-notificaties) - contact: - url: https://www.maykinmedia.nl - email: support@maykinmedia.nl - license: - name: EUPL 1.2 - url: https://opensource.org/licenses/EUPL-1.2 - version: 1.0.0 -security: - - JWT-Claims: [] -paths: - /abonnement: - get: - operationId: abonnement_list - description: Alle ABONNEMENTen opvragen. - responses: - '200': - description: OK - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van - een specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Abonnement' - '401': - $ref: '#/components/responses/401' - '403': - $ref: '#/components/responses/403' - '406': - $ref: '#/components/responses/406' - '409': - $ref: '#/components/responses/409' - '410': - $ref: '#/components/responses/410' - '415': - $ref: '#/components/responses/415' - '429': - $ref: '#/components/responses/429' - '500': - $ref: '#/components/responses/500' - tags: - - abonnement - security: - - JWT-Claims: - - (notificaties.consumeren | notificaties.publiceren) - post: - operationId: abonnement_create - description: Maak een ABONNEMENT aan. - requestBody: - $ref: '#/components/requestBodies/Abonnement' - responses: - '201': - description: Created - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van - een specifieke aanroep. Voorbeeld: 1.2.1.' - Location: - schema: - type: string - format: uri - description: URL waar de resource leeft. - content: - application/json: - schema: - $ref: '#/components/schemas/Abonnement' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '403': - $ref: '#/components/responses/403' - '406': - $ref: '#/components/responses/406' - '409': - $ref: '#/components/responses/409' - '410': - $ref: '#/components/responses/410' - '415': - $ref: '#/components/responses/415' - '429': - $ref: '#/components/responses/429' - '500': - $ref: '#/components/responses/500' - tags: - - abonnement - security: - - JWT-Claims: - - notificaties.consumeren - parameters: [] - /abonnement/{uuid}: - get: - operationId: abonnement_read - description: Een specifiek ABONNEMENT opvragen. - responses: - '200': - description: OK - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van - een specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/json: - schema: - $ref: '#/components/schemas/Abonnement' - '401': - $ref: '#/components/responses/401' - '403': - $ref: '#/components/responses/403' - '404': - $ref: '#/components/responses/404' - '406': - $ref: '#/components/responses/406' - '409': - $ref: '#/components/responses/409' - '410': - $ref: '#/components/responses/410' - '415': - $ref: '#/components/responses/415' - '429': - $ref: '#/components/responses/429' - '500': - $ref: '#/components/responses/500' - tags: - - abonnement - security: - - JWT-Claims: - - (notificaties.consumeren | notificaties.publiceren) - put: - operationId: abonnement_update - description: Werk een ABONNEMENT in zijn geheel bij. - requestBody: - $ref: '#/components/requestBodies/Abonnement' - responses: - '200': - description: OK - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van - een specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/json: - schema: - $ref: '#/components/schemas/Abonnement' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '403': - $ref: '#/components/responses/403' - '404': - $ref: '#/components/responses/404' - '406': - $ref: '#/components/responses/406' - '409': - $ref: '#/components/responses/409' - '410': - $ref: '#/components/responses/410' - '415': - $ref: '#/components/responses/415' - '429': - $ref: '#/components/responses/429' - '500': - $ref: '#/components/responses/500' - tags: - - abonnement - security: - - JWT-Claims: - - notificaties.consumeren - patch: - operationId: abonnement_partial_update - description: Werk een ABONNEMENT deels bij. - requestBody: - $ref: '#/components/requestBodies/Abonnement' - responses: - '200': - description: OK - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van - een specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/json: - schema: - $ref: '#/components/schemas/Abonnement' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '403': - $ref: '#/components/responses/403' - '404': - $ref: '#/components/responses/404' - '406': - $ref: '#/components/responses/406' - '409': - $ref: '#/components/responses/409' - '410': - $ref: '#/components/responses/410' - '415': - $ref: '#/components/responses/415' - '429': - $ref: '#/components/responses/429' - '500': - $ref: '#/components/responses/500' - tags: - - abonnement - security: - - JWT-Claims: - - notificaties.consumeren - delete: - operationId: abonnement_delete - description: Verwijder een ABONNEMENT. - responses: - '204': - description: No content - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van - een specifieke aanroep. Voorbeeld: 1.2.1.' - '401': - $ref: '#/components/responses/401' - '403': - $ref: '#/components/responses/403' - '404': - $ref: '#/components/responses/404' - '406': - $ref: '#/components/responses/406' - '409': - $ref: '#/components/responses/409' - '410': - $ref: '#/components/responses/410' - '415': - $ref: '#/components/responses/415' - '429': - $ref: '#/components/responses/429' - '500': - $ref: '#/components/responses/500' - tags: - - abonnement - security: - - JWT-Claims: - - notificaties.consumeren - parameters: - - name: uuid - in: path - description: Unique resource identifier (UUID4) - required: true - schema: - type: string - format: uuid - /kanaal: - get: - operationId: kanaal_list - description: Alle KANAALen opvragen. - parameters: - - name: naam - in: query - description: Naam van het KANAAL (ook wel "Exchange" genoemd) dat de bron - vertegenwoordigd. - required: false - schema: - type: string - responses: - '200': - description: OK - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van - een specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Kanaal' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '403': - $ref: '#/components/responses/403' - '406': - $ref: '#/components/responses/406' - '409': - $ref: '#/components/responses/409' - '410': - $ref: '#/components/responses/410' - '415': - $ref: '#/components/responses/415' - '429': - $ref: '#/components/responses/429' - '500': - $ref: '#/components/responses/500' - tags: - - kanaal - security: - - JWT-Claims: - - (notificaties.publiceren | notificaties.consumeren) - post: - operationId: kanaal_create - description: Maak een KANAAL aan. - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Kanaal' - required: true - responses: - '201': - description: Created - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van - een specifieke aanroep. Voorbeeld: 1.2.1.' - Location: - schema: - type: string - format: uri - description: URL waar de resource leeft. - content: - application/json: - schema: - $ref: '#/components/schemas/Kanaal' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '403': - $ref: '#/components/responses/403' - '406': - $ref: '#/components/responses/406' - '409': - $ref: '#/components/responses/409' - '410': - $ref: '#/components/responses/410' - '415': - $ref: '#/components/responses/415' - '429': - $ref: '#/components/responses/429' - '500': - $ref: '#/components/responses/500' - tags: - - kanaal - security: - - JWT-Claims: - - notificaties.publiceren - parameters: [] - /kanaal/{uuid}: - get: - operationId: kanaal_read - description: Een specifiek KANAAL opvragen. - responses: - '200': - description: OK - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van - een specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/json: - schema: - $ref: '#/components/schemas/Kanaal' - '401': - $ref: '#/components/responses/401' - '403': - $ref: '#/components/responses/403' - '404': - $ref: '#/components/responses/404' - '406': - $ref: '#/components/responses/406' - '409': - $ref: '#/components/responses/409' - '410': - $ref: '#/components/responses/410' - '415': - $ref: '#/components/responses/415' - '429': - $ref: '#/components/responses/429' - '500': - $ref: '#/components/responses/500' - tags: - - kanaal - security: - - JWT-Claims: - - (notificaties.publiceren | notificaties.consumeren) - parameters: - - name: uuid - in: path - description: Unique resource identifier (UUID4) - required: true - schema: - type: string - format: uuid - /notificaties: - post: - operationId: notificaties_create - description: Publiceer een notificatie. - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Message' - required: true - responses: - '200': - description: '' - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van - een specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/json: - schema: - $ref: '#/components/schemas/Message' - tags: - - notificaties - security: - - JWT-Claims: - - notificaties.publiceren - parameters: [] -servers: - - url: https://notificaties.test.openzaak.nl/api/v1 -components: - responses: - '400': - description: Bad request - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van een - specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/problem+json: - schema: - $ref: '#/components/schemas/ValidatieFout' - '401': - description: Unauthorized - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van een - specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/problem+json: - schema: - $ref: '#/components/schemas/Fout' - '403': - description: Forbidden - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van een - specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/problem+json: - schema: - $ref: '#/components/schemas/Fout' - '404': - description: Not found - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van een - specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/problem+json: - schema: - $ref: '#/components/schemas/Fout' - '406': - description: Not acceptable - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van een - specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/problem+json: - schema: - $ref: '#/components/schemas/Fout' - '409': - description: Conflict - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van een - specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/problem+json: - schema: - $ref: '#/components/schemas/Fout' - '410': - description: Gone - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van een - specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/problem+json: - schema: - $ref: '#/components/schemas/Fout' - '412': - description: Precondition failed - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van een - specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/problem+json: - schema: - $ref: '#/components/schemas/Fout' - '415': - description: Unsupported media type - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van een - specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/problem+json: - schema: - $ref: '#/components/schemas/Fout' - '429': - description: Too many requests - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van een - specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/problem+json: - schema: - $ref: '#/components/schemas/Fout' - '500': - description: Internal server error - headers: - API-version: - schema: - type: string - description: 'Geeft een specifieke API-versie aan in de context van een - specifieke aanroep. Voorbeeld: 1.2.1.' - content: - application/problem+json: - schema: - $ref: '#/components/schemas/Fout' - requestBodies: - Abonnement: - content: - application/json: - schema: - $ref: '#/components/schemas/Abonnement' - required: true - securitySchemes: - JWT-Claims: - type: http - scheme: bearer - bearerFormat: JWT - schemas: - FilterGroup: - description: Een lijst van kanalen en filters waarop het ABONNEMENT wordt afgenomen. - required: - - naam - type: object - properties: - filters: - title: Filters - description: Map van kenmerken (sleutel/waarde) waarop notificaties gefilterd - worden. Alleen notificaties waarvan de kenmerken voldoen aan het filter - worden doorgestuurd naar de afnemer van het ABONNEMENT. - type: object - additionalProperties: - title: kenmerk - description: Een waarde behorende bij de sleutel. - type: string - maxLength: 1000 - minLength: 1 - naam: - title: Naam - description: De naam van het KANAAL (`KANAAL.naam`) waarop een abonnement - is of wordt genomen. - type: string - minLength: 1 - Abonnement: - required: - - callbackUrl - - auth - - kanalen - type: object - properties: - url: - title: Url - description: URL-referentie naar dit object. Dit is de unieke identificatie - en locatie van dit object. - type: string - format: uri - readOnly: true - maxLength: 1000 - minLength: 1 - callbackUrl: - title: Callback URL - description: De URL waar notificaties naar toe gestuurd dienen te worden. - Deze URL dient uit te komen bij een API die geschikt is om notificaties - op te ontvangen. - type: string - format: uri - maxLength: 200 - minLength: 1 - auth: - title: Autorisatie header - description: 'Autorisatie header invulling voor het vesturen naar de "Callback - URL". Voorbeeld: Bearer a4daa31...' - type: string - maxLength: 1000 - minLength: 1 - kanalen: - description: Een lijst van kanalen en filters waarop het ABONNEMENT wordt - afgenomen. - type: array - items: - $ref: '#/components/schemas/FilterGroup' - Fout: - required: - - code - - title - - status - - detail - - instance - type: object - properties: - type: - title: Type - description: URI referentie naar het type fout, bedoeld voor developers - type: string - code: - title: Code - description: Systeemcode die het type fout aangeeft - type: string - minLength: 1 - title: - title: Title - description: Generieke titel voor het type fout - type: string - minLength: 1 - status: - title: Status - description: De HTTP status code - type: integer - detail: - title: Detail - description: Extra informatie bij de fout, indien beschikbaar - type: string - minLength: 1 - instance: - title: Instance - description: URI met referentie naar dit specifiek voorkomen van de fout. - Deze kan gebruikt worden in combinatie met server logs, bijvoorbeeld. - type: string - minLength: 1 - FieldValidationError: - required: - - name - - code - - reason - type: object - properties: - name: - title: Name - description: Naam van het veld met ongeldige gegevens - type: string - minLength: 1 - code: - title: Code - description: Systeemcode die het type fout aangeeft - type: string - minLength: 1 - reason: - title: Reason - description: Uitleg wat er precies fout is met de gegevens - type: string - minLength: 1 - ValidatieFout: - required: - - code - - title - - status - - detail - - instance - - invalidParams - type: object - properties: - type: - title: Type - description: URI referentie naar het type fout, bedoeld voor developers - type: string - code: - title: Code - description: Systeemcode die het type fout aangeeft - type: string - minLength: 1 - title: - title: Title - description: Generieke titel voor het type fout - type: string - minLength: 1 - status: - title: Status - description: De HTTP status code - type: integer - detail: - title: Detail - description: Extra informatie bij de fout, indien beschikbaar - type: string - minLength: 1 - instance: - title: Instance - description: URI met referentie naar dit specifiek voorkomen van de fout. - Deze kan gebruikt worden in combinatie met server logs, bijvoorbeeld. - type: string - minLength: 1 - invalidParams: - type: array - items: - $ref: '#/components/schemas/FieldValidationError' - Kanaal: - required: - - naam - type: object - properties: - url: - title: Url - description: URL-referentie naar dit object. Dit is de unieke identificatie - en locatie van dit object. - type: string - format: uri - readOnly: true - maxLength: 1000 - minLength: 1 - naam: - title: Naam - description: Naam van het KANAAL (ook wel "Exchange" genoemd) dat de bron - vertegenwoordigd. - type: string - maxLength: 50 - minLength: 1 - documentatieLink: - title: Documentatie link - description: URL naar documentatie. - type: string - format: uri - maxLength: 200 - filters: - description: Lijst van mogelijke filter kenmerken van een KANAAL. Deze filter - kenmerken kunnen worden gebruikt bij het aanmaken van een ABONNEMENT. - type: array - items: - title: Filters - type: string - maxLength: 100 - minLength: 1 - Message: - required: - - kanaal - - hoofdObject - - resource - - resourceUrl - - actie - - aanmaakdatum - type: object - properties: - kanaal: - title: kanaal - description: De naam van het kanaal (`KANAAL.naam`) waar het bericht op - moet worden gepubliceerd. - type: string - maxLength: 50 - minLength: 1 - hoofdObject: - title: hoofd object - description: URL-referentie naar het hoofd object van de publicerende API - die betrekking heeft op de `resource`. - type: string - format: uri - minLength: 1 - resource: - title: resource - description: De resourcenaam waar de notificatie over gaat. - type: string - maxLength: 100 - minLength: 1 - resourceUrl: - title: resource URL - description: URL-referentie naar de `resource` van de publicerende API. - type: string - format: uri - minLength: 1 - actie: - title: actie - description: De actie die door de publicerende API is gedaan. De publicerende - API specificeert de toegestane acties. - type: string - maxLength: 100 - minLength: 1 - aanmaakdatum: - title: aanmaakdatum - description: Datum en tijd waarop de actie heeft plaatsgevonden. - type: string - format: date-time - kenmerken: - title: kenmerken - description: Mapping van kenmerken (sleutel/waarde) van de notificatie. - De publicerende API specificeert de toegestane kenmerken. - type: object - additionalProperties: - title: kenmerk - description: Een waarde behorende bij de sleutel. - type: string - maxLength: 1000 diff --git a/tests/test_register_webhook.py b/tests/test_register_webhook.py index 6f95763..346ac43 100644 --- a/tests/test_register_webhook.py +++ b/tests/test_register_webhook.py @@ -4,16 +4,24 @@ from django.utils.translation import gettext as _ import pytest -from requests.exceptions import RequestException -from zds_client.client import ClientError +from requests import Response +from requests.exceptions import HTTPError, RequestException from notifications_api_common.admin import register_webhook from notifications_api_common.models import Subscription +class MockResponse: + def __init__(self, data): + self.data = data + + def json(self): + return self.data + + @patch( - "zds_client.client.Client.create", - return_value={"url": "https://example.com/api/v1/abonnementen/1"}, + "ape_pie.APIClient.post", + return_value=MockResponse({"url": "https://example.com/api/v1/abonnementen/1"}), ) @pytest.mark.django_db def test_register_webhook_success( @@ -47,9 +55,7 @@ def test_register_webhook_request_exception( channels=["zaken"], ) - with patch( - "zds_client.client.Client.create", side_effect=RequestException("exception") - ): + with patch("ape_pie.APIClient.post", side_effect=RequestException("exception")): register_webhook(object, request_with_middleware, Subscription.objects.all()) messages = list(get_messages(request_with_middleware)) @@ -61,7 +67,7 @@ def test_register_webhook_request_exception( @pytest.mark.django_db -def test_register_webhook_client_error(request_with_middleware, notifications_config): +def test_register_webhook_http_error(request_with_middleware, notifications_config): Subscription.objects.create( callback_url="https://example.com/callback", client_id="client_id", @@ -69,7 +75,7 @@ def test_register_webhook_client_error(request_with_middleware, notifications_co channels=["zaken"], ) - with patch("zds_client.client.Client.create", side_effect=ClientError("exception")): + with patch("ape_pie.APIClient.post", side_effect=HTTPError("400")): register_webhook(object, request_with_middleware, Subscription.objects.all()) messages = list(get_messages(request_with_middleware)) @@ -77,4 +83,4 @@ def test_register_webhook_client_error(request_with_middleware, notifications_co assert len(messages) == 1 assert messages[0].message == _( "Something went wrong while registering subscription for {callback_url}: {e}" - ).format(callback_url="https://example.com/callback", e="exception") + ).format(callback_url="https://example.com/callback", e="400") diff --git a/tests/test_send_message.py b/tests/test_send_message.py index d91666f..acdb1b4 100644 --- a/tests/test_send_message.py +++ b/tests/test_send_message.py @@ -7,7 +7,6 @@ import pytest from celery.exceptions import Retry from freezegun import freeze_time -from zgw_consumers.test import mock_service_oas_get from notifications_api_common.tasks import NotificationException, send_notification from testapp.models import Person @@ -51,9 +50,6 @@ def test_api_create_person(api_client, notifications_config): @pytest.mark.django_db() def test_task_send_notification_success(notifications_config, requests_mock, settings): msg = {"foo": "bar"} - # add mocks - settings.ZGW_CONSUMERS_TEST_SCHEMA_DIRS = [TESTS_DIR / "schemas"] - mock_service_oas_get(requests_mock, NOTIFICATIONS_API_ROOT, "nrc") requests_mock.post(f"{NOTIFICATIONS_API_ROOT}notificaties", status_code=201) send_notification(msg) @@ -69,10 +65,6 @@ def test_task_send_notification_with_retry( notifications_config, requests_mock, settings ): msg = {"foo": "bar"} - - # add NRC mocks - settings.ZGW_CONSUMERS_TEST_SCHEMA_DIRS = [TESTS_DIR / "schemas"] - mock_service_oas_get(requests_mock, NOTIFICATIONS_API_ROOT, "nrc") exc = NotificationException() requests_mock.post(f"{NOTIFICATIONS_API_ROOT}notificaties", exc=exc) @@ -94,8 +86,6 @@ def test_task_send_notification_catch_404( which can be handled by the lib users """ # add NRC mocks - settings.ZGW_CONSUMERS_TEST_SCHEMA_DIRS = [TESTS_DIR / "schemas"] - mock_service_oas_get(requests_mock, NOTIFICATIONS_API_ROOT, "nrc") requests_mock.post(f"{NOTIFICATIONS_API_ROOT}notificaties", status_code=404) with pytest.raises(NotificationException): @@ -111,8 +101,6 @@ def test_task_send_notification_catch_500( which can be handled by the lib users """ # add NRC mocks - settings.ZGW_CONSUMERS_TEST_SCHEMA_DIRS = [TESTS_DIR / "schemas"] - mock_service_oas_get(requests_mock, NOTIFICATIONS_API_ROOT, "nrc") requests_mock.post(f"{NOTIFICATIONS_API_ROOT}notificaties", status_code=500) with pytest.raises(NotificationException): @@ -136,7 +124,9 @@ def test_api_create_person_unconfigured(api_client, notifications_config): @freeze_time("2022-01-01") @pytest.mark.django_db(transaction=True) @override_settings(NOTIFICATIONS_GUARANTEE_DELIVERY=False) -def test_api_create_person_unconfigured(api_client, notifications_config): +def test_api_create_person_unconfigured_notifications_guarantee_delivery_false( + api_client, notifications_config +): notifications_config.delete() url = reverse("person-list") data = {"name": "John", "address_street": "Grotestraat", "address_number": "1"} diff --git a/tox.ini b/tox.ini index 3d956dc..d362bb4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ [tox] envlist = - py{38,39,310}-django32 - py{38,39,310}-django{41,42} + py{310,311,312}-django{42} isort black docs @@ -9,14 +8,12 @@ skip_missing_interpreters = true [gh-actions] python = - 3.8: py38 - 3.9: py39 3.10: py310 + 3.11: py311 + 3.12: py312 [gh-actions:env] DJANGO = - 3.2: django32 - 4.1: django41 4.2: django42 [testenv] @@ -24,8 +21,6 @@ extras = tests coverage deps = - django32: Django~=3.2.0 - django41: Django~=4.1.0 django42: Django~=4.2.0 passenv = PGUSER