udata 10.0.1.dev32359__py2.py3-none-any.whl → 10.0.1.dev32396__py2.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.

Files changed (29) hide show
  1. udata/api_fields.py +28 -1
  2. udata/core/badges/tests/test_model.py +22 -7
  3. udata/core/dataset/models.py +6 -1
  4. udata/core/organization/models.py +6 -1
  5. udata/core/reuse/models.py +6 -1
  6. udata/static/chunks/{10.dac55d18d0b4ef3cdacf.js → 10.a99bb538cfbadb38dbcb.js} +3 -3
  7. udata/static/chunks/{10.dac55d18d0b4ef3cdacf.js.map → 10.a99bb538cfbadb38dbcb.js.map} +1 -1
  8. udata/static/chunks/{11.4a20a75f827c5a1125c3.js → 11.bb1c1fb39f740fbbeec0.js} +3 -3
  9. udata/static/chunks/{11.4a20a75f827c5a1125c3.js.map → 11.bb1c1fb39f740fbbeec0.js.map} +1 -1
  10. udata/static/chunks/{13.645dd0b7c0b9210f1b56.js → 13.bef5fdb3e147e94fea99.js} +2 -2
  11. udata/static/chunks/{13.645dd0b7c0b9210f1b56.js.map → 13.bef5fdb3e147e94fea99.js.map} +1 -1
  12. udata/static/chunks/{17.8e19985c4d12a3b7b0c0.js → 17.b91d28f550dc44bc4979.js} +2 -2
  13. udata/static/chunks/{17.8e19985c4d12a3b7b0c0.js.map → 17.b91d28f550dc44bc4979.js.map} +1 -1
  14. udata/static/chunks/{19.825a43c330157e351fca.js → 19.2c615ffee1e807000770.js} +3 -3
  15. udata/static/chunks/{19.825a43c330157e351fca.js.map → 19.2c615ffee1e807000770.js.map} +1 -1
  16. udata/static/chunks/{8.5ee0cf635c848abbfc05.js → 8.291bde987ed97294e4de.js} +2 -2
  17. udata/static/chunks/{8.5ee0cf635c848abbfc05.js.map → 8.291bde987ed97294e4de.js.map} +1 -1
  18. udata/static/chunks/{9.df3c36f8d0d210621fbb.js → 9.985935421e62c97a9f86.js} +3 -3
  19. udata/static/chunks/{9.df3c36f8d0d210621fbb.js.map → 9.985935421e62c97a9f86.js.map} +1 -1
  20. udata/static/common.js +1 -1
  21. udata/static/common.js.map +1 -1
  22. udata/tests/organization/test_organization_model.py +20 -2
  23. udata/tests/reuse/test_reuse_model.py +17 -1
  24. {udata-10.0.1.dev32359.dist-info → udata-10.0.1.dev32396.dist-info}/METADATA +2 -1
  25. {udata-10.0.1.dev32359.dist-info → udata-10.0.1.dev32396.dist-info}/RECORD +29 -29
  26. {udata-10.0.1.dev32359.dist-info → udata-10.0.1.dev32396.dist-info}/LICENSE +0 -0
  27. {udata-10.0.1.dev32359.dist-info → udata-10.0.1.dev32396.dist-info}/WHEEL +0 -0
  28. {udata-10.0.1.dev32359.dist-info → udata-10.0.1.dev32396.dist-info}/entry_points.txt +0 -0
  29. {udata-10.0.1.dev32359.dist-info → udata-10.0.1.dev32396.dist-info}/top_level.txt +0 -0
