oarepo-runtime 2.0.0.dev3__tar.gz → 2.0.0.dev4__tar.gz

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.
Files changed (33) hide show
  1. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/PKG-INFO +2 -1
  2. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/__init__.py +1 -1
  3. oarepo_runtime-2.0.0.dev4/oarepo_runtime/api.py +210 -0
  4. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/config.py +18 -6
  5. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/records/systemfields/publication_status.py +4 -2
  6. oarepo_runtime-2.0.0.dev4/oarepo_runtime/services/facets/__init__.py +12 -0
  7. oarepo_runtime-2.0.0.dev4/oarepo_runtime/services/facets/params.py +127 -0
  8. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/pyproject.toml +3 -0
  9. oarepo_runtime-2.0.0.dev3/oarepo_runtime/api.py +0 -111
  10. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/.gitignore +0 -0
  11. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/LICENSE +0 -0
  12. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/README.md +0 -0
  13. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/cli/__init__.py +0 -0
  14. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/cli/search.py +0 -0
  15. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/ext.py +0 -0
  16. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/proxies.py +0 -0
  17. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/records/__init__.py +0 -0
  18. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/records/drafts.py +0 -0
  19. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/records/mapping.py +0 -0
  20. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/records/pid_providers.py +0 -0
  21. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/records/systemfields/__init__.py +0 -0
  22. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/records/systemfields/mapping.py +0 -0
  23. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/services/__init__.py +0 -0
  24. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/services/config/__init__.py +0 -0
  25. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/services/config/link_conditions.py +0 -0
  26. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/services/config/permissions.py +0 -0
  27. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/services/records/__init__.py +0 -0
  28. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/services/records/links.py +0 -0
  29. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/services/records/mapping.py +0 -0
  30. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/services/results.py +0 -0
  31. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/services/schema/__init__.py +0 -0
  32. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/services/schema/i18n.py +0 -0
  33. {oarepo_runtime-2.0.0.dev3 → oarepo_runtime-2.0.0.dev4}/oarepo_runtime/services/schema/i18n_ui.py +0 -0
@@ -1,7 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oarepo-runtime
3
- Version: 2.0.0.dev3
3
+ Version: 2.0.0.dev4
4
4
  Summary: A set of runtime extensions of Invenio repository
5
+ Project-URL: Homepage, https://github.com/oarepo/oarepo-runtime
5
6
  License-Expression: MIT
6
7
  License-File: LICENSE
7
8
  Requires-Python: <3.14,>=3.13
@@ -19,6 +19,6 @@ from .api import Model
19
19
  from .ext import OARepoRuntime
20
20
  from .proxies import current_runtime
21
21
 
22
- __version__ = "2.0.0dev3"
22
+ __version__ = "2.0.0dev4"
23
23
 
24
24
  __all__ = ("Model", "OARepoRuntime", "__version__", "current_runtime")
