udata 11.0.2.dev16__py3-none-any.whl → 11.0.2.dev18__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.
Potentially problematic release.
This version of udata might be problematic. Click here for more details.
- udata/api_fields.py +99 -45
- udata/core/dataservices/models.py +4 -4
- udata/core/pages/models.py +2 -2
- udata/core/reports/models.py +2 -2
- udata/core/reuse/models.py +4 -4
- udata/mail.py +1 -1
- udata/static/chunks/{11.51d706fb9521c16976bc.js → 11.0f04e49a40a0a381bcce.js} +3 -3
- udata/static/chunks/{11.51d706fb9521c16976bc.js.map → 11.0f04e49a40a0a381bcce.js.map} +1 -1
- udata/static/chunks/{13.39e106d56f794ebd06a0.js → 13.d9c1735d14038b94c17e.js} +2 -2
- udata/static/chunks/{13.39e106d56f794ebd06a0.js.map → 13.d9c1735d14038b94c17e.js.map} +1 -1
- udata/static/chunks/{17.70cbb4a91b002338007e.js → 17.81c57c0dedf812e43013.js} +2 -2
- udata/static/chunks/{17.70cbb4a91b002338007e.js.map → 17.81c57c0dedf812e43013.js.map} +1 -1
- udata/static/chunks/{19.a348a5fff8fe2801e52a.js → 19.8da42e8359d72afc2618.js} +3 -3
- udata/static/chunks/{19.a348a5fff8fe2801e52a.js.map → 19.8da42e8359d72afc2618.js.map} +1 -1
- udata/static/chunks/{5.343ca020a2d38cec1a14.js → 5.0fa1408dae4e76b87b2e.js} +3 -3
- udata/static/chunks/{5.343ca020a2d38cec1a14.js.map → 5.0fa1408dae4e76b87b2e.js.map} +1 -1
- udata/static/chunks/{6.a3b07de9dd2ca2d24e85.js → 6.d663709d877baa44a71e.js} +3 -3
- udata/static/chunks/{6.a3b07de9dd2ca2d24e85.js.map → 6.d663709d877baa44a71e.js.map} +1 -1
- udata/static/chunks/{8.462bb3029de008497675.js → 8.494b003a94383b142c18.js} +2 -2
- udata/static/chunks/{8.462bb3029de008497675.js.map → 8.494b003a94383b142c18.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/tests/test_api_fields.py +2 -2
- {udata-11.0.2.dev16.dist-info → udata-11.0.2.dev18.dist-info}/METADATA +1 -1
- {udata-11.0.2.dev16.dist-info → udata-11.0.2.dev18.dist-info}/RECORD +29 -29
- {udata-11.0.2.dev16.dist-info → udata-11.0.2.dev18.dist-info}/WHEEL +0 -0
- {udata-11.0.2.dev16.dist-info → udata-11.0.2.dev18.dist-info}/entry_points.txt +0 -0
- {udata-11.0.2.dev16.dist-info → udata-11.0.2.dev18.dist-info}/licenses/LICENSE +0 -0
- {udata-11.0.2.dev16.dist-info → udata-11.0.2.dev18.dist-info}/top_level.txt +0 -0
udata/api_fields.py
CHANGED
|
@@ -1,39 +1,24 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""API field generation and metadata management for MongoEngine documents.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
- forms.py
|
|
6
|
-
- api_fields.py
|
|
3
|
+
This module provides tools to automatically generate Flask-RESTX fields from MongoEngine
|
|
4
|
+
documents, reducing duplication between model definitions and API serialization.
|
|
7
5
|
|
|
8
|
-
|
|
6
|
+
Main components:
|
|
7
|
+
- `@generate_fields`: Decorator that adds API field generation to document classes
|
|
8
|
+
- `field()`: Universal function to add metadata to fields and methods
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
10
|
+
The `@generate_fields` decorator parameters:
|
|
11
|
+
- default_filterable_field: Default field for filtering (e.g., Badge.kind)
|
|
12
|
+
- searchable: Enables full-text search with param `q` via MongoEngine on indexed text fields
|
|
13
|
+
- additional_sorts: Custom sort options beyond field-based sorts
|
|
14
|
+
- additional_filters: Cross-document filtering (e.g., Reuse__organization__badge)
|
|
14
15
|
|
|
16
|
+
Generated attributes on decorated classes:
|
|
17
|
+
- ref_fields: Minimal fields for embedded/referenced documents
|
|
18
|
+
- read_fields: All fields returned when querying a document
|
|
19
|
+
- write_fields: Fields accepted when creating/updating a document
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
the document fields are parsed and enhanced if they are wrapped in the `field` helper.
|
|
18
|
-
|
|
19
|
-
- sortable: boolean, if True, it'll be available in the list of sort options
|
|
20
|
-
- show_as_ref: add to the list of `ref_fields` (see below)
|
|
21
|
-
- readonly: don't add this field to the `write_fields`
|
|
22
|
-
- markdown: use Mardown to format this field instead of plain old text
|
|
23
|
-
- filterable: this field can be filtered on. It's either an empty dictionnary, either {`key`: `field_name`} if the `field_name` to use is different from the original field, eg `dataset` instead of `datasets`.
|
|
24
|
-
- description: use as the info on the field in the swagger forms.
|
|
25
|
-
- check: provide a function to validate the content of the field.
|
|
26
|
-
- thumbnail_info: add additional info for a thumbnail, eg `{ "size": BIGGEST_IMAGE_SIZE }`.
|
|
27
|
-
|
|
28
|
-
You may also use the `@function_field` decorator to treat a document method as a field.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
The following fields are added on the document class once decorated:
|
|
32
|
-
|
|
33
|
-
- ref_fields: list of fields to return when embedding/referencing a document, eg when querying Reuse.organization, only return a subset of the org fields
|
|
34
|
-
- read_fields: all of the fields to return when querying a document
|
|
35
|
-
- write_fields: list of fields to provide when creating a document, eg when creating a Reuse, we only provide organization IDs, not all the org fields
|
|
36
|
-
|
|
21
|
+
For field-specific metadata, see the `field()` function documentation.
|
|
37
22
|
"""
|
|
38
23
|
|
|
39
24
|
import functools
|
|
@@ -401,7 +386,7 @@ def generate_fields(**kwargs) -> Callable:
|
|
|
401
386
|
|
|
402
387
|
# The goal of this loop is to fetch all functions (getters) of the class
|
|
403
388
|
# If a function has an `__additional_field_info__` attribute it means
|
|
404
|
-
# it has been decorated with `@
|
|
389
|
+
# it has been decorated with `@field()` and should be included
|
|
405
390
|
# in the API response.
|
|
406
391
|
for method_name in dir(cls):
|
|
407
392
|
if method_name == "objects":
|
|
@@ -558,22 +543,91 @@ def generate_fields(**kwargs) -> Callable:
|
|
|
558
543
|
return wrapper
|
|
559
544
|
|
|
560
545
|
|
|
561
|
-
def
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
546
|
+
def field(
|
|
547
|
+
inner=None,
|
|
548
|
+
sortable: bool | str | None = None,
|
|
549
|
+
filterable: dict[str, Any] | None = None,
|
|
550
|
+
readonly: bool | None = None,
|
|
551
|
+
show_as_ref: bool | None = None,
|
|
552
|
+
markdown: bool | None = None,
|
|
553
|
+
description: str | None = None,
|
|
554
|
+
auditable: bool | None = None,
|
|
555
|
+
checks: list[Callable] | None = None,
|
|
556
|
+
attribute: str | None = None,
|
|
557
|
+
thumbnail_info: dict[str, Any] | None = None,
|
|
558
|
+
example: str | None = None,
|
|
559
|
+
nested_fields: dict[str, Any] | None = None,
|
|
560
|
+
inner_field_info: dict[str, Any] | None = None,
|
|
561
|
+
size: int | None = None,
|
|
562
|
+
is_thumbnail: bool | None = None,
|
|
563
|
+
href: Callable | None = None,
|
|
564
|
+
generic: bool | None = None,
|
|
565
|
+
generic_key: str | None = None,
|
|
566
|
+
convert_to: Callable | None = None,
|
|
567
|
+
allow_null: bool | None = None,
|
|
568
|
+
**kwargs: Any, # Accept any additional parameters, forward to flask rest x constructor.
|
|
569
|
+
):
|
|
570
|
+
"""Universal field decorator/wrapper for API field metadata.
|
|
571
|
+
|
|
572
|
+
Can be used in two ways:
|
|
573
|
+
|
|
574
|
+
1. As a wrapper for MongoEngine fields:
|
|
575
|
+
title = field(db.StringField(required=True),
|
|
576
|
+
sortable=True,
|
|
577
|
+
description="The title of the item")
|
|
578
|
+
|
|
579
|
+
2. As a decorator for computed fields:
|
|
580
|
+
@field(description="Link to the API endpoint", show_as_ref=True)
|
|
581
|
+
def uri(self):
|
|
582
|
+
return f"/api/items/{self.id}"
|
|
583
|
+
|
|
584
|
+
Args:
|
|
585
|
+
inner: The MongoEngine field to wrap (or None when used as decorator)
|
|
586
|
+
sortable: If True, field can be sorted. If str, use as custom sort key
|
|
587
|
+
filterable: Filter configuration dict
|
|
588
|
+
readonly: If True, exclude from write_fields
|
|
589
|
+
show_as_ref: If True, include in ref_fields
|
|
590
|
+
markdown: If True, use Markdown formatter
|
|
591
|
+
description: Field description for Swagger
|
|
592
|
+
auditable: If False, exclude from audit trail
|
|
593
|
+
checks: List of validation functions
|
|
594
|
+
attribute: Custom attribute name for serialization
|
|
595
|
+
thumbnail_info: Thumbnail configuration dict
|
|
596
|
+
example: Example value for documentation
|
|
597
|
+
nested_fields: RestX model for nested objects
|
|
598
|
+
inner_field_info: Additional info for list inner fields
|
|
599
|
+
size: Image size for thumbnails
|
|
600
|
+
is_thumbnail: If True, this is a thumbnail field
|
|
601
|
+
href: Function to generate API link
|
|
602
|
+
generic: If True, handle generic embedded documents
|
|
603
|
+
generic_key: Key for generic type discrimination
|
|
604
|
+
convert_to: Custom converter for RestX
|
|
605
|
+
allow_null: If True, field can be null
|
|
606
|
+
**kwargs: Any additional parameters not explicitly defined
|
|
607
|
+
|
|
608
|
+
Returns:
|
|
609
|
+
When used as wrapper: The field with __additional_field_info__ attached.
|
|
610
|
+
When used as decorator: A decorator function.
|
|
611
|
+
"""
|
|
612
|
+
# Build field_info from non-None parameters, excluding 'inner' and 'kwargs'
|
|
613
|
+
field_info = {
|
|
614
|
+
k: v for k, v in locals().items() if v is not None and k not in ("inner", "kwargs")
|
|
615
|
+
}
|
|
568
616
|
|
|
569
|
-
|
|
570
|
-
|
|
617
|
+
# Add any extra kwargs passed
|
|
618
|
+
field_info.update(kwargs)
|
|
571
619
|
|
|
572
|
-
|
|
620
|
+
if inner is None:
|
|
621
|
+
# Used as a decorator for methods
|
|
622
|
+
def decorator(func):
|
|
623
|
+
func.__additional_field_info__ = field_info
|
|
624
|
+
return func
|
|
573
625
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
626
|
+
return decorator
|
|
627
|
+
else:
|
|
628
|
+
# Used as a field wrapper
|
|
629
|
+
inner.__additional_field_info__ = field_info
|
|
630
|
+
return inner
|
|
577
631
|
|
|
578
632
|
|
|
579
633
|
def patch(obj, request) -> type:
|
|
@@ -7,7 +7,7 @@ from mongoengine.signals import post_save
|
|
|
7
7
|
|
|
8
8
|
import udata.core.contact_point.api_fields as contact_api_fields
|
|
9
9
|
from udata.api import api, fields
|
|
10
|
-
from udata.api_fields import field,
|
|
10
|
+
from udata.api_fields import field, generate_fields
|
|
11
11
|
from udata.core.activity.models import Auditable
|
|
12
12
|
from udata.core.dataservices.constants import (
|
|
13
13
|
DATASERVICE_ACCESS_AUDIENCE_CONDITIONS,
|
|
@@ -284,7 +284,7 @@ class Dataservice(Auditable, WithMetrics, Linkable, Owned, db.Document):
|
|
|
284
284
|
auditable=False,
|
|
285
285
|
)
|
|
286
286
|
|
|
287
|
-
@
|
|
287
|
+
@field(description="Link to the API endpoint for this dataservice")
|
|
288
288
|
def self_api_url(self, **kwargs):
|
|
289
289
|
return url_for(
|
|
290
290
|
"api.dataservice",
|
|
@@ -292,7 +292,7 @@ class Dataservice(Auditable, WithMetrics, Linkable, Owned, db.Document):
|
|
|
292
292
|
**self._self_api_url_kwargs(**kwargs),
|
|
293
293
|
)
|
|
294
294
|
|
|
295
|
-
@
|
|
295
|
+
@field(description="Link to the udata web page for this dataservice", show_as_ref=True)
|
|
296
296
|
def self_web_url(self, **kwargs):
|
|
297
297
|
return cdata_url(f"/dataservices/{self._link_id(**kwargs)}/", **kwargs)
|
|
298
298
|
|
|
@@ -309,7 +309,7 @@ class Dataservice(Auditable, WithMetrics, Linkable, Owned, db.Document):
|
|
|
309
309
|
return self.private or self.deleted_at or self.archived_at
|
|
310
310
|
|
|
311
311
|
@property
|
|
312
|
-
@
|
|
312
|
+
@field(
|
|
313
313
|
nested_fields=dataservice_permissions_fields,
|
|
314
314
|
)
|
|
315
315
|
def permissions(self):
|
udata/core/pages/models.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from udata.api import api, fields
|
|
2
|
-
from udata.api_fields import field,
|
|
2
|
+
from udata.api_fields import field, generate_fields
|
|
3
3
|
from udata.core.activity.models import Auditable
|
|
4
4
|
from udata.core.dataservices.models import Dataservice
|
|
5
5
|
from udata.core.dataset.api_fields import dataset_fields
|
|
@@ -91,7 +91,7 @@ class Page(Auditable, Owned, Datetimed, db.Document):
|
|
|
91
91
|
)
|
|
92
92
|
|
|
93
93
|
@property
|
|
94
|
-
@
|
|
94
|
+
@field(
|
|
95
95
|
nested_fields=page_permissions_fields,
|
|
96
96
|
)
|
|
97
97
|
def permissions(self):
|
udata/core/reports/models.py
CHANGED
|
@@ -4,7 +4,7 @@ from bson import DBRef
|
|
|
4
4
|
from flask import url_for
|
|
5
5
|
from mongoengine import DO_NOTHING, NULLIFY, signals
|
|
6
6
|
|
|
7
|
-
from udata.api_fields import field,
|
|
7
|
+
from udata.api_fields import field, generate_fields
|
|
8
8
|
from udata.core.user.api_fields import user_ref_fields
|
|
9
9
|
from udata.core.user.models import User
|
|
10
10
|
from udata.mongo import db
|
|
@@ -46,7 +46,7 @@ class Report(db.Document):
|
|
|
46
46
|
readonly=True,
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
-
@
|
|
49
|
+
@field(description="Link to the API endpoint for this report")
|
|
50
50
|
def self_api_url(self):
|
|
51
51
|
return url_for("api.report", report=self, _external=True)
|
|
52
52
|
|
udata/core/reuse/models.py
CHANGED
|
@@ -3,7 +3,7 @@ from flask import url_for
|
|
|
3
3
|
from mongoengine.signals import post_save, pre_save
|
|
4
4
|
from werkzeug.utils import cached_property
|
|
5
5
|
|
|
6
|
-
from udata.api_fields import field,
|
|
6
|
+
from udata.api_fields import field, generate_fields
|
|
7
7
|
from udata.core.activity.models import Auditable
|
|
8
8
|
from udata.core.dataset.api_fields import dataset_fields
|
|
9
9
|
from udata.core.linkable import Linkable
|
|
@@ -197,16 +197,16 @@ class Reuse(db.Datetimed, Auditable, WithMetrics, ReuseBadgeMixin, Linkable, Own
|
|
|
197
197
|
"api.reuse", reuse=self._link_id(**kwargs), **self._self_api_url_kwargs(**kwargs)
|
|
198
198
|
)
|
|
199
199
|
|
|
200
|
-
@
|
|
200
|
+
@field(description="Link to the API endpoint for this reuse", show_as_ref=True)
|
|
201
201
|
def uri(self, *args, **kwargs):
|
|
202
202
|
return self.self_api_url(*args, **kwargs)
|
|
203
203
|
|
|
204
|
-
@
|
|
204
|
+
@field(description="Link to the udata web page for this reuse", show_as_ref=True)
|
|
205
205
|
def page(self, *args, **kwargs):
|
|
206
206
|
return self.self_web_url(*args, **kwargs)
|
|
207
207
|
|
|
208
208
|
@property
|
|
209
|
-
@
|
|
209
|
+
@field(
|
|
210
210
|
nested_fields=reuse_permissions_fields,
|
|
211
211
|
)
|
|
212
212
|
def permissions(self):
|
udata/mail.py
CHANGED
|
@@ -49,7 +49,7 @@ def send(subject, recipients, template_base, **kwargs):
|
|
|
49
49
|
|
|
50
50
|
debug = current_app.config.get("DEBUG", False)
|
|
51
51
|
send_mail = current_app.config.get("SEND_MAIL", not debug)
|
|
52
|
-
connection =
|
|
52
|
+
connection = mail.connect if send_mail else dummyconnection
|
|
53
53
|
extras = get_mail_campaign_dict()
|
|
54
54
|
|
|
55
55
|
with connection() as conn:
|