commonground-api-common 2.5.5__py3-none-any.whl → 2.6.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commonground-api-common
3
- Version: 2.5.5
3
+ Version: 2.6.0
4
4
  Summary: Commonground API tooling
5
5
  Home-page: https://github.com/maykinmedia/commonground-api-common
6
6
  Author: Maykin Media, VNG-Realisatie
@@ -1,5 +1,5 @@
1
- commonground_api_common-2.5.5.data/scripts/generate_schema,sha256=OpKgzlFc_uzA3TVW_vHSYXAD_feLaCdTEnkWjIcxVzA,280
2
- vng_api_common/__init__.py,sha256=UutRtbE_ZOD7CIHaQC80vf8Bv_vmHDWhwjlCbt3yseM,22
1
+ commonground_api_common-2.6.0.data/scripts/generate_schema,sha256=OpKgzlFc_uzA3TVW_vHSYXAD_feLaCdTEnkWjIcxVzA,280
2
+ vng_api_common/__init__.py,sha256=OEib63e0yPEGlhEXyrWE1OwRnleR0cHI7KSX7oZEQLs,22
3
3
  vng_api_common/admin.py,sha256=iFtUPGf-ha0I-bXgq8QIFrP23Kzk_H3FlgAjt0U-ip0,259
4
4
  vng_api_common/apps.py,sha256=QQiJXRmjX9Q91oh0P9fvVnHe3NSYd1cEcUUBw0HLBCA,3690
5
5
  vng_api_common/checks.py,sha256=tOyfV7MMLGh4anrd_W30LvJCxiyQ4sFs1mGd9mtrEc0,1175
@@ -28,7 +28,7 @@ vng_api_common/routers.py,sha256=hEnhBulkgMM-7W_lYaykKTgTBj3-avl7DGsR9P7BbTU,189
28
28
  vng_api_common/schema.py,sha256=axs2Q8IXwpHNd5WscQg5xOErL6bWhP8WFItTt4xCFO4,16305
29
29
  vng_api_common/scopes.py,sha256=PGs6CkXorAAdWXGFY1bSy-jmsPn122Njen9aFFOpFIQ,2351
30
30
  vng_api_common/search.py,sha256=yehS6boCOk1JXLCqAMU-B62hWtbTBSf_WKIVGPgp0Mg,1045
31
- vng_api_common/serializers.py,sha256=NdrZJqP7p54lRKoKG4mEDS9MqaMcPESIr4dB1tnLaqI,10163
31
+ vng_api_common/serializers.py,sha256=ErIoN1br71eszZ-hRgVUycjQkK6rJNkT5ygsagZoGNg,14084
32
32
  vng_api_common/urls.py,sha256=9IWHYLlEIIHNaZ_Zq02qNQ2HJpETb7o-89r7yBM_tQs,270
33
33
  vng_api_common/utils.py,sha256=EHqVjZhtqnbU7YrqgYIBss28Sd19jtnTLNaMWLfj3Zw,8203
34
34
  vng_api_common/validators.py,sha256=ejaFZvFXFaBlqxjA2_07NSHKHlG5pejrfC_GHjwCj6E,12852
@@ -78,7 +78,7 @@ vng_api_common/authorizations/admin.py,sha256=Tk0yYKbb005E0XZaYYWbucMf_K5M8Hhz62
78
78
  vng_api_common/authorizations/middleware.py,sha256=KJ3znCXPRMOVqSur62SmBjvC6RcKxtcWq1rzaHdYR98,8416
79
79
  vng_api_common/authorizations/models.py,sha256=slIYxSktxCxSg03Nfb2mhsQse17b93KWE-rdPdMv8Ik,5199
80
80
  vng_api_common/authorizations/serializers.py,sha256=3HeKWEqhI3UWwI8SttC4rEID-Epbk7SWsC-bEjolbaw,5151
81
- vng_api_common/authorizations/utils.py,sha256=VCXdU4q3CQ1cvuYkg1dPWSbZym1Ufoz5gSmbHYkQcgw,521
81
+ vng_api_common/authorizations/utils.py,sha256=GmwTy5GhYk3e1VU4LpdfYdr8VD-R8p00hUY11QBbOhc,555
82
82
  vng_api_common/authorizations/validators.py,sha256=u7fKm0QgGy8fiAeYmIEB9Gy-yIE9C-tC2ZpnNQBXPso,2816
83
83
  vng_api_common/authorizations/migrations/0001_initial.py,sha256=ooAZtQeDtWgDxXzAP-KnSyyFYLRPM-PMrK5RgOnTPjQ,4360