@@ -0,0 +1,210 @@
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+
10
+ """Runtime API classes that are returned from the current_runtime instance."""
11
+
12
+ from __future__ import annotations
13
+
14
+ import dataclasses
15
+ from functools import cached_property
16
+ from typing import TYPE_CHECKING, cast
17
+
18
+ from flask import current_app
19
+ from invenio_base.utils import obj_or_import_string
20
+ from invenio_records_resources.proxies import current_service_registry
21
+
22
+ if TYPE_CHECKING:
23
+ from flask_babel.speaklater import LazyString
24
+ from flask_resources.responses import ResponseHandler
25
+ from flask_resources.serializers import BaseSerializer
26
+ from invenio_drafts_resources.records.api import Draft
27
+ from invenio_records_resources.records.api import RecordBase
28
+ from invenio_records_resources.resources.records.config import RecordResourceConfig
29
+ from invenio_records_resources.resources.records.resource import RecordResource
30
+ from invenio_records_resources.services import RecordService, RecordServiceConfig
31
+
32
+
33
+ @dataclasses.dataclass
34
+ class Export:
35
+ """Configuration of an export format.
36
+
37
+ Exports are shown on the record landing page and user can download them.
38
+ """
39
+
40
+ name: LazyString
41
+ """Name of the export format, human readable."""
42
+
43
+ mimetype: str
44
+ """MIME type of the export format."""
45
+
46
+ serializer: BaseSerializer
47
+ """Serializer used to serialize the record into the export format."""
48
+
49
+ display: bool = True
50
+ """Whether the export format is displayed in the UI."""
51
+
52
+ oai_metadata_prefix: str | None = None
53
+ """OAI metadata prefix, if applicable. If not set, the export can not be used in OAI-PMH responses."""
54
+
55
+ oai_schema: str | None = None
56
+ """OAI schema, if applicable. If not set, the export can not be used in OAI-PMH responses."""
57
+
58
+ oai_namespace: str | None = None
59
+ """OAI namespace, if applicable. If not set, the export can not be used in OAI-PMH responses."""
60
+
61
+
62
+ class Model[
63
+ S: RecordService = RecordService,
64
+ C: RecordServiceConfig = RecordServiceConfig,
65
+ R: RecordBase = RecordBase,
66
+ D: Draft = Draft,
67
+ # not sure why this is flagged by pyright as an error
68
+ RR: RecordResource = RecordResource, # pyright: ignore[reportGeneralTypeIssues]
69
+ RC: RecordResourceConfig = RecordResourceConfig,
70
+ ]:
71
+ """Model configuration.
72
+
73
+ Every model in oarepo repository must have this configuration which must be
74
+ registered in the `oarepo.runtime` extension via the OAREPO_MODELS config
75
+ variable.
76
+ """
77
+
78
+ name: str | LazyString
79
+ """Name of the model, human readable."""
80
+
81
+ version: str
82
+ """Version of the model, should be a valid semantic version."""
83
+
84
+ description: str | LazyString | None = None
85
+ """Description of the model, human readable."""
86
+
87
+ records_alias_enabled: bool = False
88
+ """Whether the records alias is enabled for this model. Such models will be searchable
89
+ via the `/api/records` endpoint."""
90
+
91
+ def __init__( # noqa: PLR0913 more attributes as we are creating a config
92
+ self,
93
+ name: str | LazyString,
94
+ version: str,
95
+ service: str | S,
96
+ resource_config: RC | str,
97
+ # params with default values
98
+ service_config: C | None = None,
99
+ description: str | LazyString | None = None,
100
+ record: type[R] | None = None,
101
+ draft: type[D] | None = None,
102
+ resource: str | RR = "invenio_records_resources.resources.records.resource.RecordResource",
103
+ exports: list[Export] | None = None,
104
+ records_alias_enabled: bool = True,
105
+ ):
106
+ """Initialize the model configuration.
107
+
108
+ :param name: Name of the model, human readable.
109
+ :param version: Version of the model, should be a valid semantic version.
110
+ :param description: Description of the model, human readable.
111
+ :param service: Name of the service inside the `current_service_registry` or
112
+ a configured service instance.
113
+ :param service_config: Service configuration, if not provided,
114
+ if will be taken from the service.
115
+ :param record: Record class, if not provided, it will be taken from the service
116
+ configuration.
117
+ :param draft: Draft class, if not provided, it will be taken from the service
118
+ configuration.
119
+ :param resource: Resource class or string import path to the resource class.
120
+ If not provided, it will be taken from the service configuration.
121
+ :param resource_config: Resource configuration, if not provided, it will be
122
+ taken from the resource class.
123
+ :param exports: List of export formats that can be used to export the record.
124
+ If not provided, no exports are available.
125
+ :param records_alias_enabled: Whether the records alias is enabled for this model.
126
+ Such models will be searchable via the `/api/records` endpoint.
127
+ """
128
+ self.name = name
129
+ self.version = version
130
+ self.description = description
131
+ self.records_alias_enabled = records_alias_enabled
132
+
133
+ # lazy getters ...
134
+ self._record = record
135
+ self._draft = draft
136
+ self._service = service
137
+ self._service_config = service_config
138
+ self._resource = resource
139
+ self._resource_config = resource_config
140
+ self._exports = exports or []
141
+
142
+ @property
143
+ def service(self) -> S:
144
+ """Get the service."""
145
+ if isinstance(self._service, str):
146
+ return cast(
147
+ "S",
148
+ current_service_registry.get(self._service), # type: ignore[attr-defined]
149
+ )
150
+ return self._service
151
+
152
+ @property
153
+ def service_config(self) -> C:
154
+ """Get the service configuration."""
155
+ if self._service_config is not None:
156
+ return self._service_config
157
+ return cast("C", self.service.config)
158
+
159
+ @property
160
+ def record_cls(self) -> type[R]:
161
+ """Get the record class."""
162
+ if self._record is None:
163
+ return cast("type[R]", self.service.config.record_cls)
164
+ return self._record
165
+
166
+ @property
167
+ def draft_cls(self) -> type[D] | None:
168
+ """Get the draft class."""
169
+ if self._draft is None:
170
+ if hasattr(self.service.config, "draft_cls"):
171
+ return cast("type[D]", self.service.config.draft_cls)
172
+ return None
173
+ return self._draft
174
+
175
+ @cached_property
176
+ def resource_config(self) -> RC:
177
+ """Get the resource configuration."""
178
+ if isinstance(self._resource_config, str):
179
+ resource_config_class: type[RC] = cast("type[RC]", obj_or_import_string(self._resource_config))
180
+ # need to import it here to avoid circular import issues
181
+ from .config import build_config
182
+
183
+ return build_config(resource_config_class, current_app)
184
+ return self._resource_config
185
+
186
+ @cached_property
187
+ def resource(self) -> RR:
188
+ """Get the resource."""
189
+ if isinstance(self._resource, str):
190
+ resource_class = obj_or_import_string(self._resource)
191
+ if resource_class is None:
192
+ raise ValueError(f"Resource class {self._resource} can not be None.")
193
+ return cast(
194
+ "RR",
195
+ resource_class(
196
+ service=self.service,
197
+ config=self.resource_config,
198
+ ),
199
+ )
200
+ return self._resource
201
+
202
+ @property
203
+ def exports(self) -> list[Export]:
204
+ """Get all exportable response handlers."""
205
+ return self._exports
206
+
207
+ @property
208
+ def response_handlers(self) -> dict[str, ResponseHandler]:
209
+ """Get all response handlers from the resource configuration."""
210
+ return cast("dict[str, ResponseHandler]", self.resource_config.response_handlers)
@@ -47,7 +47,9 @@ OAREPO_MODELS: dict[str, Model] = {
47
47
  version=vocabularies_version,
48
48
  service="vocabularies",
49
49
  description="Base (non-specialized) invenio vocabularies",
50
- global_search_enabled=False,
50
+ records_alias_enabled=False,
51
+ resource_config="invenio_vocabularies.resources.config.VocabulariesResourceConfig",
52
+ resource="invenio_vocabularies.resources.resource.VocabulariesResource",
51
53
  ),
