oarepo-runtime 1.5.29__py3-none-any.whl → 1.5.31__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|