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
oarepo_runtime/cli/fixtures.py
DELETED
@@ -1,167 +0,0 @@
|
|
1
|
-
import click
|
2
|
-
import tqdm
|
3
|
-
from flask import current_app
|
4
|
-
from flask.cli import with_appcontext
|
5
|
-
from flask_principal import Identity, RoleNeed, UserNeed
|
6
|
-
from invenio_access.permissions import any_user, authenticated_user, system_identity
|
7
|
-
from invenio_accounts.models import User
|
8
|
-
|
9
|
-
from oarepo_runtime.cli import oarepo
|
10
|
-
from oarepo_runtime.datastreams import SynchronousDataStream
|
11
|
-
from oarepo_runtime.datastreams.asynchronous import AsynchronousDataStream
|
12
|
-
from oarepo_runtime.datastreams.fixtures import (
|
13
|
-
FixturesCallback,
|
14
|
-
dump_fixtures,
|
15
|
-
fixtures_asynchronous_callback,
|
16
|
-
load_fixtures,
|
17
|
-
)
|
18
|
-
from oarepo_runtime.datastreams.types import StatsKeepingDataStreamCallback
|
19
|
-
|
20
|
-
|
21
|
-
@oarepo.group()
|
22
|
-
def fixtures():
|
23
|
-
"""Load and dump fixtures"""
|
24
|
-
|
25
|
-
|
26
|
-
@fixtures.command()
|
27
|
-
@click.argument("fixture_dir_or_catalogue", required=False)
|
28
|
-
@click.option("--include", multiple=True)
|
29
|
-
@click.option("--exclude", multiple=True)
|
30
|
-
@click.option("--system-fixtures/--no-system-fixtures", default=True, is_flag=True)
|
31
|
-
@click.option("--verbose", is_flag=True)
|
32
|
-
@click.option("--on-background", is_flag=True)
|
33
|
-
@click.option(
|
34
|
-
"--bulk-size",
|
35
|
-
default=100,
|
36
|
-
type=int,
|
37
|
-
help="Size for bulk indexing - this number of records "
|
38
|
-
"will be committed in a single transaction and indexed together",
|
39
|
-
)
|
40
|
-
@click.option("--batch-size", help="Alias for --bulk-size", type=int)
|
41
|
-
@click.option(
|
42
|
-
"--identity", help="Email of the identity that will be used to import the data"
|
43
|
-
)
|
44
|
-
@with_appcontext
|
45
|
-
def load(
|
46
|
-
fixture_dir_or_catalogue=None,
|
47
|
-
include=None,
|
48
|
-
exclude=None,
|
49
|
-
system_fixtures=None,
|
50
|
-
verbose=False,
|
51
|
-
bulk_size=100,
|
52
|
-
on_background=False,
|
53
|
-
batch_size=None,
|
54
|
-
identity=None,
|
55
|
-
):
|
56
|
-
"""Loads fixtures"""
|
57
|
-
if batch_size:
|
58
|
-
bulk_size = batch_size
|
59
|
-
if not on_background:
|
60
|
-
callback = TQDMCallback(verbose=verbose)
|
61
|
-
else:
|
62
|
-
callback = fixtures_asynchronous_callback.s()
|
63
|
-
|
64
|
-
if fixture_dir_or_catalogue:
|
65
|
-
system_fixtures = False
|
66
|
-
|
67
|
-
if not identity:
|
68
|
-
user = None
|
69
|
-
identity = system_identity
|
70
|
-
else:
|
71
|
-
# identity is user email
|
72
|
-
user = User.query.filter_by(email=identity).one()
|
73
|
-
identity = Identity(user.id)
|
74
|
-
|
75
|
-
# TODO: add provides. How to do it better? It seems that we can not use
|
76
|
-
# flask signals to add these, as they depend on request context that is
|
77
|
-
# not available here
|
78
|
-
identity.provides.add(any_user)
|
79
|
-
identity.provides.add(authenticated_user)
|
80
|
-
identity.provides.add(UserNeed(user.id))
|
81
|
-
for role in getattr(user, "roles", []):
|
82
|
-
identity.provides.add(RoleNeed(role.name))
|
83
|
-
# TODO: community roles ...
|
84
|
-
|
85
|
-
with current_app.wsgi_app.mounts["/api"].app_context():
|
86
|
-
load_fixtures(
|
87
|
-
fixture_dir_or_catalogue,
|
88
|
-
_make_list(include),
|
89
|
-
_make_list(exclude),
|
90
|
-
system_fixtures=system_fixtures,
|
91
|
-
callback=callback,
|
92
|
-
batch_size=bulk_size,
|
93
|
-
datastreams_impl=(
|
94
|
-
AsynchronousDataStream if on_background else SynchronousDataStream
|
95
|
-
),
|
96
|
-
identity=identity,
|
97
|
-
)
|
98
|
-
if not on_background:
|
99
|
-
_show_stats(callback, "Load fixtures")
|
100
|
-
|
101
|
-
|
102
|
-
@fixtures.command()
|
103
|
-
@click.option("--include", multiple=True)
|
104
|
-
@click.option("--exclude", multiple=True)
|
105
|
-
@click.argument("fixture_dir", required=True)
|
106
|
-
@click.option("--verbose", is_flag=True)
|
107
|
-
@with_appcontext
|
108
|
-
def dump(fixture_dir, include, exclude, verbose):
|
109
|
-
"""Dump fixtures"""
|
110
|
-
callback = TQDMCallback(verbose=verbose)
|
111
|
-
|
112
|
-
with current_app.wsgi_app.mounts["/api"].app_context():
|
113
|
-
dump_fixtures(
|
114
|
-
fixture_dir,
|
115
|
-
_make_list(include),
|
116
|
-
_make_list(exclude),
|
117
|
-
callback=TQDMCallback(verbose=verbose),
|
118
|
-
)
|
119
|
-
_show_stats(callback, "Dump fixtures")
|
120
|
-
|
121
|
-
|
122
|
-
def _make_list(lst):
|
123
|
-
return [
|
124
|
-
item.strip() for lst_item in lst for item in lst_item.split(",") if item.strip()
|
125
|
-
]
|
126
|
-
|
127
|
-
|
128
|
-
def _show_stats(callback: StatsKeepingDataStreamCallback, title: str):
|
129
|
-
print("\n\n")
|
130
|
-
print(f"{title} stats:")
|
131
|
-
print(callback.stats())
|
132
|
-
|
133
|
-
|
134
|
-
class TQDMCallback(FixturesCallback):
|
135
|
-
def __init__(self, message_prefix="Loading ", verbose=False):
|
136
|
-
super().__init__()
|
137
|
-
self._tqdm = tqdm.tqdm(unit=" item(s)")
|
138
|
-
self._message_prefix = message_prefix
|
139
|
-
self._verbose = verbose
|
140
|
-
|
141
|
-
def fixture_started(self, fixture_name):
|
142
|
-
self._tqdm.set_description(f"{self._message_prefix}{fixture_name} running")
|
143
|
-
|
144
|
-
def fixture_finished(self, fixture_name):
|
145
|
-
self._tqdm.set_description(f"{self._message_prefix}{fixture_name} finished")
|
146
|
-
|
147
|
-
def batch_finished(self, batch):
|
148
|
-
super().batch_finished(batch)
|
149
|
-
self._tqdm.update(len(batch.entries))
|
150
|
-
for err in batch.errors:
|
151
|
-
self._tqdm.write("Failed batch: {}: {}".format(err, batch))
|
152
|
-
if self._verbose:
|
153
|
-
for entry in batch.entries:
|
154
|
-
if entry.errors:
|
155
|
-
self._tqdm.write("Failed entry: {}".format(entry))
|
156
|
-
|
157
|
-
def reader_error(self, reader, exception):
|
158
|
-
super().reader_error(reader, exception)
|
159
|
-
self._tqdm.write("Reader error:{}: {}".format(reader, exception))
|
160
|
-
|
161
|
-
def transformer_error(self, batch, transformer, exception):
|
162
|
-
super().transformer_error(batch, transformer, exception)
|
163
|
-
self._tqdm.write("Transformer error: {}: {}".format(transformer, exception))
|
164
|
-
|
165
|
-
def writer_error(self, batch, writer, exception):
|
166
|
-
super().writer_error(batch, writer, exception)
|
167
|
-
self._tqdm.write("Writer error: {}: {}".format(writer, exception))
|
oarepo_runtime/cli/index.py
DELETED
@@ -1,272 +0,0 @@
|
|
1
|
-
import sys
|
2
|
-
import traceback
|
3
|
-
from io import StringIO
|
4
|
-
|
5
|
-
import click
|
6
|
-
import yaml
|
7
|
-
from flask.cli import with_appcontext
|
8
|
-
from invenio_db import db
|
9
|
-
from invenio_records_resources.proxies import current_service_registry
|
10
|
-
from invenio_search.proxies import current_search
|
11
|
-
from werkzeug.utils import ImportStringError, import_string
|
12
|
-
|
13
|
-
try:
|
14
|
-
from tqdm import tqdm
|
15
|
-
except ImportError:
|
16
|
-
|
17
|
-
def tqdm(generator):
|
18
|
-
yield from generator
|
19
|
-
|
20
|
-
|
21
|
-
from .base import oarepo
|
22
|
-
|
23
|
-
|
24
|
-
@oarepo.group()
|
25
|
-
def index():
|
26
|
-
"OARepo indexing addons"
|
27
|
-
|
28
|
-
|
29
|
-
@index.command(
|
30
|
-
help="Create all indices that do not exist yet. "
|
31
|
-
"This is like 'invenio index init' but does not throw "
|
32
|
-
"an exception if some indices already exist"
|
33
|
-
)
|
34
|
-
@with_appcontext
|
35
|
-
def init():
|
36
|
-
click.secho("Creating indexes...", fg="green", bold=True, file=sys.stderr)
|
37
|
-
all_indices = list(gather_all_indices())
|
38
|
-
new_indices = []
|
39
|
-
with click.progressbar(all_indices, label="Checking which indices exist") as bar:
|
40
|
-
for name, alias in bar:
|
41
|
-
if not current_search.client.indices.exists(alias):
|
42
|
-
new_indices.append(name)
|
43
|
-
if new_indices:
|
44
|
-
with click.progressbar(
|
45
|
-
current_search.create(
|
46
|
-
ignore=[400], ignore_existing=True, index_list=new_indices
|
47
|
-
),
|
48
|
-
length=len(new_indices),
|
49
|
-
) as bar:
|
50
|
-
for name, response in bar:
|
51
|
-
bar.label = name
|
52
|
-
|
53
|
-
|
54
|
-
def gather_all_indices():
|
55
|
-
"""Yield index_file, index_name for all indices."""
|
56
|
-
|
57
|
-
# partially copied from invenio-search
|
58
|
-
def _build(tree_or_filename, alias=None):
|
59
|
-
"""Build a list of index/alias actions to perform."""
|
60
|
-
for name, value in tree_or_filename.items():
|
61
|
-
if isinstance(value, dict):
|
62
|
-
yield from _build(value, alias=name)
|
63
|
-
else:
|
64
|
-
index_result, alias_result = current_search.create_index(
|
65
|
-
name, dry_run=True
|
66
|
-
)
|
67
|
-
yield name, alias_result[0]
|
68
|
-
|
69
|
-
yield from _build(current_search.active_aliases)
|
70
|
-
|
71
|
-
|
72
|
-
def record_or_service(model):
|
73
|
-
# TODO: is this still used (maybe from somewhere else?)
|
74
|
-
try:
|
75
|
-
service = current_service_registry.get(model)
|
76
|
-
except KeyError:
|
77
|
-
service = None
|
78
|
-
if service and getattr(service, "config", None):
|
79
|
-
record = getattr(service.config, "record_cls", None)
|
80
|
-
else:
|
81
|
-
try:
|
82
|
-
record = import_string(model)
|
83
|
-
except ImportStringError:
|
84
|
-
record = None
|
85
|
-
|
86
|
-
if record is None:
|
87
|
-
click.secho(
|
88
|
-
"Service or model not found. Known services: ",
|
89
|
-
fg="red",
|
90
|
-
bold=True,
|
91
|
-
file=sys.stderr,
|
92
|
-
)
|
93
|
-
for svc in sorted(current_service_registry._services):
|
94
|
-
click.secho(f" {svc}", file=sys.stderr)
|
95
|
-
sys.exit(1)
|
96
|
-
return record
|
97
|
-
|
98
|
-
|
99
|
-
@index.command()
|
100
|
-
@with_appcontext
|
101
|
-
@click.argument("model", required=False)
|
102
|
-
@click.option("--bulk-size", required=False, default=5000, type=int)
|
103
|
-
@click.option("--verbose/--no-verbose", default=False)
|
104
|
-
def reindex(model, bulk_size, verbose):
|
105
|
-
if not model:
|
106
|
-
services = list(current_service_registry._services.keys())
|
107
|
-
else:
|
108
|
-
services = [model]
|
109
|
-
services = sort_services(services)
|
110
|
-
for service_id in services:
|
111
|
-
click.secho(f"Preparing to index {service_id}", file=sys.stderr)
|
112
|
-
|
113
|
-
try:
|
114
|
-
service = current_service_registry.get(service_id)
|
115
|
-
except KeyError:
|
116
|
-
click.secho(f"Service {service_id} not in known services:", color="red")
|
117
|
-
for known_service_id, known_service in sorted(
|
118
|
-
current_service_registry._services.items()
|
119
|
-
):
|
120
|
-
click.secho(
|
121
|
-
f" {known_service_id} -> {type(known_service).__module__}.{type(known_service).__name__}",
|
122
|
-
color="red",
|
123
|
-
)
|
124
|
-
sys.exit(1)
|
125
|
-
record_class = getattr(service.config, "record_cls", None)
|
126
|
-
|
127
|
-
id_generators = []
|
128
|
-
|
129
|
-
record_generator = RECORD_GENERATORS.get(service_id, model_records_generator)
|
130
|
-
|
131
|
-
if record_class and hasattr(service, "indexer"):
|
132
|
-
try:
|
133
|
-
id_generators.append(record_generator(record_class))
|
134
|
-
except Exception as e:
|
135
|
-
click.secho(
|
136
|
-
f"Could not get record ids for {service_id}, exception {e}",
|
137
|
-
file=sys.stderr,
|
138
|
-
)
|
139
|
-
|
140
|
-
draft_class = getattr(service.config, "draft_cls", None)
|
141
|
-
|
142
|
-
if draft_class and hasattr(service, "indexer"):
|
143
|
-
try:
|
144
|
-
id_generators.append(record_generator(draft_class))
|
145
|
-
except Exception as e:
|
146
|
-
click.secho(
|
147
|
-
f"Could not get draft record ids for {service_id}, exception {e}",
|
148
|
-
file=sys.stderr,
|
149
|
-
)
|
150
|
-
|
151
|
-
click.secho(f"Indexing {service_id}", file=sys.stderr)
|
152
|
-
count = 0
|
153
|
-
errors = 0
|
154
|
-
for gen in id_generators:
|
155
|
-
for bulk in generate_bulk_data(gen, service.indexer, bulk_size=bulk_size):
|
156
|
-
index_result = service.indexer.client.bulk(bulk)
|
157
|
-
count += len(bulk) // 2
|
158
|
-
for index_item_result in index_result["items"]:
|
159
|
-
result = index_item_result["index"]
|
160
|
-
if result["status"] not in (200, 201):
|
161
|
-
errors += 1
|
162
|
-
click.secho(
|
163
|
-
f"Error indexing record with id {result['_id']}",
|
164
|
-
fg="red",
|
165
|
-
file=sys.stderr,
|
166
|
-
)
|
167
|
-
click.secho(
|
168
|
-
dump_yaml(result.get("error")), fg="red", file=sys.stderr
|
169
|
-
)
|
170
|
-
if verbose:
|
171
|
-
click.secho("Record:", file=sys.stderr)
|
172
|
-
rec = [
|
173
|
-
bulk[idx + 1]
|
174
|
-
for idx in range(0, len(bulk), 2)
|
175
|
-
if bulk[idx]["index"]["_id"] == result["_id"]
|
176
|
-
]
|
177
|
-
if rec:
|
178
|
-
click.secho(dump_yaml(rec[0]))
|
179
|
-
else:
|
180
|
-
click.secho("<no record found>")
|
181
|
-
|
182
|
-
if count:
|
183
|
-
service.indexer.refresh()
|
184
|
-
|
185
|
-
if errors:
|
186
|
-
click.secho(
|
187
|
-
f"Indexing {service_id} failed, indexed {count - errors} records, failed {errors} records.",
|
188
|
-
fg="red",
|
189
|
-
file=sys.stderr,
|
190
|
-
)
|
191
|
-
if not verbose:
|
192
|
-
click.secho("Run with --verbose to see information about the errors")
|
193
|
-
else:
|
194
|
-
click.secho(
|
195
|
-
f"Indexing {service_id} finished, indexed {count} records",
|
196
|
-
fg="green",
|
197
|
-
file=sys.stderr,
|
198
|
-
)
|
199
|
-
|
200
|
-
|
201
|
-
def generate_bulk_data(record_generator, record_indexer, bulk_size):
|
202
|
-
data = []
|
203
|
-
n = 0
|
204
|
-
for record in tqdm(record_generator):
|
205
|
-
try:
|
206
|
-
index = record_indexer.record_to_index(record)
|
207
|
-
body = record_indexer._prepare_record(record, index)
|
208
|
-
index = record_indexer._prepare_index(index)
|
209
|
-
data.append(
|
210
|
-
{
|
211
|
-
"index": {
|
212
|
-
"_index": index,
|
213
|
-
"version": record.revision_id,
|
214
|
-
"version_type": "external_gte",
|
215
|
-
"_id": body["uuid"],
|
216
|
-
}
|
217
|
-
}
|
218
|
-
)
|
219
|
-
data.append(body)
|
220
|
-
if len(data) >= bulk_size:
|
221
|
-
yield data
|
222
|
-
data = []
|
223
|
-
except:
|
224
|
-
traceback.print_exc()
|
225
|
-
if data:
|
226
|
-
yield data
|
227
|
-
|
228
|
-
|
229
|
-
def dump_yaml(data):
|
230
|
-
io = StringIO()
|
231
|
-
yaml.dump(data, io, allow_unicode=True)
|
232
|
-
return io.getvalue()
|
233
|
-
|
234
|
-
|
235
|
-
def model_records_generator(model_class):
|
236
|
-
try:
|
237
|
-
for x in db.session.query(model_class.model_cls.id).filter(
|
238
|
-
model_class.model_cls.is_deleted.is_(False)
|
239
|
-
):
|
240
|
-
rec_id = x[0]
|
241
|
-
yield model_class.get_record(rec_id)
|
242
|
-
except Exception as e:
|
243
|
-
click.secho(f"Could not index {model_class}: {e}", fg="red", file=sys.stderr)
|
244
|
-
|
245
|
-
|
246
|
-
def users_record_generator(model_class):
|
247
|
-
from invenio_accounts.models import User
|
248
|
-
from invenio_users_resources.records.api import UserAggregate
|
249
|
-
|
250
|
-
try:
|
251
|
-
for x in db.session.query(User.id):
|
252
|
-
rec_id = x[0]
|
253
|
-
yield UserAggregate.get_record(rec_id)
|
254
|
-
except Exception as e:
|
255
|
-
click.secho(f"Could not index {model_class}: {e}", fg="red", file=sys.stderr)
|
256
|
-
|
257
|
-
|
258
|
-
priorities = ["vocabular", "users", "groups"]
|
259
|
-
|
260
|
-
|
261
|
-
def sort_services(services):
|
262
|
-
def idx(x):
|
263
|
-
for idx, p in enumerate(priorities):
|
264
|
-
if p in x:
|
265
|
-
return idx, x
|
266
|
-
return len(priorities), x
|
267
|
-
|
268
|
-
services.sort(key=idx)
|
269
|
-
return services
|
270
|
-
|
271
|
-
|
272
|
-
RECORD_GENERATORS = {"users": users_record_generator}
|
@@ -1,26 +0,0 @@
|
|
1
|
-
from flask import current_app
|
2
|
-
from flask_principal import Identity, UserNeed, identity_loaded
|
3
|
-
from invenio_access.models import User
|
4
|
-
|
5
|
-
from ..base import oarepo
|
6
|
-
|
7
|
-
|
8
|
-
@oarepo.group()
|
9
|
-
def permissions():
|
10
|
-
"""Commands for checking and explaining permissions."""
|
11
|
-
|
12
|
-
|
13
|
-
def get_user_and_identity(user_id_or_email):
|
14
|
-
try:
|
15
|
-
user_id = int(user_id_or_email)
|
16
|
-
user = User.query.filter_by(id=user_id).one()
|
17
|
-
except ValueError:
|
18
|
-
user = User.query.filter_by(email=user_id_or_email).one()
|
19
|
-
|
20
|
-
identity = Identity(user.id)
|
21
|
-
identity.provides.add(UserNeed(str(user.id)))
|
22
|
-
api_app = current_app.wsgi_app.mounts["/api"]
|
23
|
-
with api_app.app_context():
|
24
|
-
with current_app.test_request_context("/api"):
|
25
|
-
identity_loaded.send(api_app, identity=identity)
|
26
|
-
return user, identity
|
@@ -1,63 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
|
3
|
-
import click
|
4
|
-
from invenio_records_permissions.policies.records import RecordPermissionPolicy
|
5
|
-
from invenio_records_resources.proxies import current_service_registry
|
6
|
-
|
7
|
-
from oarepo_runtime.info.permissions.debug import add_debugging
|
8
|
-
|
9
|
-
from .base import get_user_and_identity, permissions
|
10
|
-
|
11
|
-
|
12
|
-
@permissions.command(name="evaluate")
|
13
|
-
@click.argument("user_id_or_email")
|
14
|
-
@click.argument("service_name")
|
15
|
-
@click.argument("record_id", required=False)
|
16
|
-
@click.option("--data", "-d", help="Data to pass to the policy check")
|
17
|
-
@click.option("--explain/--no-explain", default=False)
|
18
|
-
@click.option("--draft/--published", default=False)
|
19
|
-
def evaluate_permissions(
|
20
|
-
user_id_or_email: str,
|
21
|
-
service_name: str,
|
22
|
-
record_id: str | None = None,
|
23
|
-
data: str | None = None,
|
24
|
-
explain: bool = False,
|
25
|
-
draft: bool = False,
|
26
|
-
):
|
27
|
-
"""Evaluate permissions for a given workflow, community or service."""
|
28
|
-
service = current_service_registry.get(service_name)
|
29
|
-
user, identity = get_user_and_identity(user_id_or_email)
|
30
|
-
|
31
|
-
over = {}
|
32
|
-
if record_id:
|
33
|
-
if draft:
|
34
|
-
over["record"] = service.config.draft_cls.pid.resolve(
|
35
|
-
record_id, registered_only=False
|
36
|
-
)
|
37
|
-
else:
|
38
|
-
over["record"] = service.config.record_cls.pid.resolve(record_id)
|
39
|
-
if data:
|
40
|
-
over["data"] = json.loads(data)
|
41
|
-
|
42
|
-
if explain:
|
43
|
-
over["debug_identity"] = identity
|
44
|
-
add_debugging()
|
45
|
-
|
46
|
-
policy_cls = service.config.permission_policy_cls
|
47
|
-
click.secho(f"Policy: {policy_cls}")
|
48
|
-
|
49
|
-
for action_name in dir(policy_cls):
|
50
|
-
if not action_name.startswith("can_"):
|
51
|
-
continue
|
52
|
-
|
53
|
-
policy: RecordPermissionPolicy = policy_cls(action_name[4:], **over)
|
54
|
-
if explain:
|
55
|
-
click.secho()
|
56
|
-
click.secho(f"## {action_name}")
|
57
|
-
try:
|
58
|
-
if policy.allows(identity):
|
59
|
-
click.secho(f"{action_name}: True", fg="green")
|
60
|
-
else:
|
61
|
-
click.secho(f"{action_name}: False", fg="red")
|
62
|
-
except Exception as e:
|
63
|
-
click.secho(f"{action_name}: {e}", fg="yellow")
|