52
54
  # affiliations
53
55
  "affiliations": Model(
@@ -55,7 +57,9 @@ OAREPO_MODELS: dict[str, Model] = {
55
57
  version=vocabularies_version,
56
58
  service="affiliations",
57
59
  description="Affiliations vocabulary",
58
- global_search_enabled=False,
60
+ records_alias_enabled=False,
61
+ resource_config="invenio_vocabularies.contrib.affiliations.resources.AffiliationsResourceConfig",
62
+ resource="invenio_vocabularies.contrib.affiliations.resources.AffiliationsResource",
59
63
  ),
60
64
  # funders
61
65
  "funders": Model(
@@ -63,7 +67,9 @@ OAREPO_MODELS: dict[str, Model] = {
63
67
  version=vocabularies_version,
64
68
  service="funders",
65
69
  description="Funders vocabulary",
66
- global_search_enabled=False,
70
+ records_alias_enabled=False,
71
+ resource_config="invenio_vocabularies.contrib.funders.resources.FundersResourceConfig",
72
+ resource="invenio_vocabularies.contrib.funders.resources.FundersResource",
67
73
  ),
68
74
  # awards
69
75
  "awards": Model(
@@ -71,7 +77,9 @@ OAREPO_MODELS: dict[str, Model] = {
71
77
  version=vocabularies_version,
72
78
  service="awards",
73
79
  description="Awards vocabulary",
74
- global_search_enabled=False,
80
+ records_alias_enabled=False,
81
+ resource_config="invenio_vocabularies.contrib.awards.resources.AwardsResourceConfig",
82
+ resource="invenio_vocabularies.contrib.awards.resources.AwardsResource",
75
83
  ),
76
84
  # names
77
85
  "names": Model(
@@ -79,7 +87,9 @@ OAREPO_MODELS: dict[str, Model] = {
79
87
  version=vocabularies_version,
80
88
  service="names",
81
89
  description="Names vocabulary",
82
- global_search_enabled=False,
90
+ records_alias_enabled=False,
91
+ resource_config="invenio_vocabularies.contrib.names.resources.NamesResourceConfig",
92
+ resource="invenio_vocabularies.contrib.names.resources.NamesResource",
83
93
  ),
84
94
  # subjects
85
95
  "subjects": Model(
@@ -87,6 +97,8 @@ OAREPO_MODELS: dict[str, Model] = {
87
97
  version=vocabularies_version,
88
98
  service="subjects",
89
99
  description="Subjects vocabulary",
90
- global_search_enabled=False,
100
+ records_alias_enabled=False,
101
+ resource_config="invenio_vocabularies.contrib.subjects.resources.SubjectsResourceConfig",
102
+ resource="invenio_vocabularies.contrib.subjects.resources.SubjectsResource",
91
103
  ),
92
104
  }
@@ -47,9 +47,11 @@ class PublicationStatusSystemField(MappingSystemFieldMixin, SystemField):
47
47
  @override
48
48
  def post_dump(self, record: RecordBase, data: dict, dumper: Dumper | None = None) -> None:
49
49
  if self.key is None:
50
- return
50
+ return # pragma: no cover
51
51
  if not self.attr_name:
52
- raise ValueError("attr_name must be set for PublicationStatusSystemField")
52
+ raise ValueError( # pragma: no cover
53
+ "attr_name must be set for PublicationStatusSystemField"
54
+ )
53
55
  data[self.key] = getattr(record, self.attr_name)
54
56
 
55
57
  def __get__(self, record: RecordBase | None, owner: Any = None) -> Any:
@@ -0,0 +1,12 @@
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+
10
+ """Facet parameter classes."""
11
+
12
+ from __future__ import annotations
@@ -0,0 +1,127 @@
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+
10
+ """Facet params."""
11
+
12
+ from __future__ import annotations
13
+
14
+ import copy
15
+ import logging
16
+ from typing import TYPE_CHECKING
17
+
18
+ from flask import current_app
19
+ from invenio_access.permissions import system_user_id
20
+ from invenio_app.helpers import obj_or_import_string
21
+ from invenio_records_resources.services.records.facets import FacetsResponse
22
+ from invenio_records_resources.services.records.params import FacetsParam
23
+
24
+ if TYPE_CHECKING:
25
+ from flask_principal import Identity
26
+ from invenio_records_resources.services.records.config import SearchOptions
27
+ from invenio_search.engine import dsl
28
+
29
+ log = logging.getLogger(__name__)
30
+
31
+
32
+ class GroupedFacetsParam(FacetsParam):
33
+ """Facet parameter class that supports grouping of facets."""
34
+
35
+ def __init__(self, config: SearchOptions):
36
+ """Initialize the facets parameter with the given config."""
37
+ super().__init__(config)
38
+ self._facets = {**config.facets}
39
+
40
+ @property
41
+ def facets(self) -> dict[str, dsl.Facet]:
42
+ """Return the facets dictionary."""
43
+ return self._facets
44
+
45
+ def identity_facet_groups(self, identity: Identity) -> list[str]:
46
+ """Return the facet groups for the given identity."""
47
+ if "OAREPO_FACET_GROUP_NAME" in current_app.config:
48
+ find_facet_groups_func = obj_or_import_string(current_app.config["OAREPO_FACET_GROUP_NAME"])
49
+ return find_facet_groups_func(identity, self.config, None) # type: ignore[no-any-return]
50
+
51
+ if hasattr(identity, "provides"):
52
+ return [need.value for need in identity.provides if need.method == "role"]
53
+
54
+ return []
55
+
56
+ @property
57
+ def facet_groups(self) -> dict[str, dict[str, dsl.Facet]] | None:
58
+ """Return the facet groups defined in the service config."""
59
+ if hasattr(self.config, "facet_groups"):
60
+ return self.config.facet_groups # type: ignore[no-any-return]
61
+ return None
62
+
63
+ def identity_facets(self, identity: Identity) -> dict[str, dsl.Facet]:
64
+ """Return the facets for the given identity."""
65
+ if not self.facet_groups:
66
+ return self.facets
67
+
68
+ has_system_user_id = identity.id == system_user_id
69
+ has_system_process_need = any(need.method == "system_process" for need in identity.provides)
70
+ if has_system_user_id or has_system_process_need:
71
+ return self.facets
72
+
73
+ return self._filter_user_facets(identity)
74
+
75
+ def aggregate_with_user_facets(self, search: dsl.Search, user_facets: dict[str, dsl.Facet]) -> dsl.Search:
76
+ """Add aggregations representing the user facets."""
77
+ for name, facet in user_facets.items():
78
+ agg = facet.get_aggregation()
79
+ search.aggs.bucket(name, agg)
80
+
81
+ return search
82
+
83
+ def filter(self, search: dsl.Search) -> dsl.Search:
84
+ """Apply a post filter on the search."""
85
+ if not self._filters:
86
+ return search
87
+
88
+ filters = list(self._filters.values())
89
+
90
+ _filter = filters[0]
91
+ for f in filters[1:]:
92
+ _filter &= f
93
+
94
+ return search.filter(_filter).post_filter(_filter)
95
+
96
+ def apply(self, identity: Identity, search: dsl.Search, params: dict) -> dsl.Search:
97
+ """Evaluate the facets on the search."""
98
+ facets_values = params.pop("facets", {})
99
+ for name, values in facets_values.items():
100
+ if name in self.facets:
101
+ self.add_filter(name, values)
102
+
103
+ user_facets = self.identity_facets(identity)
104
+ self_copy = copy.copy(self)
105
+ self_copy._facets = user_facets # noqa: SLF001 - TODO: this looks like a hack
106
+ search = search.response_class(FacetsResponse.create_response_cls(self_copy))
107
+
108
+ search = self.aggregate_with_user_facets(search, user_facets)
109
+ search = self.filter(search)
110
+
111
+ params.update(self.selected_values)
112
+
113
+ return search
114
+
115
+ def _filter_user_facets(self, identity: Identity) -> dict[str, dsl.Facet]:
116
+ """Filter user facets based on the identity."""
117
+ user_facets = {}
118
+ if not self.facet_groups:
119
+ user_facets.update(self.facets)
120
+ else:
121
+ self.facets.clear() # TODO: why is this needed?
122
+ user_facets.update(self.facet_groups.get("default", {}))
123
+
124
+ groups = self.identity_facet_groups(identity)
125
+ for group in groups:
126
+ user_facets.update((self.facet_groups or {}).get(group, {}))
127
+ return user_facets
@@ -14,6 +14,9 @@ dependencies = [
14
14
  ]
15
15
  requires-python = ">=3.13,<3.14"
16
16
 
17
+ [project.urls]
18
+ Homepage = "https://github.com/oarepo/oarepo-runtime"
19
+
17
20
  [project.optional-dependencies]
18
21
  dev = [
19
22
  "pytest>=7.1.2",
@@ -1,111 +0,0 @@
1
- #
2
- # Copyright (c) 2025 CESNET z.s.p.o.
3
- #
4
- # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
- #
6
- # oarepo-runtime is free software; you can redistribute it and/or modify it
7
- # under the terms of the MIT License; see LICENSE file for more details.
8
- #
9
-
10
- """Runtime API classes that are returned from the current_runtime instance."""
11
-
12
- from __future__ import annotations
13
-
14
- from typing import TYPE_CHECKING, cast
15
-
16
- from invenio_records_resources.proxies import current_service_registry
17
-
18
- if TYPE_CHECKING:
19
- from flask_babel.speaklater import LazyString
20
- from invenio_drafts_resources.records.api import Draft
21
- from invenio_records_resources.records.api import RecordBase
22
- from invenio_records_resources.services import RecordService, RecordServiceConfig
23
-
24
-
25
- class Model[
26
- S: RecordService = RecordService,
27
- C: RecordServiceConfig = RecordServiceConfig,
28
- R: RecordBase = RecordBase,
29
- D: Draft = Draft,
30
- ]:
31
- """Model configuration.
32
-
33
- Every model in oarepo repository must have this configuration which must be
34
- registered in the `oarepo.runtime` extension via the OAREPO_MODELS config
35
- variable.
36
- """
37
-
38
- name: str | LazyString
39
- version: str
40
- description: str | LazyString | None = None
41
- global_search_enabled: bool = False
42
-
43
- def __init__( # noqa: PLR0913 more attributes as we are creating a config
44
- self,
45
- name: str | LazyString,
46
- version: str,
47
- service: str | S,
48
- # params with default values
49
- service_config: C | None = None,
50
- description: str | LazyString | None = None,
51
- record: type[R] | None = None,
52
- draft: type[D] | None = None,
53
- global_search_enabled: bool = True,
54
- ):
55
- """Initialize the model configuration.
56
-
57
- :param name: Name of the model, human readable.
58
- :param version: Version of the model, should be a valid semantic version.
59
- :param description: Description of the model, human readable.
60
- :param service: Name of the service inside the `current_service_registry` or
61
- a configured service instance.
62
- :param service_config: Service configuration, if not provided,
63
- if will be taken from the service.
64
- :param record: Record class, if not provided, it will be taken from the service
65
- configuration.
66
- :param draft: Draft class, if not provided, it will be taken from the service
67
- configuration.
68
- """
69
- self.name = name
70
- self.version = version
71
- self.description = description
72
- self.global_search_enabled = global_search_enabled
73
-
74
- # lazy getters ...
75
- self._record = record
76
- self._draft = draft
77
- self._service = service
78
- self._service_config = service_config
79
-
80
- @property
81
- def service(self) -> S:
82
- """Get the service."""
83
- if isinstance(self._service, str):
84
- return cast(
85
- "S",
86
- current_service_registry.get(self._service), # type: ignore[attr-defined]
87
- )
88
- return self._service
89
-
90
- @property
91
- def service_config(self) -> C:
92
- """Get the service configuration."""
93
- if self._service_config is not None:
94
- return self._service_config
95
- return cast("C", self.service.config)
96
-
97
- @property
98
- def record_cls(self) -> type[R]:
99
- """Get the record class."""
100
- if self._record is None:
101
- return cast("type[R]", self.service.config.record_cls)
102
- return self._record
103
-
104
- @property
105
- def draft_cls(self) -> type[D] | None:
106
- """Get the draft class."""
107
- if self._draft is None:
108
- if hasattr(self.service.config, "draft_cls"):
109
- return cast("type[D]", self.service.config.draft_cls)
110
- return None
111
- return self._draft