commonground-api-common 1.13.3__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. {commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/METADATA +22 -23
  2. {commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/RECORD +30 -27
  3. {commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/WHEEL +1 -1
  4. vng_api_common/__init__.py +1 -1
  5. vng_api_common/admin.py +1 -20
  6. vng_api_common/authorizations/admin.py +1 -1
  7. vng_api_common/authorizations/middleware.py +244 -0
  8. vng_api_common/authorizations/migrations/0016_remove_authorizationsconfig_api_root_and_more.py +76 -0
  9. vng_api_common/authorizations/models.py +38 -3
  10. vng_api_common/authorizations/utils.py +17 -0
  11. vng_api_common/client.py +56 -27
  12. vng_api_common/conf/api.py +0 -3
  13. vng_api_common/management/commands/generate_swagger.py +1 -1
  14. vng_api_common/middleware.py +1 -227
  15. vng_api_common/migrations/0006_delete_apicredential.py +119 -0
  16. vng_api_common/mocks.py +4 -1
  17. vng_api_common/models.py +0 -111
  18. vng_api_common/notifications/api/views.py +1 -1
  19. vng_api_common/notifications/handlers.py +8 -3
  20. vng_api_common/notifications/migrations/0011_remove_subscription_config_and_more.py +23 -0
  21. vng_api_common/oas.py +0 -3
  22. vng_api_common/routers.py +3 -3
  23. vng_api_common/tests/schema.py +12 -0
  24. vng_api_common/utils.py +0 -22
  25. vng_api_common/validators.py +0 -38
  26. vng_api_common/views.py +26 -22
  27. vng_api_common/notifications/constants.py +0 -3
  28. vng_api_common/notifications/models.py +0 -97
  29. {commonground_api_common-1.13.3.data → commonground_api_common-2.0.0.data}/scripts/generate_schema +0 -0
  30. {commonground_api_common-1.13.3.data → commonground_api_common-2.0.0.data}/scripts/patch_content_types +0 -0
  31. {commonground_api_common-1.13.3.data → commonground_api_common-2.0.0.data}/scripts/use_external_components +0 -0
  32. {commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,17 @@
1
+ def generate_jwt(client_id, secret, user_id, user_representation):
2
+ from zgw_consumers.client import ZGWAuth
3
+
4
+ class FakeService:
5
+ def __init__(self, **kwargs):
6
+ for key, value in kwargs.items():
7
+ setattr(self, key, value)
8
+
9
+ auth = ZGWAuth(
10
+ service=FakeService( # type: ignore
11
+ client_id=client_id,
12
+ secret=secret,
13
+ user_id=user_id,
14
+ user_representation=user_representation,
15
+ )
16
+ )
17
+ return f"Bearer {auth._token}"
vng_api_common/client.py CHANGED
@@ -1,44 +1,73 @@
1
1
  """
2
- Interface to get a zds_client object for a given URL.
2
+ Interface to get a client object for a given URL.
3
3
  """
4
4
 
5
- from typing import Optional
5
+ import logging
6
+ from typing import Any, Optional
6
7
 
7
- from django.apps import apps
8
8
  from django.conf import settings
9
9
  from django.utils.module_loading import import_string
10
10
 
11
- from zds_client import Client
11
+ from ape_pie import APIClient
12
+ from requests import JSONDecodeError, RequestException, Response
12
13
 
14
+ logger = logging.getLogger(__name__)
13
15
 
14
- def get_client(url: str, url_is_api_root=False) -> Optional[Client]:
15
- """
16
- Get a client instance for the given URL.
17
16
 
18
- If the setting CUSTOM_CLIENT_FETCHER is defined, then this callable is invoked.
19
- Otherwise we fall back on the default implementation.
17
+ class ClientError(RuntimeError):
18
+ pass
20
19
 
21
- If no suitable client is found, ``None`` is returned.
22
- """
23
- custom_client_fetcher = getattr(settings, "CUSTOM_CLIENT_FETCHER", None)
24
- if custom_client_fetcher:
25
- client_getter = import_string(custom_client_fetcher)
26
- return client_getter(url)
27
20
 
28
- # default implementation
29
- Client = import_string(settings.ZDS_CLIENT_CLASS)
21
+ # TODO: use more approriate method name
22
+ def to_internal_data(response: Response) -> dict | list | None:
23
+ try:
24
+ response_json = response.json()
25
+ except JSONDecodeError:
26
+ logger.exception("Unable to parse json from response")
27
+ response_json = None
30
28
 
31
- if url_is_api_root and not url.endswith("/"):
32
- url = f"{url}/"
29
+ try:
30
+ response.raise_for_status()
31
+ except RequestException as exc:
32
+ if response.status_code >= 500:
33
+ raise
34
+ raise ClientError(response_json if response_json is not None else {}) from exc
33
35
 
34
- client = Client.from_url(url)
35
- if client is None:
36
- return None
36
+ assert response_json
37
+ return response_json
38
+
39
+
40
+ class Client(APIClient):
41
+ def request(
42
+ self, method: str | bytes, url: str | bytes, *args, **kwargs
43
+ ) -> Response:
44
+
45
+ headers = kwargs.pop("headers", {})
46
+ headers.setdefault("Accept", "application/json")
47
+ headers.setdefault("Content-Type", "application/json")
48
+
49
+ kwargs["headers"] = headers
50
+
51
+ data = kwargs.get("data", {})
37
52
 
38
- APICredential = apps.get_model("vng_api_common", "APICredential")
53
+ if data:
54
+ kwargs["json"] = data
39
55
 
40
- if url_is_api_root:
41
- client.base_url = url
56
+ return super().request(method, url, *args, **kwargs)
57
+
58
+
59
+ def get_client(url: str) -> Client | None:
60
+ """
61
+ Get a client instance for the given URL.
62
+ If no suitable client is found, ``None`` is returned.
63
+ """
64
+ from zgw_consumers.client import build_client
65
+ from zgw_consumers.models import Service
66
+
67
+ service: Optional[Service] = Service.get_service(url)
68
+
69
+ if not service:
70
+ logger.warning(f"No service configured for {url}")
71
+ return None
42
72
 
43
- client.auth = APICredential.get_auth(url)
44
- return client
73
+ return build_client(service, client_factory=Client)
@@ -4,7 +4,6 @@ __all__ = [
4
4
  "BASE_SWAGGER_SETTINGS",
5
5
  "COMMON_SPEC",
6
6
  "LINK_FETCHER",
7
- "ZDS_CLIENT_CLASS",
8
7
  "GEMMA_URL_TEMPLATE",
9
8
  "GEMMA_URL_COMPONENTTYPE",
10
9
  "GEMMA_URL_INFORMATIEMODEL",
@@ -94,8 +93,6 @@ REDOC_SETTINGS = {"EXPAND_RESPONSES": "200,201", "SPEC_URL": "openapi.json"}
94
93
  # See: https://github.com/Rebilly/ReDoc#redoc-options-object
95
94
  LINK_FETCHER = "requests.get"
96
95
 
97
- ZDS_CLIENT_CLASS = "zds_client.Client"
98
-
99
96
  GEMMA_URL_TEMPLATE = "https://www.gemmaonline.nl/index.php/{informatiemodel}_{versie}/doc/{componenttype}/{component}"
100
97
  GEMMA_URL_COMPONENTTYPE = "objecttype"
101
98
  GEMMA_URL_INFORMATIEMODEL = "Rgbz"
@@ -47,7 +47,7 @@ class Row:
47
47
 
48
48
  class Command(generate_swagger.Command):
49
49
  """
50
- Patches to the provided command to modify the schema for ZDS needs.
50
+ Patches to the provided command to modify the schema.
51
51
  """
52
52
 
53
53
  leave_locale_alone = True
@@ -1,242 +1,16 @@
1
1
  # https://pyjwt.readthedocs.io/en/latest/usage.html#reading-headers-without-validation
2
2
  # -> we can put the organization/service in the headers itself
3
3
  import logging
4
- from typing import Any, Dict, Iterable, List, Optional
5
4
 
6
5
  from django.conf import settings
7
- from django.db import models, transaction
8
- from django.db.models import QuerySet
9
- from django.utils.translation import gettext as _
10
6
 
11
- import jwt
12
- from djangorestframework_camel_case.util import underscoreize
13
- from rest_framework.exceptions import PermissionDenied
14
7
  from rest_framework.response import Response
15
- from zds_client.client import ClientError
16
8
 
17
- from .authorizations.models import Applicatie, AuthorizationsConfig, Autorisatie
18
- from .authorizations.serializers import ApplicatieUuidSerializer
19
- from .constants import VERSION_HEADER, VertrouwelijkheidsAanduiding
20
- from .models import JWTSecret
21
- from .utils import get_uuid_from_path
9
+ from .constants import VERSION_HEADER
22
10
 
23
11
  logger = logging.getLogger(__name__)
24
12
 
25
13
 
26
- class JWTAuth:
27
- def __init__(self, encoded: str = None):
28
- self.encoded = encoded
29
-
30
- @property
31
- def applicaties(self) -> Iterable[Applicatie]:
32
- if self.client_id is None:
33
- return []
34
-
35
- applicaties = self._get_auth()
36
-
37
- if not applicaties:
38
- auth_data = self._request_auth()
39
- applicaties = self._save_auth(auth_data)
40
-
41
- return applicaties
42
-
43
- @property
44
- def autorisaties(self) -> models.QuerySet:
45
- """
46
- Retrieve all authorizations relevant to this component.
47
- """
48
- app_ids = [app.id for app in self.applicaties]
49
- config = AuthorizationsConfig.get_solo()
50
- return Autorisatie.objects.filter(
51
- applicatie_id__in=app_ids, component=config.component
52
- )
53
-
54
- def _request_auth(self) -> list:
55
- client = AuthorizationsConfig.get_client()
56
- try:
57
- response = client.list(
58
- "applicatie", query_params={"clientIds": self.client_id}
59
- )
60
- except ClientError as exc:
61
- response = exc.args[0]
62
- # friendly debug - hint at where the problem is located
63
- if response["status"] == 403 and response["code"] == "not_authenticated":
64
- detail = _(
65
- "Component could not authenticate against the AC - "
66
- "authorizations could not be retrieved"
67
- )
68
- raise PermissionDenied(detail=detail, code="not_authenticated_for_ac")
69
- logger.warn("Authorization component can't be accessed")
70
- return []
71
-
72
- return underscoreize(response["results"])
73
-
74
- def _get_auth(self):
75
- return Applicatie.objects.filter(client_ids__contains=[self.client_id])
76
-
77
- @transaction.atomic
78
- def _save_auth(self, auth_data):
79
- applicaties = []
80
-
81
- for applicatie_data in auth_data:
82
- applicatie_serializer = ApplicatieUuidSerializer(data=applicatie_data)
83
- uuid = get_uuid_from_path(applicatie_data["url"])
84
- applicatie_data["uuid"] = uuid
85
- applicatie_serializer.is_valid()
86
- applicaties.append(applicatie_serializer.save())
87
-
88
- return applicaties
89
-
90
- @property
91
- def payload(self) -> Optional[Dict[str, Any]]:
92
- if self.encoded is None:
93
- return None
94
-
95
- if not hasattr(self, "_payload"):
96
- # decode the JWT and validate it
97
-
98
- # jwt check
99
- try:
100
- payload = jwt.decode(
101
- self.encoded,
102
- algorithms=["HS256"],
103
- options={"verify_signature": False},
104
- leeway=settings.JWT_LEEWAY,
105
- )
106
- except jwt.DecodeError:
107
- logger.info("Invalid JWT encountered")
108
- raise PermissionDenied(
109
- _(
110
- "JWT could not be decoded. Possibly you made a copy-paste mistake."
111
- ),
112
- code="jwt-decode-error",
113
- )
114
-
115
- # get client_id
116
- try:
117
- client_id = payload["client_id"]
118
- except KeyError:
119
- raise PermissionDenied(
120
- "Client identifier is niet aanwezig in JWT",
121
- code="missing-client-identifier",
122
- )
123
-
124
- # find client_id in DB and retrieve its secret
125
- try:
126
- jwt_secret = JWTSecret.objects.exclude(secret="").get(
127
- identifier=client_id
128
- )
129
- except JWTSecret.DoesNotExist:
130
- raise PermissionDenied(
131
- "Client identifier bestaat niet", code="invalid-client-identifier"
132
- )
133
- else:
134
- key = jwt_secret.secret
135
-
136
- # check signature of the token
137
- try:
138
- payload = jwt.decode(
139
- self.encoded,
140
- key,
141
- algorithms=["HS256"],
142
- leeway=settings.JWT_LEEWAY,
143
- )
144
- except jwt.InvalidSignatureError:
145
- logger.exception("Invalid signature - possible payload tampering?")
146
- raise PermissionDenied(
147
- "Client credentials zijn niet geldig", code="invalid-jwt-signature"
148
- )
149
-
150
- self._payload = payload
151
-
152
- return self._payload
153
-
154
- @property
155
- def client_id(self) -> str:
156
- if not self.payload:
157
- return None
158
- return self.payload["client_id"]
159
-
160
- def filter_vertrouwelijkheidaanduiding(self, base: QuerySet, value) -> QuerySet:
161
- if value is None:
162
- return base
163
-
164
- order_provided = VertrouwelijkheidsAanduiding.get_choice_order(value)
165
- order_case = VertrouwelijkheidsAanduiding.get_order_expression(
166
- "max_vertrouwelijkheidaanduiding"
167
- )
168
-
169
- # In this case we are filtering Autorisatie model to look for auth which meets our needs.
170
- # Therefore we're only considering authorizations here that have a max_vertrouwelijkheidaanduiding
171
- # bigger or equal than what we're checking for the object.
172
- # In cases when we are filtering data objects (Zaak, InformatieObject etc) it's the other way around
173
-
174
- return base.annotate(max_vertr=order_case).filter(max_vertr__gte=order_provided)
175
-
176
- def filter_default(self, base: QuerySet, name, value) -> QuerySet:
177
- if value is None:
178
- return base
179
-
180
- return base.filter(**{name: value})
181
-
182
- def has_auth(
183
- self, scopes: List[str], component: Optional[str] = None, **fields
184
- ) -> bool:
185
- if scopes is None:
186
- return False
187
-
188
- scopes_provided = set()
189
- config = AuthorizationsConfig.get_solo()
190
- if component is None:
191
- component = config.component
192
-
193
- for applicatie in self.applicaties:
194
- # allow everything
195
- if applicatie.heeft_alle_autorisaties is True:
196
- return True
197
-
198
- autorisaties = applicatie.autorisaties.filter(component=component)
199
-
200
- # filter on all additional components
201
- for field_name, field_value in fields.items():
202
- if hasattr(self, f"filter_{field_name}"):
203
- autorisaties = getattr(self, f"filter_{field_name}")(
204
- autorisaties, field_value
205
- )
206
- else:
207
- autorisaties = self.filter_default(
208
- autorisaties, field_name, field_value
209
- )
210
-
211
- for autorisatie in autorisaties:
212
- scopes_provided.update(autorisatie.scopes)
213
-
214
- return scopes.is_contained_in(list(scopes_provided))
215
-
216
-
217
- class AuthMiddleware:
218
- header = "HTTP_AUTHORIZATION"
219
- auth_type = "Bearer"
220
-
221
- def __init__(self, get_response=None):
222
- self.get_response = get_response
223
-
224
- def __call__(self, request):
225
- self.extract_jwt_payload(request)
226
- return self.get_response(request) if self.get_response else None
227
-
228
- def extract_jwt_payload(self, request):
229
- authorization = request.META.get(self.header, "")
230
- prefix = f"{self.auth_type} "
231
- if authorization.startswith(prefix):
232
- # grab the actual token
233
- encoded = authorization[len(prefix) :]
234
- else:
235
- encoded = None
236
-
237
- request.jwt_auth = JWTAuth(encoded)
238
-
239
-
240
14
  class APIVersionHeaderMiddleware:
241
15
  """
242
16
  Include a header specifying the API-version
@@ -0,0 +1,119 @@
1
+ # Generated by Django 5.1.2 on 2024-10-24 13:51
2
+
3
+ import logging
4
+ from typing import Set
5
+
6
+ from django.db import migrations, models
7
+ from django.utils.text import slugify
8
+
9
+ from zgw_consumers.constants import APITypes, AuthTypes
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def _get_api_type(api_root: str) -> APITypes:
15
+ mapping = {
16
+ "/autorisaties/api/": APITypes.ac,
17
+ "/zaken/api/": APITypes.zrc,
18
+ "/catalogi/api/": APITypes.ztc,
19
+ "/documenten/api/": APITypes.drc,
20
+ "/besluiten/api/": APITypes.drc,
21
+ }
22
+
23
+ for path, _type in mapping.items():
24
+ if path in api_root.lower():
25
+ return _type
26
+
27
+ return APITypes.orc
28
+
29
+
30
+ def _get_service_slug(credential: models.Model, existing_slugs: Set[str]) -> str:
31
+ default_slug: str = slugify(credential.label)
32
+
33
+ if default_slug not in existing_slugs or not existing_slugs:
34
+ return default_slug
35
+
36
+ count = 2
37
+ slug = f"{default_slug}-{count}"
38
+
39
+ while slug in existing_slugs:
40
+ count += 1
41
+ slug = f"{default_slug}-{count}"
42
+
43
+ return slug
44
+
45
+
46
+ def migrate_credentials_to_service(apps, _) -> None:
47
+ APICredential = apps.get_model("vng_api_common", "APICredential")
48
+ Service = apps.get_model("zgw_consumers", "Service")
49
+
50
+ credentials = APICredential.objects.all()
51
+
52
+ existings_service_slugs = set(Service.objects.values_list("slug", flat=True))
53
+
54
+ for credential in credentials:
55
+ logger.info(f"Creating Service for {credential.client_id}")
56
+
57
+ service_slug = _get_service_slug(credential, existings_service_slugs)
58
+
59
+ _, created = Service.objects.get_or_create(
60
+ api_root=credential.api_root,
61
+ defaults=dict(
62
+ label=credential.label,
63
+ slug=service_slug,
64
+ api_type=_get_api_type(credential.api_root),
65
+ auth_type=AuthTypes.zgw,
66
+ client_id=credential.client_id,
67
+ secret=credential.secret,
68
+ user_id=credential.user_id,
69
+ user_representation=credential.user_representation,
70
+ ),
71
+ )
72
+
73
+ existings_service_slugs.add(service_slug)
74
+
75
+ if created:
76
+ logger.info(f"Created new Service for {credential.api_root}")
77
+ else:
78
+ logger.info(f"Existing service found for {credential.api_root}")
79
+
80
+
81
+ def migrate_service_to_credentials(apps, _) -> None:
82
+ APICredential = apps.get_model("vng_api_common", "APICredential")
83
+ Service = apps.get_model("zgw_consumers", "Service")
84
+
85
+ services = Service.objects.filter(auth_type=AuthTypes.zgw)
86
+
87
+ for service in services:
88
+ logger.info(f"Creating APICredentials for {service.client_id}")
89
+
90
+ _, created = APICredential.objects.get_or_create(
91
+ api_root=service.api_root,
92
+ defaults=dict(
93
+ label=f"Migrated credentials for {service.client_id}",
94
+ client_id=service.client_id,
95
+ secret=service.secret,
96
+ user_id=service.user_id,
97
+ user_representation=service.user_representation,
98
+ ),
99
+ )
100
+ if created:
101
+ logger.info(f"Created new APICredentials for {service.api_root}")
102
+ else:
103
+ logger.info(f"Existing APICredentials found for {service.api_root}")
104
+
105
+
106
+ class Migration(migrations.Migration):
107
+
108
+ dependencies = [
109
+ ("vng_api_common", "0005_auto_20190614_1346"),
110
+ ]
111
+
112
+ operations = [
113
+ migrations.RunPython(
114
+ migrate_credentials_to_service, reverse_code=migrate_service_to_credentials
115
+ ),
116
+ migrations.DeleteModel(
117
+ name="APICredential",
118
+ ),
119
+ ]
vng_api_common/mocks.py CHANGED
@@ -1,7 +1,10 @@
1
1
  import re
2
2
  from urllib.parse import urlparse
3
3
 
4
- from zds_client.client import UUID_PATTERN
4
+ UUID_PATTERN = re.compile(
5
+ r"[0-9a-f]{8}\-[0-9a-f]{4}\-4[0-9a-f]{3}\-[89ab][0-9a-f]{3}\-[0-9a-f]{12}",
6
+ flags=re.I,
7
+ )
5
8
 
6
9
 
7
10
  class Response:
vng_api_common/models.py CHANGED
@@ -1,15 +1,7 @@
1
- from typing import Optional, Union
2
- from urllib.parse import urlsplit, urlunsplit
3
-
4
1
  from django.db import models
5
- from django.db.models.functions import Length
6
2
  from django.utils.translation import gettext_lazy as _
7
3
 
8
4
  from rest_framework.reverse import reverse
9
- from solo.models import SingletonModel
10
- from zds_client import Client, ClientAuth
11
-
12
- from .client import get_client as _get_client
13
5
 
14
6
 
15
7
  class APIMixin:
@@ -69,106 +61,3 @@ class JWTSecret(models.Model):
69
61
 
70
62
  def __str__(self):
71
63
  return self.identifier
72
-
73
-
74
- class APICredential(models.Model):
75
- """
76
- Store credentials for external APIs.
77
-
78
- When we need to authenticate against a remote API, we need to know which
79
- client ID and secret to use to sign the JWT.
80
- """
81
-
82
- api_root = models.URLField(
83
- _("API-root"),
84
- unique=True,
85
- help_text=_(
86
- "URL of the external API, ending in a trailing slash. Example: https://example.com/api/v1/"
87
- ),
88
- )
89
- label = models.CharField(
90
- _("label"),
91
- max_length=100,
92
- default="",
93
- help_text=_("Human readable label of the external API."),
94
- )
95
- client_id = models.CharField(
96
- _("client ID"),
97
- max_length=255,
98
- help_text=_("Client ID to identify this API at the external API."),
99
- )
100
- secret = models.CharField(
101
- _("secret"), max_length=255, help_text=_("Secret belonging to the client ID.")
102
- )
103
- user_id = models.CharField(
104
- _("user ID"),
105
- max_length=255,
106
- help_text=_(
107
- "User ID to use for the audit trail. Although these external API credentials are typically used by"
108
- "this API itself instead of a user, the user ID is required."
109
- ),
110
- )
111
- user_representation = models.CharField(
112
- _("user representation"),
113
- max_length=255,
114
- default="",
115
- help_text=_("Human readable representation of the user."),
116
- )
117
-
118
- class Meta:
119
- verbose_name = _("external API credential")
120
- verbose_name_plural = _("external API credentials")
121
-
122
- def __str__(self):
123
- return self.api_root
124
-
125
- @classmethod
126
- def get_auth(cls, url: str, **kwargs) -> Union[ClientAuth, None]:
127
- split_url = urlsplit(url)
128
- scheme_and_domain = urlunsplit(split_url[:2] + ("", "", ""))
129
-
130
- candidates = (
131
- cls.objects.filter(api_root__startswith=scheme_and_domain)
132
- .annotate(api_root_length=Length("api_root"))
133
- .order_by("-api_root_length")
134
- )
135
-
136
- # select the one matching
137
- for candidate in candidates.iterator():
138
- if url.startswith(candidate.api_root):
139
- credentials = candidate
140
- break
141
- else:
142
- return None
143
-
144
- auth = ClientAuth(
145
- client_id=credentials.client_id,
146
- secret=credentials.secret,
147
- user_id=credentials.user_id,
148
- user_representation=credentials.user_representation,
149
- **kwargs,
150
- )
151
- return auth
152
-
153
-
154
- class ClientConfig(SingletonModel):
155
- api_root = models.URLField(_("api root"), unique=True)
156
-
157
- class Meta:
158
- abstract = True
159
-
160
- def __str__(self):
161
- return self.api_root
162
-
163
- def save(self, *args, **kwargs):
164
- if not self.api_root.endswith("/"):
165
- self.api_root = f"{self.api_root}/"
166
- super().save(*args, **kwargs)
167
-
168
- @classmethod
169
- def get_client(cls) -> Optional[Client]:
170
- """
171
- Construct a client, prepared with the required auth.
172
- """
173
- config = cls.get_solo()
174
- return _get_client(config.api_root, url_is_api_root=True)
@@ -3,6 +3,7 @@ from django.utils.module_loading import import_string
3
3
 
4
4
  from drf_yasg.utils import swagger_auto_schema
5
5
  from notifications_api_common.api.serializers import NotificatieSerializer
6
+ from notifications_api_common.constants import SCOPE_NOTIFICATIES_PUBLICEREN_LABEL
6
7
  from rest_framework import status
7
8
  from rest_framework.response import Response
8
9
  from rest_framework.views import APIView
@@ -10,7 +11,6 @@ from rest_framework.views import APIView
10
11
  from ...permissions import AuthScopesRequired
11
12
  from ...scopes import Scope
12
13
  from ...serializers import FoutSerializer, ValidatieFoutSerializer
13
- from ..constants import SCOPE_NOTIFICATIES_PUBLICEREN_LABEL
14
14
 
15
15
 
16
16
  class NotificationBaseView(APIView):
@@ -4,7 +4,7 @@ from djangorestframework_camel_case.util import underscoreize
4
4
 
5
5
  from ..authorizations.models import Applicatie
6
6
  from ..authorizations.serializers import ApplicatieUuidSerializer
7
- from ..client import get_client
7
+ from ..client import get_client, to_internal_data
8
8
  from ..constants import CommonResourceAction
9
9
  from ..utils import get_uuid_from_path
10
10
 
@@ -20,8 +20,13 @@ class LoggingHandler:
20
20
  class AuthHandler:
21
21
  def _request_auth(self, url: str) -> dict:
22
22
  client = get_client(url)
23
- response = client.retrieve("applicatie", url)
24
- return underscoreize(response)
23
+
24
+ if not client:
25
+ return {}
26
+
27
+ response = client.get(url)
28
+ data = to_internal_data(response)
29
+ return underscoreize(data)
25
30
 
26
31
  def handle(self, message: dict) -> None:
27
32
  uuid = get_uuid_from_path(message["resource_url"])