oarepo-runtime 1.5.29__py3-none-any.whl → 1.5.31__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.
- oarepo_runtime/services/custom_fields/mappings.py +6 -3
- oarepo_runtime/services/results.py +18 -2
- oarepo_runtime/services/schema/oneofschema.py +192 -0
- oarepo_runtime/services/schema/polymorphic.py +2 -2
- {oarepo_runtime-1.5.29.dist-info → oarepo_runtime-1.5.31.dist-info}/METADATA +1 -1
- {oarepo_runtime-1.5.29.dist-info → oarepo_runtime-1.5.31.dist-info}/RECORD +10 -9
- {oarepo_runtime-1.5.29.dist-info → oarepo_runtime-1.5.31.dist-info}/LICENSE +0 -0
- {oarepo_runtime-1.5.29.dist-info → oarepo_runtime-1.5.31.dist-info}/WHEEL +0 -0
- {oarepo_runtime-1.5.29.dist-info → oarepo_runtime-1.5.31.dist-info}/entry_points.txt +0 -0
- {oarepo_runtime-1.5.29.dist-info → oarepo_runtime-1.5.31.dist-info}/top_level.txt +0 -0
@@ -68,11 +68,14 @@ def prepare_cf_indices():
|
|
68
68
|
service: RecordService
|
69
69
|
for service in current_service_registry._services.values():
|
70
70
|
config: RecordServiceConfig = service.config
|
71
|
-
|
71
|
+
record_class = getattr(config, "record_cls", None)
|
72
|
+
if record_class:
|
73
|
+
prepare_cf_index(record_class, config)
|
74
|
+
parent_class = getattr(record_class, "parent_record_cls", None)
|
75
|
+
prepare_cf_index(parent_class, config)
|
72
76
|
|
73
77
|
|
74
|
-
def prepare_cf_index(config
|
75
|
-
record_class = getattr(config, "record_cls", None)
|
78
|
+
def prepare_cf_index(record_class, config):
|
76
79
|
if not record_class:
|
77
80
|
return
|
78
81
|
|
@@ -6,6 +6,11 @@ from invenio_records_resources.services.records.results import (
|
|
6
6
|
)
|
7
7
|
|
8
8
|
|
9
|
+
class ResultsComponent:
|
10
|
+
def update_data(self, identity, record, projection, expand):
|
11
|
+
raise NotImplementedError
|
12
|
+
|
13
|
+
|
9
14
|
class RecordItem(BaseRecordItem):
|
10
15
|
"""Single record result."""
|
11
16
|
|
@@ -17,7 +22,12 @@ class RecordItem(BaseRecordItem):
|
|
17
22
|
return self._data
|
18
23
|
_data = super().data
|
19
24
|
for c in self.components:
|
20
|
-
c.update_data(
|
25
|
+
c.update_data(
|
26
|
+
identity=self._identity,
|
27
|
+
record=self._record,
|
28
|
+
projection=_data,
|
29
|
+
expand=self._expand,
|
30
|
+
)
|
21
31
|
return _data
|
22
32
|
|
23
33
|
|
@@ -47,6 +57,12 @@ class RecordList(BaseRecordList):
|
|
47
57
|
projection["links"] = self._links_item_tpl.expand(
|
48
58
|
self._identity, record
|
49
59
|
)
|
60
|
+
# todo optimization viz FieldsResolver
|
50
61
|
for c in self.components:
|
51
|
-
c.update_data(
|
62
|
+
c.update_data(
|
63
|
+
identity=self._identity,
|
64
|
+
record=self._record,
|
65
|
+
projection=projection,
|
66
|
+
expand=self._expand,
|
67
|
+
)
|
52
68
|
yield projection
|
@@ -0,0 +1,192 @@
|
|
1
|
+
import typing
|
2
|
+
|
3
|
+
from marshmallow import Schema, ValidationError
|
4
|
+
|
5
|
+
|
6
|
+
class OneOfSchema(Schema):
|
7
|
+
"""
|
8
|
+
This is a special kind of schema that actually multiplexes other schemas
|
9
|
+
based on object type. When serializing values, it uses get_obj_type() method
|
10
|
+
to get object type name. Then it uses `type_schemas` name-to-Schema mapping
|
11
|
+
to get schema for that particular object type, serializes object using that
|
12
|
+
schema and adds an extra "type" field with name of object type.
|
13
|
+
Deserialization is reverse.
|
14
|
+
|
15
|
+
Example:
|
16
|
+
|
17
|
+
class Foo(object):
|
18
|
+
def __init__(self, foo):
|
19
|
+
self.foo = foo
|
20
|
+
|
21
|
+
class Bar(object):
|
22
|
+
def __init__(self, bar):
|
23
|
+
self.bar = bar
|
24
|
+
|
25
|
+
class FooSchema(marshmallow.Schema):
|
26
|
+
foo = marshmallow.fields.String(required=True)
|
27
|
+
|
28
|
+
@marshmallow.post_load
|
29
|
+
def make_foo(self, data, **kwargs):
|
30
|
+
return Foo(**data)
|
31
|
+
|
32
|
+
class BarSchema(marshmallow.Schema):
|
33
|
+
bar = marshmallow.fields.Integer(required=True)
|
34
|
+
|
35
|
+
@marshmallow.post_load
|
36
|
+
def make_bar(self, data, **kwargs):
|
37
|
+
return Bar(**data)
|
38
|
+
|
39
|
+
class MyUberSchema(marshmallow.OneOfSchema):
|
40
|
+
type_schemas = {
|
41
|
+
'foo': FooSchema,
|
42
|
+
'bar': BarSchema,
|
43
|
+
}
|
44
|
+
|
45
|
+
def get_obj_type(self, obj):
|
46
|
+
if isinstance(obj, Foo):
|
47
|
+
return 'foo'
|
48
|
+
elif isinstance(obj, Bar):
|
49
|
+
return 'bar'
|
50
|
+
else:
|
51
|
+
raise Exception('Unknown object type: %s' % repr(obj))
|
52
|
+
|
53
|
+
MyUberSchema().dump([Foo(foo='hello'), Bar(bar=123)], many=True)
|
54
|
+
# => [{'type': 'foo', 'foo': 'hello'}, {'type': 'bar', 'bar': 123}]
|
55
|
+
|
56
|
+
You can control type field name added to serialized object representation by
|
57
|
+
setting `type_field` class property.
|
58
|
+
"""
|
59
|
+
|
60
|
+
type_field = "type"
|
61
|
+
type_field_remove = True
|
62
|
+
type_schemas: typing.Mapping[str, typing.Union[typing.Type[Schema], Schema]] = {}
|
63
|
+
|
64
|
+
def get_obj_type(self, obj):
|
65
|
+
"""Returns name of the schema during dump() calls, given the object
|
66
|
+
being dumped."""
|
67
|
+
return obj.__class__.__name__
|
68
|
+
|
69
|
+
def get_data_type(self, data):
|
70
|
+
"""Returns name of the schema during load() calls, given the data being
|
71
|
+
loaded. Defaults to looking up `type_field` in the data."""
|
72
|
+
data_type = data.get(self.type_field)
|
73
|
+
if self.type_field in data and self.type_field_remove:
|
74
|
+
data.pop(self.type_field)
|
75
|
+
return data_type
|
76
|
+
|
77
|
+
def dump(self, obj, *, many=None, **kwargs):
|
78
|
+
errors = {}
|
79
|
+
result_data = []
|
80
|
+
result_errors = {}
|
81
|
+
many = self.many if many is None else bool(many)
|
82
|
+
if not many:
|
83
|
+
result = result_data = self._dump(obj, **kwargs)
|
84
|
+
else:
|
85
|
+
for idx, o in enumerate(obj):
|
86
|
+
try:
|
87
|
+
result = self._dump(o, **kwargs)
|
88
|
+
result_data.append(result)
|
89
|
+
except ValidationError as error:
|
90
|
+
result_errors[idx] = error.normalized_messages()
|
91
|
+
result_data.append(error.valid_data)
|
92
|
+
|
93
|
+
result = result_data
|
94
|
+
errors = result_errors
|
95
|
+
|
96
|
+
if not errors:
|
97
|
+
return result
|
98
|
+
else:
|
99
|
+
exc = ValidationError(errors, data=obj, valid_data=result)
|
100
|
+
raise exc
|
101
|
+
|
102
|
+
def _dump(self, obj, *, update_fields=True, **kwargs):
|
103
|
+
obj_type = self.get_obj_type(obj)
|
104
|
+
if obj_type is None:
|
105
|
+
return (
|
106
|
+
None,
|
107
|
+
{"_schema": "Unknown object class: %s" % obj.__class__.__name__},
|
108
|
+
)
|
109
|
+
|
110
|
+
type_schema = self.type_schemas.get(obj_type)
|
111
|
+
if not type_schema:
|
112
|
+
return None, {"_schema": "Unsupported object type: %s" % obj_type}
|
113
|
+
|
114
|
+
schema = type_schema if isinstance(type_schema, Schema) else type_schema()
|
115
|
+
|
116
|
+
schema.context.update(getattr(self, "context", {}))
|
117
|
+
|
118
|
+
result = schema.dump(obj, many=False, **kwargs)
|
119
|
+
if result is not None:
|
120
|
+
result[self.type_field] = obj_type
|
121
|
+
return result
|
122
|
+
|
123
|
+
def load(self, data, *, many=None, partial=None, unknown=None, **kwargs):
|
124
|
+
errors = {}
|
125
|
+
result_data = []
|
126
|
+
result_errors = {}
|
127
|
+
many = self.many if many is None else bool(many)
|
128
|
+
if partial is None:
|
129
|
+
partial = self.partial
|
130
|
+
if not many:
|
131
|
+
try:
|
132
|
+
result_data = self._load(
|
133
|
+
data, partial=partial, unknown=unknown, **kwargs
|
134
|
+
)
|
135
|
+
except ValidationError as error:
|
136
|
+
result_errors = error.normalized_messages()
|
137
|
+
result_data = error.valid_data
|
138
|
+
else:
|
139
|
+
for idx, item in enumerate(data):
|
140
|
+
try:
|
141
|
+
result = self._load(item, partial=partial, **kwargs)
|
142
|
+
result_data.append(result)
|
143
|
+
except ValidationError as error:
|
144
|
+
result_errors[idx] = error.normalized_messages()
|
145
|
+
result_data.append(error.valid_data)
|
146
|
+
|
147
|
+
result = result_data
|
148
|
+
errors = result_errors
|
149
|
+
|
150
|
+
if not errors:
|
151
|
+
return result
|
152
|
+
else:
|
153
|
+
exc = ValidationError(errors, data=data, valid_data=result)
|
154
|
+
raise exc
|
155
|
+
|
156
|
+
def _load(self, data, *, partial=None, unknown=None, **kwargs):
|
157
|
+
if not isinstance(data, dict):
|
158
|
+
raise ValidationError({"_schema": "Invalid data type: %s" % data})
|
159
|
+
|
160
|
+
data = dict(data)
|
161
|
+
unknown = unknown or self.unknown
|
162
|
+
data_type = self.get_data_type(data)
|
163
|
+
|
164
|
+
if data_type is None:
|
165
|
+
raise ValidationError(
|
166
|
+
{self.type_field: ["Missing data for required field."]}
|
167
|
+
)
|
168
|
+
|
169
|
+
try:
|
170
|
+
type_schema = self.type_schemas.get(data_type)
|
171
|
+
except TypeError as error:
|
172
|
+
# data_type could be unhashable
|
173
|
+
raise ValidationError(
|
174
|
+
{self.type_field: ["Invalid value: %s" % data_type]}
|
175
|
+
) from error
|
176
|
+
if not type_schema:
|
177
|
+
raise ValidationError(
|
178
|
+
{self.type_field: ["Unsupported value: %s" % data_type]}
|
179
|
+
)
|
180
|
+
|
181
|
+
schema = type_schema if isinstance(type_schema, Schema) else type_schema()
|
182
|
+
|
183
|
+
schema.context.update(getattr(self, "context", {}))
|
184
|
+
|
185
|
+
return schema.load(data, many=False, partial=partial, unknown=unknown, **kwargs)
|
186
|
+
|
187
|
+
def validate(self, data, *, many=None, partial=None):
|
188
|
+
try:
|
189
|
+
self.load(data, many=many, partial=partial)
|
190
|
+
except ValidationError as ve:
|
191
|
+
return ve.messages
|
192
|
+
return {}
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from functools import cached_property
|
2
2
|
|
3
3
|
import marshmallow as ma
|
4
|
-
from marshmallow_oneofschema import OneOfSchema
|
5
|
-
|
4
|
+
#from marshmallow_oneofschema import OneOfSchema
|
5
|
+
from oarepo_runtime.services.schema.oneofschema import OneOfSchema
|
6
6
|
|
7
7
|
class PolymorphicSchema(OneOfSchema):
|
8
8
|
type_field_remove = False
|
@@ -70,13 +70,13 @@ oarepo_runtime/resources/localized_ui_json_serializer.py,sha256=3V9cJaG_e1PMXKVX
|
|
70
70
|
oarepo_runtime/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
71
71
|
oarepo_runtime/services/components.py,sha256=9wt9CmoCFA8Utbb8eNA-Mvzo5LCApHT9zHpWIWZNyXY,1506
|
72
72
|
oarepo_runtime/services/generators.py,sha256=V582uA813AIXnFhzqUwakmDgBOI1SQe3XZeJtUXNbwM,872
|
73
|
-
oarepo_runtime/services/results.py,sha256=
|
73
|
+
oarepo_runtime/services/results.py,sha256=gcITj-bWPwrGdNGGEiNWroRC-JLUoBaZbAMOZeDrQRg,1955
|
74
74
|
oarepo_runtime/services/search.py,sha256=ywfwGH7oAM44WeOSjlIsY_qoCMZJ1TlTLd_NgE2ow3Y,5296
|
75
75
|
oarepo_runtime/services/config/__init__.py,sha256=SCqww5sV8qh3gmev6TE8EyJbD58juIEDCm_7MEHxtSg,440
|
76
76
|
oarepo_runtime/services/config/permissions_presets.py,sha256=zApeA-2DYAlD--SzVz3vq_OFjq48Ko0pe08e4o2vxr4,6114
|
77
77
|
oarepo_runtime/services/config/service.py,sha256=2aq5jobPH22T1QqlJDommvAxJwo9aQGiqK5q-k-l9CA,4668
|
78
78
|
oarepo_runtime/services/custom_fields/__init__.py,sha256=xJ7XEyMJHPfIgX5JKpgpwh7SYc9Zee2dC5oC8cm99Qc,2282
|
79
|
-
oarepo_runtime/services/custom_fields/mappings.py,sha256=
|
79
|
+
oarepo_runtime/services/custom_fields/mappings.py,sha256=CYJJAGo4k6Bv6D5FLy4Js9EjhWWNThJBkosAMdZIXzI,4600
|
80
80
|
oarepo_runtime/services/expansions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
81
81
|
oarepo_runtime/services/expansions/expandable_fields.py,sha256=7DWKFL6ml8J7zGI6wm9LO7Xd6R0LSylsuq4lyRumNHQ,745
|
82
82
|
oarepo_runtime/services/expansions/service.py,sha256=HaEy76XOhDf__sQ91hi-8iH1hthM9q07pRhOmyZyVrs,144
|
@@ -100,7 +100,8 @@ oarepo_runtime/services/schema/i18n.py,sha256=myyg0tU8up0BmMt9IESKD91w5KC0V9v8Qa
|
|
100
100
|
oarepo_runtime/services/schema/i18n_ui.py,sha256=18tA6uA067TP_wcit47hTel2M4hz88wYtwBgaeZDrew,1880
|
101
101
|
oarepo_runtime/services/schema/i18n_validation.py,sha256=fyMTi2Rw-KiHv7c7HN61zGxRVa9sAjAEEkAL5wUyKNo,236
|
102
102
|
oarepo_runtime/services/schema/marshmallow.py,sha256=LmcSxvbZ9jIhkNHCqqxt1SA2UNijoDmIzqli1MkoTrE,1153
|
103
|
-
oarepo_runtime/services/schema/
|
103
|
+
oarepo_runtime/services/schema/oneofschema.py,sha256=X_pXzrkYcLGGAtGN1qltrz45OzD_atrJHkHkp3L01xg,6660
|
104
|
+
oarepo_runtime/services/schema/polymorphic.py,sha256=f9yC7MGVynAFGM0fXIq0NbqGJxI65Xpi8GaM2ZM4c6Q,561
|
104
105
|
oarepo_runtime/services/schema/ui.py,sha256=caRca4vuUVoLew3gkhbPQYGaLXB8C67E5jxz45-9zkE,3663
|
105
106
|
oarepo_runtime/services/schema/validation.py,sha256=fahqKGDdIYWux5ZeoljrEe8VD2fDZR9VpfvYmTYAmpw,1050
|
106
107
|
oarepo_runtime/translations/default_translations.py,sha256=060GBlA1ghWxfeumo6NqxCCZDb-6OezOuF6pr-_GEOQ,104
|
@@ -114,9 +115,9 @@ oarepo_runtime/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
114
115
|
oarepo_runtime/utils/functools.py,sha256=gKS9YZtlIYcDvdNA9cmYO00yjiXBYV1jg8VpcRUyQyg,1324
|
115
116
|
oarepo_runtime/utils/path.py,sha256=V1NVyk3m12_YLbj7QHYvUpE1wScO78bYsX1LOLeXDkI,3108
|
116
117
|
tests/pkg_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
117
|
-
oarepo_runtime-1.5.
|
118
|
-
oarepo_runtime-1.5.
|
119
|
-
oarepo_runtime-1.5.
|
120
|
-
oarepo_runtime-1.5.
|
121
|
-
oarepo_runtime-1.5.
|
122
|
-
oarepo_runtime-1.5.
|
118
|
+
oarepo_runtime-1.5.31.dist-info/LICENSE,sha256=h2uWz0OaB3EN-J1ImdGJZzc7yvfQjvHVYdUhQ-H7ypY,1064
|
119
|
+
oarepo_runtime-1.5.31.dist-info/METADATA,sha256=may40TQGXta2TdoZNWFGiyYWRoaJxV54J2UtH6gaKzg,4680
|
120
|
+
oarepo_runtime-1.5.31.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
121
|
+
oarepo_runtime-1.5.31.dist-info/entry_points.txt,sha256=QrlXAKuPDVBinaSh_v3yO9_Nb9ZNmJCJ0VFcCW-z0Jg,327
|
122
|
+
oarepo_runtime-1.5.31.dist-info/top_level.txt,sha256=bHhlkT1_RQC4IkfTQCqA3iN4KCB6cSFQlsXpQMSP-bE,21
|
123
|
+
oarepo_runtime-1.5.31.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|