udata/api_fields.py CHANGED
@@ -94,6 +94,8 @@ def convert_db_to_field(key, field, info) -> tuple[Callable | None, Callable | N
94
94
  params["min_length"] = field.min_length
95
95
  params["max_length"] = field.max_length
96
96
  params["enum"] = field.choices
97
+ if field.validation:
98
+ params["validation"] = validation_to_type(field.validation)
97
99
  elif isinstance(field, mongo_fields.ObjectIdField):
98
100
  constructor = restx_fields.String
99
101
  elif isinstance(field, mongo_fields.FloatField):
@@ -287,7 +289,7 @@ def generate_fields(**kwargs) -> Callable:
287
289
  if not isinstance(
288
290
  field, mongo_fields.ReferenceField | mongo_fields.LazyReferenceField
289
291
  ):
290
- raise Exception("Cannot use additional_filters on not a ref.")
292
+ raise Exception("Cannot use additional_filters on a field that is not a ref.")
291
293
 
292
294
  ref_model: db.Document = field.document_type
293
295
 
@@ -678,9 +680,34 @@ def compute_filter(column: str, field, info, filterable) -> dict:
678
680
  filterable["type"] = boolean
679
681
  else:
680
682
  filterable["type"] = str
683
+ if field.validation:
684
+ filterable["type"] = validation_to_type(field.validation)
681
685
 
682
686
  filterable["choices"] = info.get("choices", None)
683
687
  if hasattr(field, "choices") and field.choices:
684
688
  filterable["choices"] = field.choices
685
689
 
686
690
  return filterable
691
+
692
+
693
+ def validation_to_type(validation: Callable) -> Callable:
694
+ """Convert a mongo field's validation function to a ReqParser's type.
695
+
696
+ In flask_restx.ReqParser, validation is done by setting the param's type to
697
+ a callable that will either raise, or return the parsed value.
698
+
699
+ In mongo, a field's validation function cannot return anything, so this
700
+ helper wraps the mongo field's validation to return the value if it validated.
701
+ """
702
+ from udata.models import db
703
+
704
+ def wrapper(value: str) -> str:
705
+ try:
706
+ validation(value)
707
+ except db.ValidationError:
708
+ raise
709
+ return value
710
+
711
+ wrapper.__schema__ = {"type": "string", "format": "my-custom-format"}
712
+
713
+ return wrapper
@@ -15,8 +15,13 @@ BADGES = {
15
15
  }
16
16
 
17
17
 
18
+ def validate_badge(value):
19
+ if value not in Fake.__badges__.keys():
20
+ raise db.ValidationError("Unknown badge type")
21
+
22
+
18
23
  class FakeBadge(Badge):
19
- kind = db.StringField(required=True, choices=list(BADGES.keys()))
24
+ kind = db.StringField(required=True, validation=validate_badge)
20
25
 
21
26
 
22
27
  class FakeBadgeMixin(BadgeMixin):
@@ -34,12 +39,6 @@ class BadgeMixinTest(DBTestMixin, TestCase):
34
39
  fake = Fake.objects.create()
35
40
  self.assertIsInstance(fake.badges, (list, tuple))
36
41
 
37
- def test_choices(self):
38
- """It should have a choice list on the badge field."""
39
- self.assertEqual(
40
- Fake._fields["badges"].field.document_type.kind.choices, list(Fake.__badges__.keys())
41
- )
42
-
43
42
  def test_get_badge_found(self):
44
43
  """It allow to get a badge by kind if present"""
45
44
  fake = Fake.objects.create()
@@ -155,3 +154,19 @@ class BadgeMixinTest(DBTestMixin, TestCase):
155
154
  with self.assertRaises(db.ValidationError):
156
155
  fake = Fake.objects.create()
157
156
  fake.add_badge("unknown")
157
+
158
+ def test_validation(self):
159
+ """It should validate default badges as well as extended ones"""
160
+ # Model badges can be extended in plugins, for example in udata-front
161
+ # for french only badges.
162
+ Fake.__badges__["new"] = "new"
163
+
164
+ fake = FakeBadge(kind="test")
165
+ fake.validate()
166
+
167
+ fake = FakeBadge(kind="new")
168
+ fake.validate()
169
+
170
+ with self.assertRaises(db.ValidationError):
171
+ fake = FakeBadge(kind="doesnotexist")
172
+ fake.validate()
@@ -521,8 +521,13 @@ class Resource(ResourceMixin, WithMetrics, db.EmbeddedDocument):
521
521
  self.dataset.save(*args, **kwargs)
522
522
 
523
523
 
524
+ def validate_badge(value):
525
+ if value not in Dataset.__badges__.keys():
526
+ raise db.ValidationError("Unknown badge type")
527
+
528
+
524
529
  class DatasetBadge(Badge):
525
- kind = db.StringField(required=True, choices=list(BADGES.keys()))
530
+ kind = db.StringField(required=True, validation=validate_badge)
526
531
 
527
532
 
528
533
  class DatasetBadgeMixin(BadgeMixin):
@@ -95,8 +95,13 @@ class OrganizationQuerySet(db.BaseQuerySet):
95
95
  return self(badges__kind=kind)
96
96
 
97
97
 
98
+ def validate_badge(value):
99
+ if value not in Organization.__badges__.keys():
100
+ raise db.ValidationError("Unknown badge type")
101
+
102
+
98
103
  class OrganizationBadge(Badge):
99
- kind = db.StringField(required=True, choices=list(BADGES.keys()))
104
+ kind = db.StringField(required=True, validation=validate_badge)
100
105
 
101
106
 
102
107
  class OrganizationBadgeMixin(BadgeMixin):
@@ -35,8 +35,13 @@ def check_url_does_not_exists(url):
35
35
  raise FieldValidationError(_("This URL is already registered"), field="url")
36
36
 
37
37
 
38
+ def validate_badge(value):
39
+ if value not in Reuse.__badges__.keys():
40
+ raise db.ValidationError("Unknown badge type")
41
+
42
+
38
43
  class ReuseBadge(Badge):
39
- kind = db.StringField(required=True, choices=list(BADGES.keys()))
44
+ kind = db.StringField(required=True, validation=validate_badge)
40
45
 
41
46
 
42
47
  class ReuseBadgeMixin(BadgeMixin):