rudi-node-write 1.1.0__tar.gz → 1.2.0__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.
- {rudi_node_write-1.1.0/src/rudi_node_write.egg-info → rudi_node_write-1.2.0}/PKG-INFO +2 -7
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/README.md +1 -1
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/pyproject.toml +3 -3
- rudi_node_write-1.2.0/src/rudi_node_write/__init__.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/io_connector.py +5 -4
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/io_rudi_api_write.py +20 -13
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/io_rudi_jwt_factory.py +7 -3
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/io_rudi_manager_write.py +148 -64
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/io_rudi_media_write.py +14 -8
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/rudi_node_auth.py +3 -2
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_node_writer.py +45 -13
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/serializable.py +3 -3
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/err.py +14 -11
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/file_utils.py +1 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/str_utils.py +9 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/url_utils.py +9 -0
- rudi_node_write-1.2.0/src/rudi_node_write/wip/federation_backup.py +145 -0
- rudi_node_write-1.2.0/src/rudi_node_write/wip/rudinode_federation.py +412 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0/src/rudi_node_write.egg-info}/PKG-INFO +2 -7
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write.egg-info/SOURCES.txt +2 -1
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/tests/test_rudi_node_write.py +3 -2
- rudi_node_write-1.1.0/requirements.txt +0 -7
- rudi_node_write-1.1.0/src/rudi_node_write.egg-info/requires.txt +0 -5
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/LICENCE.md +0 -0
- /rudi_node_write-1.1.0/src/rudi_node_write/__init__.py → /rudi_node_write-1.2.0/requirements.txt +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/setup.cfg +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/conf/meta_defaults.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_const.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_contact.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_dates.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_dictionary_entry.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_geo.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_licence.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_media.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_meta.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_meta_misc.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_org.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/dict_utils.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/html_utils.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/jwt.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/list_utils.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/log.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/type_date.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/typing_utils.py +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write.egg-info/dependency_links.txt +0 -0
- {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: rudi-node-write
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Use the internal API of a RUDI Producer node
|
|
5
5
|
Author-email: Olivier Martineau <olivier.martineau@irisa.fr>
|
|
6
6
|
Maintainer-email: Olivier Martineau <olivier.martineau@irisa.fr>
|
|
@@ -17,11 +17,6 @@ Classifier: License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1
|
|
|
17
17
|
Requires-Python: >=3.11
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENCE.md
|
|
20
|
-
Requires-Dist: beautifulsoup4==4.12.3
|
|
21
|
-
Requires-Dist: chardet==5.2.0
|
|
22
|
-
Requires-Dist: deepdiff==7.0.1
|
|
23
|
-
Requires-Dist: defusedxml==0.7.1
|
|
24
|
-
Requires-Dist: puremagic==1.24
|
|
25
20
|
|
|
26
21
|
[](https://github.com/psf/black)
|
|
27
22
|
[](https://microsoft.github.io/pyright/)
|
|
@@ -47,7 +42,7 @@ The Jupyter notebook [`README.ipynb`](https://github.com/OlivierMartineau/rudi-n
|
|
|
47
42
|
|
|
48
43
|
## Testing
|
|
49
44
|
|
|
50
|
-
The [tests](https://github.com/OlivierMartineau/rudi-node-write/tree/
|
|
45
|
+
The [tests](https://github.com/OlivierMartineau/rudi-node-write/tree/main/tests) can be analyzed for further
|
|
51
46
|
information about how to call the API
|
|
52
47
|
|
|
53
48
|
```bash
|
|
@@ -22,7 +22,7 @@ The Jupyter notebook [`README.ipynb`](https://github.com/OlivierMartineau/rudi-n
|
|
|
22
22
|
|
|
23
23
|
## Testing
|
|
24
24
|
|
|
25
|
-
The [tests](https://github.com/OlivierMartineau/rudi-node-write/tree/
|
|
25
|
+
The [tests](https://github.com/OlivierMartineau/rudi-node-write/tree/main/tests) can be analyzed for further
|
|
26
26
|
information about how to call the API
|
|
27
27
|
|
|
28
28
|
```bash
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rudi-node-write"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.2.0"
|
|
8
8
|
authors = [{ name = "Olivier Martineau", email = "olivier.martineau@irisa.fr" }]
|
|
9
9
|
maintainers = [{ name = "Olivier Martineau", email = "olivier.martineau@irisa.fr" }]
|
|
10
10
|
description = "Use the internal API of a RUDI Producer node"
|
|
@@ -34,13 +34,13 @@ target-version = ['py311']
|
|
|
34
34
|
# ----- Tool: commitizen
|
|
35
35
|
[tool.commitizen]
|
|
36
36
|
name = "cz_conventional_commits"
|
|
37
|
-
version = "1.
|
|
37
|
+
version = "1.2.0"
|
|
38
38
|
version_files = ["pyproject.toml:version"]
|
|
39
39
|
|
|
40
40
|
# ----- Tool: pytest
|
|
41
41
|
[tool.pytest.ini_options]
|
|
42
42
|
pythonpath = ["src"]
|
|
43
|
-
norecursedirs = ["*.egg", ".eggs", "dist", "build"]
|
|
43
|
+
norecursedirs = ["*.egg", ".eggs", "dist", "build", "wip"]
|
|
44
44
|
filterwarnings = ["ignore:.*pkg_resources.*:DeprecationWarning"]
|
|
45
45
|
addopts = ["--cov=rudi_node_write", "--cov-report=term-missing", "--cov-report=html:reports/html_dir", "--cov-report=xml:reports/coverage.xml" ]
|
|
46
46
|
|
|
File without changes
|
{rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/io_connector.py
RENAMED
|
@@ -44,12 +44,13 @@ def https_download(resource_url: str, headers=None, should_show_debug_line: bool
|
|
|
44
44
|
class Connector:
|
|
45
45
|
_default_connector = None
|
|
46
46
|
|
|
47
|
-
def __init__(self, server_url: str):
|
|
47
|
+
def __init__(self, server_url: str, keep_connection: bool = False):
|
|
48
48
|
self.scheme = None
|
|
49
49
|
self.host = None
|
|
50
50
|
self.path = None
|
|
51
51
|
self.base_url = None
|
|
52
52
|
self.connection = None
|
|
53
|
+
self.keep_connection = keep_connection
|
|
53
54
|
|
|
54
55
|
self._set_url(server_url)
|
|
55
56
|
self._cookies = None
|
|
@@ -63,7 +64,7 @@ class Connector:
|
|
|
63
64
|
self.host = netloc
|
|
64
65
|
self.path = path
|
|
65
66
|
self.base_url = slash_join(f"{self.scheme}://{self.host}", self.path)
|
|
66
|
-
log_d(here, "base_url", self.base_url)
|
|
67
|
+
# log_d(here, "base_url", self.base_url)
|
|
67
68
|
|
|
68
69
|
@property
|
|
69
70
|
def class_name(self):
|
|
@@ -119,7 +120,7 @@ class Connector:
|
|
|
119
120
|
else:
|
|
120
121
|
log_d_if(should_log_response, here, f"OK {response.status}", path_url)
|
|
121
122
|
res_data = response.read()
|
|
122
|
-
if not keep_alive:
|
|
123
|
+
if not keep_alive and not self.keep_connection:
|
|
123
124
|
self.connection.close()
|
|
124
125
|
if not res_data:
|
|
125
126
|
log_e(here, "empty data?")
|
|
@@ -214,7 +215,7 @@ class Connector:
|
|
|
214
215
|
except (TypeError, JSONDecodeError):
|
|
215
216
|
response_data = repr(rdata)
|
|
216
217
|
log_d_if(should_log_response, here, "Response is not a JSON", response_data)
|
|
217
|
-
if not keep_alive:
|
|
218
|
+
if not keep_alive and not self.keep_connection:
|
|
218
219
|
self.close_connection()
|
|
219
220
|
|
|
220
221
|
if type(response_data) is str:
|
{rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/io_rudi_api_write.py
RENAMED
|
@@ -30,6 +30,8 @@ from rudi_node_write.utils.list_utils import get_first_list_elt_or_none
|
|
|
30
30
|
from rudi_node_write.utils.log import log_d, log_e, log_w
|
|
31
31
|
from rudi_node_write.utils.str_utils import slash_join, uuid4_str, is_uuid_v4
|
|
32
32
|
from rudi_node_write.utils.typing_utils import get_type_name, check_type
|
|
33
|
+
from rudi_node_write.utils.url_utils import ensure_url_startswith
|
|
34
|
+
|
|
33
35
|
|
|
34
36
|
REQ_LIMIT = 500
|
|
35
37
|
_DELAY_REFRESH_S = 60 # seconds
|
|
@@ -39,11 +41,13 @@ _STATUS_SKIPPED = "skipped"
|
|
|
39
41
|
_STATUS_MISSING = "missing"
|
|
40
42
|
_STATUS_DOWNLOADED = "downloaded"
|
|
41
43
|
|
|
42
|
-
_API_ADMIN_PATH = "api/admin"
|
|
43
|
-
|
|
44
44
|
here = "RudiNodeApiConnector"
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
def ensure_url_startswith_api_admin(url):
|
|
48
|
+
return ensure_url_startswith(url, "api/admin")
|
|
49
|
+
|
|
50
|
+
|
|
47
51
|
class RudiNodeApiConnector(Connector):
|
|
48
52
|
def __init__(
|
|
49
53
|
self,
|
|
@@ -76,7 +80,6 @@ class RudiNodeApiConnector(Connector):
|
|
|
76
80
|
else:
|
|
77
81
|
raise AttributeError("Either a RudiNodeJwtFactory object or a valid JWT is required")
|
|
78
82
|
self.test_connection()
|
|
79
|
-
|
|
80
83
|
self._data_cache = {}
|
|
81
84
|
|
|
82
85
|
def test_connection(self):
|
|
@@ -85,7 +88,7 @@ class RudiNodeApiConnector(Connector):
|
|
|
85
88
|
log_e(here, f"!! Node '{self.host}'", "no connection!")
|
|
86
89
|
raise ConnectionError(f"An error occurred while connecting to RUDI node JWT server {self.base_url}")
|
|
87
90
|
|
|
88
|
-
log_d(here, f"Node '{self.host}'", "connection OK")
|
|
91
|
+
# log_d(here, f"Node '{self.host}'", "connection OK")
|
|
89
92
|
return test
|
|
90
93
|
|
|
91
94
|
def set_jwt_factory(self, jwt_factory: RudiNodeJwtFactory) -> None:
|
|
@@ -113,7 +116,9 @@ class RudiNodeApiConnector(Connector):
|
|
|
113
116
|
def _headers(self):
|
|
114
117
|
return self._initial_headers | {"Authorization": f"Bearer {self._jwt}"}
|
|
115
118
|
|
|
116
|
-
def get_admin_api(
|
|
119
|
+
def get_admin_api(
|
|
120
|
+
self, url: str, keep_alive: bool = False, should_log_request=False, should_log_response=False
|
|
121
|
+
) -> str | int | list | dict:
|
|
117
122
|
"""
|
|
118
123
|
Performs an identified GET request through /api/admin path
|
|
119
124
|
:param url: part of the URL that comes after /api/admin
|
|
@@ -123,9 +128,11 @@ class RudiNodeApiConnector(Connector):
|
|
|
123
128
|
"""
|
|
124
129
|
return self.request(
|
|
125
130
|
req_method="GET",
|
|
126
|
-
relative_url=
|
|
131
|
+
relative_url=ensure_url_startswith_api_admin(url),
|
|
127
132
|
headers=self._headers,
|
|
128
133
|
keep_alive=keep_alive,
|
|
134
|
+
should_log_request=should_log_request,
|
|
135
|
+
should_log_response=should_log_response,
|
|
129
136
|
) # type: ignore
|
|
130
137
|
|
|
131
138
|
def put_admin_api(
|
|
@@ -139,7 +146,7 @@ class RudiNodeApiConnector(Connector):
|
|
|
139
146
|
"""
|
|
140
147
|
return self.request(
|
|
141
148
|
req_method="PUT",
|
|
142
|
-
relative_url=
|
|
149
|
+
relative_url=ensure_url_startswith_api_admin(url),
|
|
143
150
|
headers=headers if headers else self._headers,
|
|
144
151
|
body=payload,
|
|
145
152
|
keep_alive=keep_alive,
|
|
@@ -156,7 +163,7 @@ class RudiNodeApiConnector(Connector):
|
|
|
156
163
|
"""
|
|
157
164
|
return self.request(
|
|
158
165
|
req_method="POST",
|
|
159
|
-
relative_url=
|
|
166
|
+
relative_url=ensure_url_startswith_api_admin(url),
|
|
160
167
|
headers=headers if headers else self._headers,
|
|
161
168
|
body=payload,
|
|
162
169
|
keep_alive=keep_alive,
|
|
@@ -171,7 +178,7 @@ class RudiNodeApiConnector(Connector):
|
|
|
171
178
|
"""
|
|
172
179
|
return self.request(
|
|
173
180
|
req_method="DELETE",
|
|
174
|
-
relative_url=
|
|
181
|
+
relative_url=ensure_url_startswith_api_admin(url),
|
|
175
182
|
headers=headers if headers else self._headers,
|
|
176
183
|
keep_alive=keep_alive,
|
|
177
184
|
)
|
|
@@ -766,7 +773,7 @@ if __name__ == "__main__": # pragma: no cover
|
|
|
766
773
|
# ----------- INIT -----------
|
|
767
774
|
tests = "RudiNodeApiConnector tests"
|
|
768
775
|
begin = time()
|
|
769
|
-
creds_file = "
|
|
776
|
+
creds_file = "../creds/creds_bas.json"
|
|
770
777
|
rudi_node_creds = read_json_file(creds_file)
|
|
771
778
|
url = rudi_node_creds["url"]
|
|
772
779
|
|
|
@@ -774,7 +781,7 @@ if __name__ == "__main__": # pragma: no cover
|
|
|
774
781
|
rudi_node_api = RudiNodeApiConnector(server_url=url, jwt_factory=node_jwt_factory)
|
|
775
782
|
|
|
776
783
|
# ----------- TESTS -----------
|
|
777
|
-
test_dir = "
|
|
784
|
+
test_dir = "../dwnld"
|
|
778
785
|
log_d(tests, "producers", len(rudi_node_api.organization_list))
|
|
779
786
|
log_d(tests, "producer names", rudi_node_api.producer_names)
|
|
780
787
|
log_d(tests, "metadata_contacts", len(rudi_node_api.contact_list))
|
|
@@ -793,13 +800,13 @@ if __name__ == "__main__": # pragma: no cover
|
|
|
793
800
|
log_d(
|
|
794
801
|
tests,
|
|
795
802
|
"download_files_for_metadata",
|
|
796
|
-
rudi_node_api.download_files_for_metadata("
|
|
803
|
+
rudi_node_api.download_files_for_metadata("65d99589-7a7a-46a3-afe8-c5a47b964310", test_dir),
|
|
797
804
|
)
|
|
798
805
|
|
|
799
806
|
log_d(
|
|
800
807
|
tests,
|
|
801
808
|
"download_file_with_media_uuid '782bab2d-7ee8-4633-9c0a-173649b4d879'",
|
|
802
|
-
rudi_node_api.download_file_with_media_uuid("
|
|
809
|
+
rudi_node_api.download_file_with_media_uuid("fef11852-0756-4cbe-bdfb-3722a1751de9", test_dir),
|
|
803
810
|
)
|
|
804
811
|
|
|
805
812
|
log_d(
|
|
@@ -22,10 +22,14 @@ class RudiNodeJwtFactory(Connector):
|
|
|
22
22
|
headers_user_agent: str = "RudiNodeJwtFactory",
|
|
23
23
|
):
|
|
24
24
|
super().__init__(server_url)
|
|
25
|
+
self.sub = "rudi_prod_token"
|
|
25
26
|
if has_key(auth, B64_AUTH_KEY):
|
|
26
27
|
self._b64_auth = f"Basic {auth[B64_AUTH_KEY]}"
|
|
27
28
|
elif has_key(auth, USR_AUTH_KEY) and has_key(auth, PWD_AUTH_KEY):
|
|
28
29
|
self._b64_auth = get_basic_auth(auth[USR_AUTH_KEY], auth[PWD_AUTH_KEY])
|
|
30
|
+
elif has_key(auth, "sub"):
|
|
31
|
+
self.sub = auth["sub"]
|
|
32
|
+
self._b64_auth = ""
|
|
29
33
|
else:
|
|
30
34
|
err_msg = f"{B64_AUTH_KEY}', or both '{USR_AUTH_KEY}' and '{PWD_AUTH_KEY}"
|
|
31
35
|
raise UnexpectedValueException("auth", err_msg, auth)
|
|
@@ -45,7 +49,7 @@ class RudiNodeJwtFactory(Connector):
|
|
|
45
49
|
if not isinstance(test, dict) or test["RUDI"] != "JWT":
|
|
46
50
|
log_e("RudiNodeJwtFactory", f"!! Node '{self.host}'", "no connection!")
|
|
47
51
|
raise ConnectionError(f"An error occurred while connecting to RUDI node JWT server {self.base_url}")
|
|
48
|
-
log_d("RudiNodeJwtFactory", f"Node '{self.host}'", "connection OK")
|
|
52
|
+
# log_d("RudiNodeJwtFactory", f"Node '{self.host}'", "connection OK")
|
|
49
53
|
return True
|
|
50
54
|
|
|
51
55
|
def _renew_jwt(self, delay_s: int) -> str:
|
|
@@ -53,7 +57,7 @@ class RudiNodeJwtFactory(Connector):
|
|
|
53
57
|
delay_s = self._default_exp_s
|
|
54
58
|
jwt_body = {
|
|
55
59
|
"exp": Date.time_epoch_s(delay_s),
|
|
56
|
-
"sub":
|
|
60
|
+
"sub": self.sub,
|
|
57
61
|
"req_mtd": "all",
|
|
58
62
|
"req_url": "all",
|
|
59
63
|
}
|
|
@@ -77,7 +81,7 @@ class RudiNodeJwtFactory(Connector):
|
|
|
77
81
|
|
|
78
82
|
if __name__ == "__main__": # pragma: no cover
|
|
79
83
|
tests = "JwtFactory tests"
|
|
80
|
-
creds_file = "
|
|
84
|
+
creds_file = "../creds/creds_bas.json"
|
|
81
85
|
rudi_node_creds = read_json_file(creds_file)
|
|
82
86
|
# log_d(tests, "rudi_node_creds", rudi_node_creds)
|
|
83
87
|
rudi_jwt_connector = RudiNodeJwtFactory(rudi_node_creds["url"], rudi_node_creds)
|