84
84
  vng_api_common/authorizations/migrations/0002_authorizationsconfig.py,sha256=m4taH6ClHI-YHYGGOKaq_qYXGx9lq1InXOGLQKg9MSw,1364
@@ -183,7 +183,7 @@ vng_api_common/tests/auth.py,sha256=IKDWTEFv4Bign4F70-ibsFcnJqRxEJaXvqaPQJWa1xY,
183
183
  vng_api_common/tests/caching.py,sha256=zfIw5cRRvO9cekHZZKfRqZc8cx5IfJUYNmcH6cuIMg4,624
184
184
  vng_api_common/tests/schema.py,sha256=WDvifDQQiKqIpQijpeQ7rYkFroJmuPuHe7zNhl1Bigk,2293
185
185
  vng_api_common/tests/urls.py,sha256=PFrYzQbBC0TFPMEn3uPhcBG0IQs9JsEPqckicJT1UA4,2159
186
- commonground_api_common-2.5.5.dist-info/METADATA,sha256=-ob8zNCm7QtO8IekDfwux_xZx00EdHYWqEKoqa6_sNU,6988
187
- commonground_api_common-2.5.5.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
188
- commonground_api_common-2.5.5.dist-info/top_level.txt,sha256=vPismc83zPzWXTmlNCCwfDlFV9iygJYxNJW5iDjKTgw,15
189
- commonground_api_common-2.5.5.dist-info/RECORD,,
186
+ commonground_api_common-2.6.0.dist-info/METADATA,sha256=HsGhhHvzoGLHlqzsYvNxs_qrG76ym2BtbcCX3u7jqx8,6988
187
+ commonground_api_common-2.6.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
188
+ commonground_api_common-2.6.0.dist-info/top_level.txt,sha256=vPismc83zPzWXTmlNCCwfDlFV9iygJYxNJW5iDjKTgw,15
189
+ commonground_api_common-2.6.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (77.0.3)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1 +1 @@
1
- __version__ = "2.5.5"
1
+ __version__ = "2.6.0"
@@ -12,6 +12,7 @@ def generate_jwt(client_id, secret, user_id, user_representation):
12
12
  secret=secret,
13
13
  user_id=user_id,
14
14
  user_representation=user_representation,
15
+ jwt_valid_for=5 * 60,
15
16
  )
16
17
  )
17
18
  return f"Bearer {auth._token}"
@@ -1,13 +1,17 @@
1
1
  import datetime
2
2
  import inspect
3
3
  from collections import OrderedDict
4
+ from functools import reduce
4
5
  from typing import List, Optional, Tuple, Union
5
6
 
6
7
  from django.db import models, transaction
8
+ from django.db.models import Model
7
9
  from django.utils.translation import gettext_lazy as _
8
10
 
9
11
  import isodate
10
12
  from rest_framework import fields, serializers
13
+ from rest_framework.request import Request
14
+ from rest_framework_nested.relations import NestedHyperlinkedRelatedField
11
15
 
12
16
  from .choices import TextChoicesWithDescriptions
13
17
  from .descriptors import GegevensGroepType
