oarepo-runtime 1.10.2__py3-none-any.whl → 2.0.0.dev3__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/__init__.py +24 -0
- oarepo_runtime/api.py +111 -0
- oarepo_runtime/cli/__init__.py +10 -21
- oarepo_runtime/cli/search.py +34 -0
- oarepo_runtime/config.py +86 -13
- oarepo_runtime/ext.py +64 -82
- oarepo_runtime/proxies.py +21 -5
- oarepo_runtime/records/__init__.py +11 -50
- oarepo_runtime/records/drafts.py +24 -18
- oarepo_runtime/records/mapping.py +84 -0
- oarepo_runtime/records/pid_providers.py +43 -7
- oarepo_runtime/records/systemfields/__init__.py +15 -33
- oarepo_runtime/records/systemfields/mapping.py +41 -24
- oarepo_runtime/records/systemfields/publication_status.py +59 -0
- oarepo_runtime/services/__init__.py +12 -0
- oarepo_runtime/services/config/__init__.py +15 -21
- oarepo_runtime/services/config/link_conditions.py +69 -75
- oarepo_runtime/services/config/permissions.py +62 -0
- oarepo_runtime/services/records/__init__.py +14 -1
- oarepo_runtime/services/records/links.py +21 -11
- oarepo_runtime/services/records/mapping.py +42 -0
- oarepo_runtime/services/results.py +98 -109
- oarepo_runtime/services/schema/__init__.py +12 -44
- oarepo_runtime/services/schema/i18n.py +47 -22
- oarepo_runtime/services/schema/i18n_ui.py +61 -24
- {oarepo_runtime-1.10.2.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/METADATA +9 -21
- oarepo_runtime-2.0.0.dev3.dist-info/RECORD +30 -0
- {oarepo_runtime-1.10.2.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/WHEEL +1 -2
- oarepo_runtime-2.0.0.dev3.dist-info/entry_points.txt +5 -0
- oarepo_runtime/cli/assets.py +0 -145
- oarepo_runtime/cli/base.py +0 -25
- oarepo_runtime/cli/cf.py +0 -15
- oarepo_runtime/cli/check.py +0 -167
- oarepo_runtime/cli/configuration.py +0 -51
- oarepo_runtime/cli/fixtures.py +0 -167
- oarepo_runtime/cli/index.py +0 -272
- oarepo_runtime/cli/permissions/__init__.py +0 -6
- oarepo_runtime/cli/permissions/base.py +0 -26
- oarepo_runtime/cli/permissions/evaluate.py +0 -63
- oarepo_runtime/cli/permissions/list.py +0 -239
- oarepo_runtime/cli/permissions/search.py +0 -121
- oarepo_runtime/cli/validate.py +0 -150
- oarepo_runtime/datastreams/__init__.py +0 -38
- oarepo_runtime/datastreams/asynchronous.py +0 -247
- oarepo_runtime/datastreams/catalogue.py +0 -150
- oarepo_runtime/datastreams/datastreams.py +0 -152
- oarepo_runtime/datastreams/errors.py +0 -54
- oarepo_runtime/datastreams/ext.py +0 -41
- oarepo_runtime/datastreams/fixtures.py +0 -265
- oarepo_runtime/datastreams/json.py +0 -4
- oarepo_runtime/datastreams/readers/__init__.py +0 -39
- oarepo_runtime/datastreams/readers/attachments.py +0 -51
- oarepo_runtime/datastreams/readers/excel.py +0 -123
- oarepo_runtime/datastreams/readers/json.py +0 -27
- oarepo_runtime/datastreams/readers/service.py +0 -54
- oarepo_runtime/datastreams/readers/yaml.py +0 -14
- oarepo_runtime/datastreams/semi_asynchronous.py +0 -91
- oarepo_runtime/datastreams/synchronous.py +0 -70
- oarepo_runtime/datastreams/transformers.py +0 -18
- oarepo_runtime/datastreams/types.py +0 -323
- oarepo_runtime/datastreams/utils.py +0 -131
- oarepo_runtime/datastreams/writers/__init__.py +0 -21
- oarepo_runtime/datastreams/writers/attachments_file.py +0 -92
- oarepo_runtime/datastreams/writers/attachments_service.py +0 -118
- oarepo_runtime/datastreams/writers/publish.py +0 -70
- oarepo_runtime/datastreams/writers/service.py +0 -175
- oarepo_runtime/datastreams/writers/utils.py +0 -30
- oarepo_runtime/datastreams/writers/validation_errors.py +0 -20
- oarepo_runtime/datastreams/writers/yaml.py +0 -56
- oarepo_runtime/ext_config.py +0 -67
- oarepo_runtime/i18n/__init__.py +0 -3
- oarepo_runtime/info/__init__.py +0 -0
- oarepo_runtime/info/check.py +0 -95
- oarepo_runtime/info/permissions/__init__.py +0 -0
- oarepo_runtime/info/permissions/debug.py +0 -191
- oarepo_runtime/info/views.py +0 -586
- oarepo_runtime/profile.py +0 -60
- oarepo_runtime/records/dumpers/__init__.py +0 -8
- oarepo_runtime/records/dumpers/edtf_interval.py +0 -38
- oarepo_runtime/records/dumpers/multilingual_dumper.py +0 -34
- oarepo_runtime/records/entity_resolvers/__init__.py +0 -13
- oarepo_runtime/records/entity_resolvers/proxies.py +0 -57
- oarepo_runtime/records/mappings/__init__.py +0 -0
- oarepo_runtime/records/mappings/rdm_parent_mapping.json +0 -483
- oarepo_runtime/records/owners/__init__.py +0 -3
- oarepo_runtime/records/owners/registry.py +0 -22
- oarepo_runtime/records/relations/__init__.py +0 -22
- oarepo_runtime/records/relations/base.py +0 -296
- oarepo_runtime/records/relations/internal.py +0 -46
- oarepo_runtime/records/relations/lookup.py +0 -28
- oarepo_runtime/records/relations/pid_relation.py +0 -102
- oarepo_runtime/records/systemfields/featured_file.py +0 -45
- oarepo_runtime/records/systemfields/has_draftcheck.py +0 -47
- oarepo_runtime/records/systemfields/icu.py +0 -371
- oarepo_runtime/records/systemfields/owner.py +0 -115
- oarepo_runtime/records/systemfields/record_status.py +0 -35
- oarepo_runtime/records/systemfields/selectors.py +0 -98
- oarepo_runtime/records/systemfields/synthetic.py +0 -130
- oarepo_runtime/resources/__init__.py +0 -4
- oarepo_runtime/resources/config.py +0 -12
- oarepo_runtime/resources/file_resource.py +0 -15
- oarepo_runtime/resources/json_serializer.py +0 -27
- oarepo_runtime/resources/localized_ui_json_serializer.py +0 -54
- oarepo_runtime/resources/resource.py +0 -53
- oarepo_runtime/resources/responses.py +0 -20
- oarepo_runtime/services/components.py +0 -429
- oarepo_runtime/services/config/draft_link.py +0 -23
- oarepo_runtime/services/config/permissions_presets.py +0 -174
- oarepo_runtime/services/config/service.py +0 -117
- oarepo_runtime/services/custom_fields/__init__.py +0 -80
- oarepo_runtime/services/custom_fields/mappings.py +0 -188
- oarepo_runtime/services/entity/__init__.py +0 -0
- oarepo_runtime/services/entity/config.py +0 -14
- oarepo_runtime/services/entity/schema.py +0 -9
- oarepo_runtime/services/entity/service.py +0 -48
- oarepo_runtime/services/expansions/__init__.py +0 -0
- oarepo_runtime/services/expansions/expandable_fields.py +0 -21
- oarepo_runtime/services/expansions/service.py +0 -4
- oarepo_runtime/services/facets/__init__.py +0 -33
- oarepo_runtime/services/facets/base.py +0 -12
- oarepo_runtime/services/facets/date.py +0 -72
- oarepo_runtime/services/facets/enum.py +0 -11
- oarepo_runtime/services/facets/facet_groups_names.py +0 -17
- oarepo_runtime/services/facets/max_facet.py +0 -13
- oarepo_runtime/services/facets/multilingual_facet.py +0 -33
- oarepo_runtime/services/facets/nested_facet.py +0 -32
- oarepo_runtime/services/facets/params.py +0 -192
- oarepo_runtime/services/facets/year_histogram.py +0 -200
- oarepo_runtime/services/files/__init__.py +0 -8
- oarepo_runtime/services/files/components.py +0 -62
- oarepo_runtime/services/files/service.py +0 -16
- oarepo_runtime/services/generators.py +0 -10
- oarepo_runtime/services/permissions/__init__.py +0 -3
- oarepo_runtime/services/permissions/generators.py +0 -103
- oarepo_runtime/services/relations/__init__.py +0 -0
- oarepo_runtime/services/relations/components.py +0 -15
- oarepo_runtime/services/relations/errors.py +0 -18
- oarepo_runtime/services/relations/mapping.py +0 -38
- oarepo_runtime/services/schema/cf.py +0 -13
- oarepo_runtime/services/schema/i18n_validation.py +0 -7
- oarepo_runtime/services/schema/marshmallow.py +0 -44
- oarepo_runtime/services/schema/marshmallow_to_json_schema.py +0 -72
- oarepo_runtime/services/schema/oneofschema.py +0 -192
- oarepo_runtime/services/schema/polymorphic.py +0 -21
- oarepo_runtime/services/schema/rdm.py +0 -75
- oarepo_runtime/services/schema/rdm_ui.py +0 -156
- oarepo_runtime/services/schema/ui.py +0 -251
- oarepo_runtime/services/schema/validation.py +0 -70
- oarepo_runtime/services/search.py +0 -282
- oarepo_runtime/services/service.py +0 -61
- oarepo_runtime/tasks.py +0 -6
- oarepo_runtime/translations/cs/LC_MESSAGES/messages.mo +0 -0
- oarepo_runtime/translations/cs/LC_MESSAGES/messages.po +0 -85
- oarepo_runtime/translations/default_translations.py +0 -6
- oarepo_runtime/translations/en/LC_MESSAGES/messages.mo +0 -0
- oarepo_runtime/translations/en/LC_MESSAGES/messages.po +0 -89
- oarepo_runtime/translations/messages.pot +0 -91
- oarepo_runtime/uow.py +0 -146
- oarepo_runtime/utils/__init__.py +0 -0
- oarepo_runtime/utils/functools.py +0 -37
- oarepo_runtime/utils/identity_utils.py +0 -35
- oarepo_runtime/utils/index.py +0 -11
- oarepo_runtime/utils/path.py +0 -97
- oarepo_runtime-1.10.2.dist-info/RECORD +0 -163
- oarepo_runtime-1.10.2.dist-info/entry_points.txt +0 -16
- oarepo_runtime-1.10.2.dist-info/top_level.txt +0 -2
- tests/marshmallow_to_json/__init__.py +0 -0
- tests/marshmallow_to_json/test_datacite_ui_schema.py +0 -1410
- tests/marshmallow_to_json/test_simple_schema.py +0 -52
- tests/pkg_data/__init__.py +0 -0
- {oarepo_runtime-1.10.2.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,84 @@
|
|
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
|
+
"""Module to update the mapping of system fields in a record class."""
|
10
|
+
|
11
|
+
from __future__ import annotations
|
12
|
+
|
13
|
+
import inspect
|
14
|
+
from collections.abc import Iterable
|
15
|
+
from typing import TYPE_CHECKING, Any
|
16
|
+
|
17
|
+
from invenio_search import current_search_client
|
18
|
+
from invenio_search.engine import dsl
|
19
|
+
from invenio_search.utils import build_alias_name
|
20
|
+
|
21
|
+
from oarepo_runtime.records.systemfields.mapping import MappingSystemFieldMixin
|
22
|
+
|
23
|
+
if TYPE_CHECKING:
|
24
|
+
from collections.abc import Iterable
|
25
|
+
|
26
|
+
from invenio_records.api import RecordBase
|
27
|
+
|
28
|
+
|
29
|
+
def prefixed_index(index: dsl.Index) -> dsl.Index:
|
30
|
+
"""Return a prefixed index for the given index."""
|
31
|
+
return dsl.Index(
|
32
|
+
build_alias_name(
|
33
|
+
index._name, # type: ignore[attr-defined] # noqa: SLF001
|
34
|
+
),
|
35
|
+
using=current_search_client, # pyright: ignore[reportArgumentType]
|
36
|
+
)
|
37
|
+
|
38
|
+
|
39
|
+
def update_record_system_fields_mapping(record_class: type[RecordBase]) -> None:
|
40
|
+
"""Update mapping for system fields in the record class.
|
41
|
+
|
42
|
+
:param record_class: The record class which index mapping should be updated.
|
43
|
+
:raise search.RequestError: If there is an error while updating the mapping.
|
44
|
+
"""
|
45
|
+
index = getattr(record_class, "index", None)
|
46
|
+
if not index:
|
47
|
+
return
|
48
|
+
|
49
|
+
for fld in get_mapping_fields(record_class):
|
50
|
+
# get mapping
|
51
|
+
mapping = fld.mapping
|
52
|
+
settings = fld.mapping_settings
|
53
|
+
dynamic_templates = fld.dynamic_templates
|
54
|
+
|
55
|
+
# upload mapping
|
56
|
+
update_record_index(prefixed_index(index), settings, mapping, dynamic_templates)
|
57
|
+
|
58
|
+
|
59
|
+
def update_record_index(
|
60
|
+
record_index: dsl.Index,
|
61
|
+
settings: dict,
|
62
|
+
mapping: dict,
|
63
|
+
dynamic_templates: list | None = None,
|
64
|
+
) -> None:
|
65
|
+
"""Update the index mapping for the given record index."""
|
66
|
+
if settings:
|
67
|
+
record_index.close()
|
68
|
+
record_index.put_settings(body=settings)
|
69
|
+
record_index.open()
|
70
|
+
|
71
|
+
body: dict[str, Any] = {}
|
72
|
+
if mapping:
|
73
|
+
body["properties"] = mapping
|
74
|
+
if dynamic_templates:
|
75
|
+
body["dynamic_templates"] = dynamic_templates
|
76
|
+
if body:
|
77
|
+
record_index.put_mapping(body=body)
|
78
|
+
|
79
|
+
|
80
|
+
def get_mapping_fields(
|
81
|
+
record_class: type[RecordBase],
|
82
|
+
) -> Iterable[MappingSystemFieldMixin]:
|
83
|
+
"""Get all mapping fields from the record class."""
|
84
|
+
return (attr for _, attr in inspect.getmembers(record_class, lambda x: isinstance(x, MappingSystemFieldMixin)))
|
@@ -1,17 +1,53 @@
|
|
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
|
+
"""PID providers."""
|
10
|
+
|
11
|
+
from __future__ import annotations
|
12
|
+
|
13
|
+
from typing import TYPE_CHECKING, Any, Self, cast
|
14
|
+
|
1
15
|
from invenio_pidstore.models import PersistentIdentifier, PIDStatus
|
2
16
|
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
from invenio_pidstore.providers.recordid_v2 import RecordIdProviderV2
|
19
|
+
else:
|
20
|
+
RecordIdProviderV2 = object
|
21
|
+
|
22
|
+
|
23
|
+
class UniversalPIDMixin(RecordIdProviderV2):
|
24
|
+
"""Mixin class to handle creation and management of universal PIDs for records."""
|
3
25
|
|
4
|
-
class UniversalPIDMixin:
|
5
26
|
unpid_pid_type = "unpid"
|
6
27
|
unpid_default_status = PIDStatus.REGISTERED
|
7
28
|
|
8
29
|
@classmethod
|
9
|
-
def create(
|
10
|
-
|
11
|
-
|
30
|
+
def create( # type: ignore[override]
|
31
|
+
cls,
|
32
|
+
object_type: str | None = None,
|
33
|
+
object_uuid: str | None = None,
|
34
|
+
options: dict | None = None,
|
35
|
+
**kwargs: Any,
|
36
|
+
) -> Self:
|
37
|
+
"""Create PID for a given object and store it."""
|
38
|
+
pid = cast(
|
39
|
+
"Self",
|
40
|
+
super().create(
|
41
|
+
object_type=object_type,
|
42
|
+
object_uuid=object_uuid,
|
43
|
+
options=options,
|
44
|
+
**kwargs,
|
45
|
+
),
|
12
46
|
)
|
13
|
-
|
14
|
-
|
47
|
+
if pid.pid.pid_value is None:
|
48
|
+
raise ValueError("PID value cannot be None.") # pragma: no cover
|
49
|
+
|
50
|
+
PersistentIdentifier.create(
|
15
51
|
cls.unpid_pid_type,
|
16
52
|
pid.pid.pid_value,
|
17
53
|
pid_provider=None,
|
@@ -19,4 +55,4 @@ class UniversalPIDMixin:
|
|
19
55
|
object_uuid=object_uuid,
|
20
56
|
status=cls.unpid_default_status,
|
21
57
|
)
|
22
|
-
return pid
|
58
|
+
return pid
|
@@ -1,34 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
from .selectors import (
|
11
|
-
FilteredSelector,
|
12
|
-
FirstItemSelector,
|
13
|
-
MultiSelector,
|
14
|
-
PathSelector,
|
15
|
-
Selector,
|
16
|
-
)
|
17
|
-
from .synthetic import SyntheticSystemField
|
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
|
+
"""Records system fields."""
|
18
10
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
"MappingSystemFieldMixin",
|
26
|
-
"SystemFieldDumperExt",
|
27
|
-
"SyntheticSystemField",
|
28
|
-
"PathSelector",
|
29
|
-
"Selector",
|
30
|
-
"FirstItemSelector",
|
31
|
-
"FilteredSelector",
|
32
|
-
"MultiSelector",
|
33
|
-
"TermIndexField",
|
34
|
-
)
|
11
|
+
from __future__ import annotations
|
12
|
+
|
13
|
+
from .mapping import MappingSystemFieldMixin
|
14
|
+
from .publication_status import PublicationStatusSystemField
|
15
|
+
|
16
|
+
__all__ = ("MappingSystemFieldMixin", "PublicationStatusSystemField")
|
@@ -1,39 +1,56 @@
|
|
1
|
-
|
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
|
+
"""System fields mapping."""
|
2
10
|
|
3
|
-
from
|
11
|
+
from __future__ import annotations
|
4
12
|
|
13
|
+
from typing import TYPE_CHECKING, override
|
14
|
+
|
15
|
+
if TYPE_CHECKING:
|
16
|
+
from invenio_records.api import RecordBase
|
17
|
+
from invenio_records.dumpers import Dumper
|
18
|
+
from invenio_records.systemfields import SystemField
|
19
|
+
else:
|
20
|
+
SystemField = object
|
21
|
+
|
22
|
+
|
23
|
+
class MappingSystemFieldMixin(SystemField):
|
24
|
+
"""Mixin class that provides default mapping, mapping settings, and dynamic templates for system fields."""
|
5
25
|
|
6
|
-
class MappingSystemFieldMixin:
|
7
26
|
@property
|
8
|
-
def mapping(self):
|
27
|
+
def mapping(self) -> dict:
|
28
|
+
"""Return the default mapping for the system field."""
|
9
29
|
return {}
|
10
30
|
|
11
31
|
@property
|
12
|
-
def mapping_settings(self):
|
32
|
+
def mapping_settings(self) -> dict:
|
33
|
+
"""Return the default mapping settings for the system field."""
|
13
34
|
return {}
|
14
35
|
|
15
36
|
@property
|
16
|
-
def dynamic_templates(self):
|
37
|
+
def dynamic_templates(self) -> list:
|
38
|
+
"""Return the default dynamic templates for the system field."""
|
17
39
|
return []
|
18
40
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
"""Load custom field."""
|
41
|
+
# The following methods are added just for typing purposes.
|
42
|
+
@override
|
43
|
+
def pre_dump(self, record: RecordBase, data: dict, dumper: Dumper | None = None) -> None: # type: ignore[misc]
|
44
|
+
"""Dump record to the data - pre-dump phase."""
|
24
45
|
|
46
|
+
@override
|
47
|
+
def post_dump(self, record: RecordBase, data: dict, dumper: Dumper | None = None) -> None: # type: ignore[misc]
|
48
|
+
"""Dump record to the data - post-dump phase."""
|
25
49
|
|
26
|
-
|
27
|
-
def
|
28
|
-
"""
|
29
|
-
for cf in inspect.getmembers(
|
30
|
-
type(record), lambda x: isinstance(x, MappingSystemFieldMixin)
|
31
|
-
):
|
32
|
-
cf[1].search_dump(data, record=record)
|
50
|
+
@override
|
51
|
+
def pre_load(self, data: dict, loader: Dumper | None = None) -> None: # type: ignore[misc]
|
52
|
+
"""Load record from the data - pre-load phase."""
|
33
53
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
record_cls, lambda x: isinstance(x, MappingSystemFieldMixin)
|
38
|
-
):
|
39
|
-
cf[1].search_load(data, record_cls=record_cls)
|
54
|
+
@override
|
55
|
+
def post_load(self, record: RecordBase, data: dict, loader: Dumper | None = None) -> None: # type: ignore[misc]
|
56
|
+
"""Load record from the data - post-load phase."""
|
@@ -0,0 +1,59 @@
|
|
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
|
+
"""Record status module."""
|
10
|
+
|
11
|
+
from __future__ import annotations
|
12
|
+
|
13
|
+
from typing import TYPE_CHECKING, Any, override
|
14
|
+
|
15
|
+
from invenio_records.systemfields import SystemField
|
16
|
+
|
17
|
+
from .mapping import MappingSystemFieldMixin
|
18
|
+
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
from invenio_records.api import RecordBase
|
21
|
+
from invenio_records.dumpers import Dumper
|
22
|
+
|
23
|
+
|
24
|
+
class PublicationStatusSystemField(MappingSystemFieldMixin, SystemField):
|
25
|
+
"""A system field to track the status of a record (either 'draft' or 'published').
|
26
|
+
|
27
|
+
The default key for this field is 'publication_status', but it can be customized.
|
28
|
+
"""
|
29
|
+
|
30
|
+
def __init__(self, key: str | None = "publication_status"):
|
31
|
+
"""Initialize the system field with an optional key."""
|
32
|
+
super().__init__(key)
|
33
|
+
|
34
|
+
@property
|
35
|
+
def mapping(self) -> dict:
|
36
|
+
"""Return the mapping for the field in the search index."""
|
37
|
+
return {
|
38
|
+
self.key: {
|
39
|
+
"type": "keyword",
|
40
|
+
},
|
41
|
+
}
|
42
|
+
|
43
|
+
@override
|
44
|
+
def post_load(self, record: RecordBase, data: dict, loader: Dumper | None = None) -> None:
|
45
|
+
data.pop(self.key, None)
|
46
|
+
|
47
|
+
@override
|
48
|
+
def post_dump(self, record: RecordBase, data: dict, dumper: Dumper | None = None) -> None:
|
49
|
+
if self.key is None:
|
50
|
+
return
|
51
|
+
if not self.attr_name:
|
52
|
+
raise ValueError("attr_name must be set for PublicationStatusSystemField")
|
53
|
+
data[self.key] = getattr(record, self.attr_name)
|
54
|
+
|
55
|
+
def __get__(self, record: RecordBase | None, owner: Any = None) -> Any:
|
56
|
+
"""Access the attribute."""
|
57
|
+
if record is None:
|
58
|
+
return self
|
59
|
+
return "draft" if getattr(record, "is_draft", False) else "published"
|
@@ -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
|
+
"""Services module."""
|
11
|
+
|
12
|
+
from __future__ import annotations
|
@@ -1,35 +1,29 @@
|
|
1
|
-
|
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
|
+
"""Service config module."""
|
10
|
+
|
11
|
+
from __future__ import annotations
|
12
|
+
|
2
13
|
from .link_conditions import (
|
3
14
|
has_draft,
|
4
15
|
has_draft_permission,
|
5
|
-
has_file_permission,
|
6
16
|
has_permission,
|
7
|
-
has_permission_file_service,
|
8
17
|
has_published_record,
|
9
|
-
is_draft_record,
|
10
18
|
is_published_record,
|
11
19
|
)
|
12
|
-
from .
|
13
|
-
AuthenticatedPermissionPolicy,
|
14
|
-
EveryonePermissionPolicy,
|
15
|
-
OaiHarvesterPermissionPolicy,
|
16
|
-
ReadOnlyPermissionPolicy,
|
17
|
-
)
|
18
|
-
from .service import PermissionsPresetsConfigMixin
|
20
|
+
from .permissions import EveryonePermissionPolicy
|
19
21
|
|
20
22
|
__all__ = (
|
21
|
-
"PermissionsPresetsConfigMixin",
|
22
|
-
"OaiHarvesterPermissionPolicy",
|
23
|
-
"ReadOnlyPermissionPolicy",
|
24
23
|
"EveryonePermissionPolicy",
|
25
|
-
"AuthenticatedPermissionPolicy",
|
26
|
-
"is_published_record",
|
27
|
-
"is_draft_record",
|
28
24
|
"has_draft",
|
25
|
+
"has_draft_permission",
|
29
26
|
"has_permission",
|
30
|
-
"has_permission_file_service",
|
31
|
-
"has_file_permission",
|
32
27
|
"has_published_record",
|
33
|
-
"
|
34
|
-
"DraftLink",
|
28
|
+
"is_published_record",
|
35
29
|
)
|
@@ -1,126 +1,120 @@
|
|
1
|
-
|
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
|
+
"""Link conditions module."""
|
11
|
+
|
12
|
+
from __future__ import annotations
|
13
|
+
|
2
14
|
from abc import abstractmethod
|
3
15
|
from logging import getLogger
|
16
|
+
from typing import Any
|
4
17
|
|
5
18
|
from invenio_pidstore.errors import PIDDoesNotExistError, PIDUnregistered
|
6
|
-
from
|
7
|
-
from invenio_records_resources.records.api import FileRecord, Record
|
19
|
+
from invenio_records_resources.records.api import FileRecord, RecordBase
|
8
20
|
|
9
|
-
from
|
10
|
-
|
11
|
-
get_record_service_for_record,
|
12
|
-
)
|
13
|
-
from ...records.drafts import get_draft
|
21
|
+
from oarepo_runtime.proxies import current_runtime
|
22
|
+
from oarepo_runtime.records.drafts import get_draft
|
14
23
|
|
15
24
|
log = getLogger(__name__)
|
16
25
|
|
17
26
|
|
18
27
|
class Condition:
|
28
|
+
"""Base class for defining conditions with callable logic."""
|
29
|
+
|
19
30
|
@abstractmethod
|
20
|
-
def __call__(self, obj, ctx: dict):
|
21
|
-
|
31
|
+
def __call__(self, obj: RecordBase, ctx: dict):
|
32
|
+
"""Abstract method to be implemented in subclasses to define a condition."""
|
33
|
+
raise NotImplementedError # pragma: no cover
|
22
34
|
|
23
|
-
def __and__(self, other):
|
35
|
+
def __and__(self, other: Any):
|
36
|
+
"""Combine two conditions using a logical AND."""
|
24
37
|
return type(
|
25
|
-
"
|
38
|
+
"And",
|
26
39
|
(Condition,),
|
27
40
|
{"__call__": lambda _, obj, ctx: self(obj, ctx) and other(obj, ctx)},
|
28
41
|
)()
|
29
42
|
|
30
|
-
def __or__(self, other):
|
43
|
+
def __or__(self, other: Any):
|
44
|
+
"""Combine two conditions using a logical OR."""
|
31
45
|
return type(
|
32
|
-
"
|
46
|
+
"Or",
|
33
47
|
(Condition,),
|
34
48
|
{"__call__": lambda _, obj, ctx: self(obj, ctx) or other(obj, ctx)},
|
35
49
|
)()
|
36
50
|
|
37
51
|
|
38
|
-
class
|
39
|
-
"""
|
40
|
-
|
41
|
-
def __call__(self, obj: Record, ctx: dict):
|
42
|
-
return not getattr(obj, "is_draft", False)
|
43
|
-
|
44
|
-
|
45
|
-
class is_draft_record(Condition):
|
46
|
-
"""Shortcut for links to determine if record is a draft record."""
|
47
|
-
|
48
|
-
def __call__(self, obj: Record, ctx: dict):
|
49
|
-
return getattr(obj, "is_draft", False)
|
50
|
-
|
51
|
-
|
52
|
-
class has_draft(Condition):
|
53
|
-
"""Shortcut for links to determine if record is either a draft or a published one with a draft associated."""
|
54
|
-
|
55
|
-
def __call__(self, obj: Record, ctx: dict):
|
56
|
-
if getattr(obj, "is_draft", False):
|
57
|
-
return True
|
58
|
-
if getattr(obj, "has_draft", False):
|
59
|
-
return True
|
60
|
-
return False
|
61
|
-
|
52
|
+
class has_permission(Condition): # noqa: N801
|
53
|
+
"""A condition to check if a user has the specified permission for a given record."""
|
62
54
|
|
63
|
-
|
64
|
-
|
55
|
+
def __init__(self, action_name: str):
|
56
|
+
"""Initialize the condition with the specified action name."""
|
65
57
|
self.action_name = action_name
|
66
58
|
|
67
59
|
def __call__(self, obj: RecordBase, ctx: dict):
|
60
|
+
"""Evaluate the condition by checking the permission for a given record."""
|
68
61
|
if isinstance(obj, FileRecord):
|
69
62
|
obj = obj.record
|
70
|
-
service = get_record_service_for_record(obj)
|
63
|
+
service = current_runtime.get_record_service_for_record(obj)
|
71
64
|
try:
|
72
|
-
return service.check_permission(
|
73
|
-
|
74
|
-
)
|
75
|
-
|
76
|
-
log.exception(f"Unexpected exception {e}.")
|
65
|
+
return service.check_permission(action_name=self.action_name, record=obj, **ctx)
|
66
|
+
except Exception: # pragma: no cover
|
67
|
+
log.exception("Unexpected exception.")
|
68
|
+
|
77
69
|
|
70
|
+
class has_draft_permission(Condition): # noqa: N801
|
71
|
+
"""A condition to check if a user has the specified permission for a draft record."""
|
78
72
|
|
79
|
-
|
80
|
-
|
73
|
+
def __init__(self, action_name: str):
|
74
|
+
"""Initialize the condition with the specified action name."""
|
81
75
|
self.action_name = action_name
|
82
76
|
|
83
77
|
def __call__(self, obj: RecordBase, ctx: dict):
|
78
|
+
"""Valuates the condition by checking the permission for a draft record."""
|
79
|
+
_ = ctx
|
84
80
|
draft_record = get_draft(obj)
|
85
81
|
if not draft_record:
|
86
82
|
return False
|
87
|
-
service = get_record_service_for_record(obj)
|
83
|
+
service = current_runtime.get_record_service_for_record(obj)
|
88
84
|
try:
|
89
|
-
return service.check_permission(
|
90
|
-
|
91
|
-
)
|
92
|
-
except Exception as e:
|
93
|
-
log.exception(f"Unexpected exception {e}.")
|
85
|
+
return service.check_permission(action_name=self.action_name, record=draft_record, **ctx)
|
86
|
+
except Exception: # pragma: no cover
|
87
|
+
log.exception("Unexpected exception.")
|
94
88
|
return False
|
95
89
|
|
96
90
|
|
97
|
-
class
|
98
|
-
|
99
|
-
if isinstance(obj, FileRecord):
|
100
|
-
obj = obj.record
|
101
|
-
service = get_file_service_for_record_class(type(obj))
|
102
|
-
try:
|
103
|
-
return service.check_permission(
|
104
|
-
action_name=self.action_name, record=obj, **ctx
|
105
|
-
)
|
106
|
-
except Exception as e:
|
107
|
-
log.exception(f"Unexpected exception {e}.")
|
91
|
+
class has_draft(Condition): # noqa: N801
|
92
|
+
"""Shortcut for links to determine if record is either a draft or a published one with a draft associated."""
|
108
93
|
|
94
|
+
def __call__(self, obj: RecordBase, ctx: dict):
|
95
|
+
"""Check if the given record has draft."""
|
96
|
+
_ = ctx
|
97
|
+
return bool(getattr(obj, "is_draft", False)) or bool(getattr(obj, "has_draft", False))
|
109
98
|
|
110
|
-
class has_permission_file_service(has_file_permission):
|
111
|
-
def __init__(self, action_name):
|
112
|
-
warnings.warn(
|
113
|
-
"has_permission_file_service is deprecated, use has_file_permission instead",
|
114
|
-
DeprecationWarning,
|
115
|
-
)
|
116
|
-
super().__init__(action_name)
|
117
99
|
|
100
|
+
class has_published_record(Condition): # noqa: N801
|
101
|
+
"""Shortcut for links to determine if the given record has a published PID."""
|
118
102
|
|
119
|
-
|
120
|
-
|
121
|
-
|
103
|
+
def __call__(self, obj: RecordBase, ctx: dict):
|
104
|
+
"""Check if the given record has a published PID."""
|
105
|
+
_ = ctx
|
106
|
+
service = current_runtime.get_record_service_for_record(obj)
|
122
107
|
try:
|
123
108
|
service.record_cls.pid.resolve(obj["id"])
|
124
109
|
except (PIDUnregistered, PIDDoesNotExistError):
|
125
110
|
return False
|
126
111
|
return True
|
112
|
+
|
113
|
+
|
114
|
+
class is_published_record(Condition): # noqa: N801
|
115
|
+
"""Shortcut for links to determine if record is a published record."""
|
116
|
+
|
117
|
+
def __call__(self, obj: RecordBase, ctx: dict):
|
118
|
+
"""Check if the given record is draft."""
|
119
|
+
_ = ctx
|
120
|
+
return not getattr(obj, "is_draft", False)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# mypy: disable-error-code="assignment"
|
2
|
+
#
|
3
|
+
# Copyright (c) 2025 CESNET z.s.p.o.
|
4
|
+
#
|
5
|
+
# This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
|
6
|
+
#
|
7
|
+
# oarepo-runtime is free software; you can redistribute it and/or modify it
|
8
|
+
# under the terms of the MIT License; see LICENSE file for more details.
|
9
|
+
#
|
10
|
+
|
11
|
+
"""Everyone permissions."""
|
12
|
+
|
13
|
+
from __future__ import annotations
|
14
|
+
|
15
|
+
from invenio_records_permissions import RecordPermissionPolicy
|
16
|
+
from invenio_records_permissions.generators import AnyUser, Generator, SystemProcess
|
17
|
+
|
18
|
+
type GeneratorList = tuple[Generator, ...]
|
19
|
+
|
20
|
+
|
21
|
+
class EveryonePermissionPolicy(RecordPermissionPolicy):
|
22
|
+
"""Record policy for read-only repository."""
|
23
|
+
|
24
|
+
can_search: GeneratorList = (SystemProcess(), AnyUser())
|
25
|
+
can_read: GeneratorList = (SystemProcess(), AnyUser())
|
26
|
+
can_create: GeneratorList = (SystemProcess(), AnyUser())
|
27
|
+
can_update: GeneratorList = (SystemProcess(), AnyUser())
|
28
|
+
can_delete: GeneratorList = (SystemProcess(), AnyUser())
|
29
|
+
can_manage: GeneratorList = (SystemProcess(), AnyUser())
|
30
|
+
|
31
|
+
can_create_files: GeneratorList = (SystemProcess(), AnyUser())
|
32
|
+
can_set_content_files: GeneratorList = (SystemProcess(), AnyUser())
|
33
|
+
can_get_content_files: GeneratorList = (SystemProcess(), AnyUser())
|
34
|
+
can_commit_files: GeneratorList = (SystemProcess(), AnyUser())
|
35
|
+
can_read_files: GeneratorList = (SystemProcess(), AnyUser())
|
36
|
+
can_update_files: GeneratorList = (SystemProcess(), AnyUser())
|
37
|
+
can_delete_files: GeneratorList = (SystemProcess(), AnyUser())
|
38
|
+
can_list_files: GeneratorList = (SystemProcess(), AnyUser())
|
39
|
+
can_manage_files: GeneratorList = (SystemProcess(), AnyUser())
|
40
|
+
|
41
|
+
can_edit: GeneratorList = (SystemProcess(), AnyUser())
|
42
|
+
can_new_version: GeneratorList = (SystemProcess(), AnyUser())
|
43
|
+
can_search_drafts: GeneratorList = (SystemProcess(), AnyUser())
|
44
|
+
can_read_draft: GeneratorList = (SystemProcess(), AnyUser())
|
45
|
+
can_search_versions: GeneratorList = (SystemProcess(), AnyUser())
|
46
|
+
can_update_draft: GeneratorList = (SystemProcess(), AnyUser())
|
47
|
+
can_delete_draft: GeneratorList = (SystemProcess(), AnyUser())
|
48
|
+
can_publish: GeneratorList = (SystemProcess(), AnyUser())
|
49
|
+
can_draft_create_files: GeneratorList = (SystemProcess(), AnyUser())
|
50
|
+
can_draft_set_content_files: GeneratorList = (SystemProcess(), AnyUser())
|
51
|
+
can_draft_get_content_files: GeneratorList = (SystemProcess(), AnyUser())
|
52
|
+
can_draft_commit_files: GeneratorList = (SystemProcess(), AnyUser())
|
53
|
+
can_draft_read_files: GeneratorList = (SystemProcess(), AnyUser())
|
54
|
+
can_draft_update_files: GeneratorList = (SystemProcess(), AnyUser())
|
55
|
+
can_draft_delete_files: GeneratorList = (SystemProcess(), AnyUser())
|
56
|
+
|
57
|
+
can_add_community: GeneratorList = (SystemProcess(), AnyUser())
|
58
|
+
can_remove_community: GeneratorList = (SystemProcess(), AnyUser())
|
59
|
+
|
60
|
+
can_read_deleted: GeneratorList = (SystemProcess(), AnyUser())
|
61
|
+
can_manage_record_access: GeneratorList = (SystemProcess(), AnyUser())
|
62
|
+
can_lift_embargo: GeneratorList = (SystemProcess(), AnyUser())
|