@@ -164,11 +168,11 @@ class GegevensGroepSerializer(
164
168
 
165
169
  Usage::
166
170
 
167
- >>> class VerlengingSerializer(GegevensGroepSerializer):
168
- ... class Meta:
169
- ... model = Zaak
170
- ... gegevensgroep = 'verlenging'
171
- >>>
171
+ >>> class VerlengingSerializer(GegevensGroepSerializer):
172
+ ... class Meta:
173
+ ... model = Zaak
174
+ ... gegevensgroep = 'verlenging'
175
+ >>>
172
176
 
173
177
  Where ``Zaak.verlenging`` is a :class:``GegevensGroepType``.
174
178
  """
@@ -275,7 +279,77 @@ class NestedGegevensGroepMixin:
275
279
  return super().update(instance, validated_data)
276
280
 
277
281
 
278
- class LengthHyperlinkedRelatedField(serializers.HyperlinkedRelatedField):
282
+ def get_nested_fk_attribute(instance, relation_path):
283
+ """
284
+ Retrieves an attribute from a nested foreign key relation.
285
+
286
+ Args:
287
+ - instance: The model instance (e.g., Book).
288
+ - relation_path: A string with the relation path, e.g., 'author__publisher__name'.
289
+
290
+ Returns:
291
+ - The value of the nested attribute or None if not found.
292
+ """
293
+ relations = relation_path.split("__")
294
+
295
+ return reduce(
296
+ lambda obj, attr: getattr(obj, attr, None) if obj else None, relations, instance
297
+ )
298
+
299
+
300
+ class CacheMixin:
301
+ """
302
+ Mixin for Hyperlinked DRF fields to cache the base URI per view, to avoid
303
+ having to recalculate this for each related object that has to be serialized
304
+
305
+ This cache is stored on the field instance itself, so it's reset between requests
306
+ """
307
+
308
+ lookup_url_kwarg = "" # Should be defined on `HyperlinkedRelatedField`
309
+ identifier_placeholder = "id-placeholder"
310
+
311
+ def get_extra_reverse_kwargs(self) -> dict[str, str]:
312
+ """
313
+ Hook to inject extra kwargs to be passed to `reverse()`
314
+ """
315
+ return {}
316
+
317
+ def __init__(self, *args, **kwargs):
318
+ super().__init__(*args, **kwargs)
319
+
320
+ self._reverse_cache = {}
321
+
322
+ def get_url(
323
+ self, obj: Model, view_name: str, request: Request, format: str | None
324
+ ) -> str | None:
325
+ # Unsaved objects will not yet have a valid URL.
326
+ if hasattr(obj, "pk") and obj.pk in (None, ""):
327
+ return None
328
+
329
+ base_url = self._reverse_cache.get(view_name)
330
+
331
+ if base_url is None:
332
+ # If not cached, compute and cache it for this request cycle
333
+ try:
334
+ # Insert placeholders for identifiers, these will be replaced by
335
+ # real identifiers later on
336
+ kwargs = {self.lookup_url_kwarg: self.identifier_placeholder}
337
+ kwargs.update(self.get_extra_reverse_kwargs())
338
+
339
+ base_url = self.reverse(
340
+ view_name, kwargs=kwargs, request=request, format=format
341
+ )
342
+ self._reverse_cache[view_name] = base_url
343
+ except Exception as e:
344
+ raise ValueError(f"Could not resolve reverse for {view_name}: {e}")
345
+
346
+ url = base_url.replace(
347
+ self.identifier_placeholder, str(getattr(obj, self.lookup_url_kwarg))
348
+ )
349
+ return url
350
+
351
+
352
+ class LengthHyperlinkedRelatedField(CacheMixin, serializers.HyperlinkedRelatedField):
279
353
  default_error_messages = {
280
354
  "max_length": _("Ensure this field has no more than {max_length} characters."),
281
355
  "min_length": _("Ensure this field has at least {min_length} characters."),
@@ -296,3 +370,40 @@ class LengthHyperlinkedRelatedField(serializers.HyperlinkedRelatedField):
296
370
  self.fail("min_length", max_length=self.min_length, length=len(data))
297
371
 
298
372
  return super().to_internal_value(data)
373
+
374
+
375
+ class CachedHyperlinkedRelatedField(CacheMixin, serializers.HyperlinkedRelatedField):
376
+ """
377
+ Subclass of ``serializers.HyperlinkedRelatedField`` that applies caching in
378
+ ``.get_url()`` to ``reverse()`` calls to improve serialization performance
379
+ """
380
+
381
+
382
+ class CachedHyperlinkedIdentityField(CacheMixin, serializers.HyperlinkedIdentityField):
383
+ """
384
+ Subclass of ``serializers.HyperlinkedIdentityField`` that applies caching in
385
+ ``.get_url()`` to ``reverse()`` calls to improve serialization performance
386
+ """
387
+
388
+
389
+ class CachedNestedHyperlinkedRelatedField(CacheMixin, NestedHyperlinkedRelatedField):
390
+ """
391
+ Subclass of ``serializers.HyperlinkedIdentityField`` that applies caching in
392
+ ``.get_url()`` to ``reverse()`` calls to improve serialization performance
393
+ """
394
+
395
+ def get_extra_reverse_kwargs(self) -> dict[str, str]:
396
+ return self.parent_lookup_kwargs
397
+
398
+ def get_url(
399
+ self, obj: Model, view_name: str, request: Request, format: str | None
400
+ ) -> str | None:
401
+ url = super().get_url(obj, view_name, request, format)
402
+
403
+ if not url:
404
+ return None
405
+
406
+ # Replace the placeholder from the cached base URI with the actual identifier
407
+ for k, v in self.parent_lookup_kwargs.items():
408
+ url = url.replace(v, str(get_nested_fk_attribute(obj, v)))
409
+ return url