rudi-node-write 1.2.0__tar.gz → 1.2.2__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.2.0/src/rudi_node_write.egg-info → rudi_node_write-1.2.2}/PKG-INFO +24 -1
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/pyproject.toml +5 -4
- rudi_node_write-1.2.2/requirements-dev.txt +102 -0
- rudi_node_write-1.2.2/requirements.txt +6 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/connectors/io_rudi_manager_write.py +12 -8
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_media.py +8 -1
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/utils/file_utils.py +38 -16
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/utils/list_utils.py +13 -13
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2/src/rudi_node_write.egg-info}/PKG-INFO +24 -1
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write.egg-info/SOURCES.txt +2 -2
- rudi_node_write-1.2.2/src/rudi_node_write.egg-info/requires.txt +24 -0
- rudi_node_write-1.2.0/requirements.txt +0 -0
- rudi_node_write-1.2.0/src/rudi_node_write/wip/federation_backup.py +0 -145
- rudi_node_write-1.2.0/src/rudi_node_write/wip/rudinode_federation.py +0 -412
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/LICENCE.md +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/README.md +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/setup.cfg +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/__init__.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/conf/meta_defaults.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/connectors/io_connector.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/connectors/io_rudi_api_write.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/connectors/io_rudi_jwt_factory.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/connectors/io_rudi_media_write.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/connectors/rudi_node_auth.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_node_writer.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_const.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_contact.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_dates.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_dictionary_entry.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_geo.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_licence.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_meta.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_meta_misc.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_org.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/serializable.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/utils/dict_utils.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/utils/err.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/utils/html_utils.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/utils/jwt.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/utils/log.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/utils/str_utils.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/utils/type_date.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/utils/typing_utils.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/utils/url_utils.py +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write.egg-info/dependency_links.txt +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write.egg-info/top_level.txt +0 -0
- {rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/tests/test_rudi_node_write.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: rudi-node-write
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
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,6 +17,29 @@ 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==8.0.1
|
|
23
|
+
Requires-Dist: puremagic==1.28
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: black==24.10.0; extra == "dev"
|
|
26
|
+
Requires-Dist: build==1.2.2.post1; extra == "dev"
|
|
27
|
+
Requires-Dist: commitizen==3.29.1; extra == "dev"
|
|
28
|
+
Requires-Dist: importlib-resources==6.4.5; extra == "dev"
|
|
29
|
+
Requires-Dist: inflect==7.3.1; extra == "dev"
|
|
30
|
+
Requires-Dist: ipykernel==6.29.5; extra == "dev"
|
|
31
|
+
Requires-Dist: jaraco.collections==5.1.0; extra == "dev"
|
|
32
|
+
Requires-Dist: nbstripout==0.7.1; extra == "dev"
|
|
33
|
+
Requires-Dist: ordered-set==4.1.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pip-autoremove==0.10.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pip-chill==1.0.3; extra == "dev"
|
|
36
|
+
Requires-Dist: pip-upgrade-outdated==1.5; extra == "dev"
|
|
37
|
+
Requires-Dist: pip3-autoremove==1.2.2; extra == "dev"
|
|
38
|
+
Requires-Dist: pre-commit==4.0.1; extra == "dev"
|
|
39
|
+
Requires-Dist: pyright==1.1.386; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-cov==5.0.0; extra == "dev"
|
|
41
|
+
Requires-Dist: tomli==2.0.2; extra == "dev"
|
|
42
|
+
Requires-Dist: twine==5.1.1; extra == "dev"
|
|
20
43
|
|
|
21
44
|
[](https://github.com/psf/black)
|
|
22
45
|
[](https://microsoft.github.io/pyright/)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rudi-node-write"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.2"
|
|
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"
|
|
@@ -18,8 +18,8 @@ classifiers = ["Programming Language :: Python :: 3.11",
|
|
|
18
18
|
"License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1.2)",
|
|
19
19
|
]
|
|
20
20
|
keywords = ["rudi-node-write", "rudi-node-put", "RUDI", "producer node", "RUDI node", "open-data", "Univ. Rennes"]
|
|
21
|
-
dynamic = ["dependencies"]
|
|
22
|
-
# dependencies = ["beautifulsoup4", "chardet", "deepdiff", "
|
|
21
|
+
dynamic = ["dependencies", "optional-dependencies"]
|
|
22
|
+
# dependencies = ["beautifulsoup4", "chardet", "deepdiff", "puremagic"]
|
|
23
23
|
|
|
24
24
|
[project.urls]
|
|
25
25
|
Homepage = "https://github.com/OlivierMartineau/rudi-node-write"
|
|
@@ -34,7 +34,7 @@ target-version = ['py311']
|
|
|
34
34
|
# ----- Tool: commitizen
|
|
35
35
|
[tool.commitizen]
|
|
36
36
|
name = "cz_conventional_commits"
|
|
37
|
-
version = "1.2.
|
|
37
|
+
version = "1.2.2"
|
|
38
38
|
version_files = ["pyproject.toml:version"]
|
|
39
39
|
|
|
40
40
|
# ----- Tool: pytest
|
|
@@ -51,6 +51,7 @@ include = ["rudi_node_write*"]
|
|
|
51
51
|
|
|
52
52
|
[tool.setuptools.dynamic]
|
|
53
53
|
dependencies = {file = ["requirements.txt"]}
|
|
54
|
+
optional-dependencies = {dev = { file = ["requirements-dev.txt"] }}
|
|
54
55
|
|
|
55
56
|
# ----- Tool: pyright
|
|
56
57
|
[tool.pyright]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
black==24.10.0
|
|
2
|
+
build==1.2.2.post1
|
|
3
|
+
commitizen==3.29.1
|
|
4
|
+
importlib-resources==6.4.5
|
|
5
|
+
inflect==7.3.1
|
|
6
|
+
ipykernel==6.29.5
|
|
7
|
+
jaraco.collections==5.1.0
|
|
8
|
+
nbstripout==0.7.1
|
|
9
|
+
ordered-set==4.1.0
|
|
10
|
+
pip-autoremove==0.10.0
|
|
11
|
+
pip-chill==1.0.3
|
|
12
|
+
pip-upgrade-outdated==1.5
|
|
13
|
+
pip3-autoremove==1.2.2
|
|
14
|
+
pre-commit==4.0.1
|
|
15
|
+
pyright==1.1.386
|
|
16
|
+
pytest-cov==5.0.0
|
|
17
|
+
tomli==2.0.2
|
|
18
|
+
twine==5.1.1
|
|
19
|
+
# appnope==0.1.4 # Installed as dependency for ipykernel
|
|
20
|
+
# argcomplete==3.5.1 # Installed as dependency for commitizen
|
|
21
|
+
# asttokens==2.4.1 # Installed as dependency for stack-data
|
|
22
|
+
# attrs==24.2.0 # Installed as dependency for jsonschema, referencing
|
|
23
|
+
# autocommand==2.2.2 # Installed as dependency for jaraco.text
|
|
24
|
+
# backports.tarfile==1.2.0 # Installed as dependency for jaraco.context
|
|
25
|
+
# certifi==2024.8.30 # Installed as dependency for requests
|
|
26
|
+
# cfgv==3.4.0 # Installed as dependency for pre-commit
|
|
27
|
+
# charset-normalizer==3.4.0 # Installed as dependency for commitizen, requests
|
|
28
|
+
# click==8.1.7 # Installed as dependency for black
|
|
29
|
+
# colorama==0.4.6 # Installed as dependency for commitizen
|
|
30
|
+
# comm==0.2.2 # Installed as dependency for ipykernel
|
|
31
|
+
# coverage==7.6.4 # Installed as dependency for pytest-cov
|
|
32
|
+
# debugpy==1.8.7 # Installed as dependency for ipykernel
|
|
33
|
+
# decli==0.6.2 # Installed as dependency for commitizen
|
|
34
|
+
# decorator==5.1.1 # Installed as dependency for ipython
|
|
35
|
+
# distlib==0.3.9 # Installed as dependency for virtualenv
|
|
36
|
+
# docutils==0.21.2 # Installed as dependency for readme-renderer
|
|
37
|
+
# executing==2.1.0 # Installed as dependency for stack-data
|
|
38
|
+
# fastjsonschema==2.20.0 # Installed as dependency for nbformat
|
|
39
|
+
# filelock==3.16.1 # Installed as dependency for virtualenv
|
|
40
|
+
# identify==2.6.1 # Installed as dependency for pre-commit
|
|
41
|
+
# idna==3.10 # Installed as dependency for requests
|
|
42
|
+
# importlib-metadata==8.5.0 # Installed as dependency for keyring, twine
|
|
43
|
+
# iniconfig==2.0.0 # Installed as dependency for pytest
|
|
44
|
+
# ipython==8.28.0 # Installed as dependency for ipykernel
|
|
45
|
+
# jaraco.classes==3.4.0 # Installed as dependency for keyring
|
|
46
|
+
# jaraco.context==6.0.1 # Installed as dependency for jaraco.text, keyring
|
|
47
|
+
# jaraco.functools==4.1.0 # Installed as dependency for jaraco.text, keyring
|
|
48
|
+
# jaraco.text==4.0.0 # Installed as dependency for jaraco.collections
|
|
49
|
+
# jedi==0.19.1 # Installed as dependency for ipython
|
|
50
|
+
# jinja2==3.1.4 # Installed as dependency for commitizen
|
|
51
|
+
# jsonschema==4.23.0 # Installed as dependency for nbformat
|
|
52
|
+
# jsonschema-specifications==2024.10.1 # Installed as dependency for jsonschema
|
|
53
|
+
# jupyter-client==8.6.3 # Installed as dependency for ipykernel
|
|
54
|
+
# jupyter-core==5.7.2 # Installed as dependency for ipykernel, jupyter-client, nbformat
|
|
55
|
+
# keyring==25.4.1 # Installed as dependency for twine
|
|
56
|
+
# markdown-it-py==3.0.0 # Installed as dependency for rich
|
|
57
|
+
# markupsafe==3.0.2 # Installed as dependency for jinja2
|
|
58
|
+
# matplotlib-inline==0.1.7 # Installed as dependency for ipykernel, ipython
|
|
59
|
+
# mdurl==0.1.2 # Installed as dependency for markdown-it-py
|
|
60
|
+
# more-itertools==10.5.0 # Installed as dependency for inflect, jaraco.classes, jaraco.functools, jaraco.text
|
|
61
|
+
# mypy-extensions==1.0.0 # Installed as dependency for black
|
|
62
|
+
# nbformat==5.10.4 # Installed as dependency for nbstripout
|
|
63
|
+
# nest-asyncio==1.6.0 # Installed as dependency for ipykernel
|
|
64
|
+
# nh3==0.2.18 # Installed as dependency for readme-renderer
|
|
65
|
+
# nodeenv==1.9.1 # Installed as dependency for pre-commit, pyright
|
|
66
|
+
# packaging==24.1 # Installed as dependency for black, build, commitizen, ipykernel, pytest
|
|
67
|
+
# parso==0.8.4 # Installed as dependency for jedi
|
|
68
|
+
# pathspec==0.12.1 # Installed as dependency for black
|
|
69
|
+
# pexpect==4.9.0 # Installed as dependency for ipython
|
|
70
|
+
# pkginfo==1.11.2 # Installed as dependency for twine
|
|
71
|
+
# platformdirs==4.3.6 # Installed as dependency for black, jupyter-core, virtualenv
|
|
72
|
+
# pluggy==1.5.0 # Installed as dependency for pytest
|
|
73
|
+
# prompt-toolkit==3.0.48 # Installed as dependency for ipython, questionary
|
|
74
|
+
# psutil==6.1.0 # Installed as dependency for ipykernel
|
|
75
|
+
# ptyprocess==0.7.0 # Installed as dependency for pexpect
|
|
76
|
+
# pure-eval==0.2.3 # Installed as dependency for stack-data
|
|
77
|
+
# pygments==2.18.0 # Installed as dependency for ipython, readme-renderer, rich
|
|
78
|
+
# pyproject-hooks==1.2.0 # Installed as dependency for build
|
|
79
|
+
# pytest==8.3.3 # Installed as dependency for pytest-cov
|
|
80
|
+
# python-dateutil==2.9.0.post0 # Installed as dependency for jupyter-client
|
|
81
|
+
# pyyaml==6.0.2 # Installed as dependency for commitizen, pre-commit
|
|
82
|
+
# pyzmq==26.2.0 # Installed as dependency for ipykernel, jupyter-client
|
|
83
|
+
# questionary==2.0.1 # Installed as dependency for commitizen
|
|
84
|
+
# readme-renderer==44.0 # Installed as dependency for twine
|
|
85
|
+
# referencing==0.35.1 # Installed as dependency for jsonschema, jsonschema-specifications
|
|
86
|
+
# requests==2.32.3 # Installed as dependency for requests-toolbelt, twine
|
|
87
|
+
# requests-toolbelt==1.0.0 # Installed as dependency for twine
|
|
88
|
+
# rfc3986==2.0.0 # Installed as dependency for twine
|
|
89
|
+
# rich==13.9.3 # Installed as dependency for twine
|
|
90
|
+
# rpds-py==0.20.0 # Installed as dependency for jsonschema, referencing
|
|
91
|
+
# six==1.16.0 # Installed as dependency for asttokens, python-dateutil
|
|
92
|
+
# stack-data==0.6.3 # Installed as dependency for ipython
|
|
93
|
+
# termcolor==2.5.0 # Installed as dependency for commitizen
|
|
94
|
+
# tomlkit==0.13.2 # Installed as dependency for commitizen
|
|
95
|
+
# tornado==6.4.1 # Installed as dependency for ipykernel, jupyter-client
|
|
96
|
+
# traitlets==5.14.3 # Installed as dependency for comm, ipykernel, ipython, jupyter-client, jupyter-core, matplotlib-inline, nbformat
|
|
97
|
+
# typeguard==4.3.0 # Installed as dependency for inflect
|
|
98
|
+
# typing-extensions==4.12.2 # Installed as dependency for ipython, pyright, typeguard
|
|
99
|
+
# urllib3==2.2.3 # Installed as dependency for requests, twine
|
|
100
|
+
# virtualenv==20.27.0 # Installed as dependency for pre-commit
|
|
101
|
+
# wcwidth==0.2.13 # Installed as dependency for prompt-toolkit
|
|
102
|
+
# zipp==3.20.2 # Installed as dependency for importlib-metadata
|
|
@@ -55,6 +55,8 @@ def ensure_url_startswith_api(url):
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def ensure_url_startswith_api_data(url):
|
|
58
|
+
if url.startswith("data"):
|
|
59
|
+
return ensure_url_startswith_api(url)
|
|
58
60
|
return ensure_url_startswith(url, "api/data")
|
|
59
61
|
|
|
60
62
|
|
|
@@ -367,7 +369,7 @@ class RudiNodeManagerConnector(Connector):
|
|
|
367
369
|
if url_bit in RUDI_OBJECT_TYPES:
|
|
368
370
|
data: list[dict] = self._get_full_obj_list(url_bit)
|
|
369
371
|
else:
|
|
370
|
-
data = self._get_api(url=url_bit, headers=self._pm_headers) # type: ignore
|
|
372
|
+
data = self._get_api(url=url_bit, headers=self._pm_headers, should_log_request=True) # type: ignore
|
|
371
373
|
|
|
372
374
|
self._data_cache[url_bit] = {"data": data, _REFRESH_KEY: time()}
|
|
373
375
|
obj_data = self._data_cache[url_bit]["data"]
|
|
@@ -394,7 +396,7 @@ class RudiNodeManagerConnector(Connector):
|
|
|
394
396
|
log_w(here, "Legacy node, request not available for 'media'")
|
|
395
397
|
return {}
|
|
396
398
|
else:
|
|
397
|
-
return self._get_cache(
|
|
399
|
+
return self._get_cache(ensure_url_startswith(obj_type, "data"))
|
|
398
400
|
|
|
399
401
|
# ----------[ Requesting data ]-------------------------------------------------------------------------------------
|
|
400
402
|
|
|
@@ -439,7 +441,7 @@ class RudiNodeManagerConnector(Connector):
|
|
|
439
441
|
|
|
440
442
|
@property
|
|
441
443
|
def last_metadata_update_date(self) -> Date | None:
|
|
442
|
-
res = self.get_data("resources
|
|
444
|
+
res = self.get_data("resources?sort=-updatedAt&limit=1&fields=updatedAt")
|
|
443
445
|
if not isinstance(res, list):
|
|
444
446
|
raise TypeError(f"The server should have returned a list, got:\n{res}")
|
|
445
447
|
if len(res) == 0:
|
|
@@ -449,7 +451,7 @@ class RudiNodeManagerConnector(Connector):
|
|
|
449
451
|
|
|
450
452
|
@property
|
|
451
453
|
def last_data_update_date(self) -> Date | None:
|
|
452
|
-
res = self.get_data("resources
|
|
454
|
+
res = self.get_data("resources?sort=-dataset_dates.updated&limit=1&fields=dataset_dates.updated")
|
|
453
455
|
print("last_data_update_date", res)
|
|
454
456
|
if not isinstance(res, list):
|
|
455
457
|
raise TypeError(f"The server should have returned a list, got:\n{res}")
|
|
@@ -1300,12 +1302,11 @@ class FileTooBigException(Exception):
|
|
|
1300
1302
|
if __name__ == "__main__": # pragma: no cover
|
|
1301
1303
|
begin = time()
|
|
1302
1304
|
tests = "RudiNodeManagerConnector tests"
|
|
1303
|
-
creds_file = "../creds/
|
|
1305
|
+
creds_file = "../creds/creds_release.json"
|
|
1304
1306
|
rudi_node_creds = read_json_file(creds_file)
|
|
1305
|
-
|
|
1306
|
-
log_d(tests, "node_url",
|
|
1307
|
+
pm_url = rudi_node_creds["pm_url"]
|
|
1308
|
+
log_d(tests, "node_url", pm_url)
|
|
1307
1309
|
auth = RudiNodeAuth.from_json(rudi_node_creds)
|
|
1308
|
-
pm_url = slash_join(node_url, "/prodmanager")
|
|
1309
1310
|
|
|
1310
1311
|
pm_connector = RudiNodeManagerConnector(server_url=pm_url, auth=auth)
|
|
1311
1312
|
pm_connector.test_connection()
|
|
@@ -1354,4 +1355,7 @@ if __name__ == "__main__": # pragma: no cover
|
|
|
1354
1355
|
)
|
|
1355
1356
|
except HttpError as e:
|
|
1356
1357
|
log_w(tests, e)
|
|
1358
|
+
|
|
1359
|
+
log_d(tests, "last_metadata_update_date", pm_connector.last_metadata_update_date)
|
|
1360
|
+
log_d(tests, "last_data_update_date", pm_connector.last_data_update_date)
|
|
1357
1361
|
log_d(tests, "exec. time", time() - begin)
|
{rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_media.py
RENAMED
|
@@ -52,6 +52,13 @@ def check_is_accepted_value(value, accepted_values: list | None = None):
|
|
|
52
52
|
return value
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
def get_normalized_type_name(value):
|
|
56
|
+
if isinstance(value, str):
|
|
57
|
+
if value.lower() in ("true", "false"):
|
|
58
|
+
return "bool"
|
|
59
|
+
return get_type_name(value)
|
|
60
|
+
|
|
61
|
+
|
|
55
62
|
def normalize_connector_values(value, value_type: str | None = None, accepted_values: list | None = None):
|
|
56
63
|
if value_type is None:
|
|
57
64
|
type_name = get_type_name(value)
|
|
@@ -66,7 +73,7 @@ def normalize_connector_values(value, value_type: str | None = None, accepted_va
|
|
|
66
73
|
else:
|
|
67
74
|
# log_d(here, "in", value, f"({value_type})")
|
|
68
75
|
normalized_val_type = normalize_connector_parameter_type(value_type)
|
|
69
|
-
if normalize_connector_parameter_type(
|
|
76
|
+
if normalize_connector_parameter_type(get_normalized_type_name(value)) != normalized_val_type:
|
|
70
77
|
raise ValueError(f"incoherence in connector parameter type: input '{value}' is not of type '{value_type}'")
|
|
71
78
|
return [check_is_accepted_value(value, accepted_values), normalized_val_type, accepted_values]
|
|
72
79
|
|
|
@@ -70,6 +70,8 @@ def get_file_mime(file_local_path: str) -> str:
|
|
|
70
70
|
"""
|
|
71
71
|
file_info = magic_file(file_local_path)
|
|
72
72
|
if not file_info: # pragma: no cover
|
|
73
|
+
if get_file_extension(file_local_path) == ".csv":
|
|
74
|
+
return "text/csv"
|
|
73
75
|
return "application/octet-stream"
|
|
74
76
|
mime_type = file_info[0].mime_type
|
|
75
77
|
if mime_type == "application/x-gzip": # pragma: no cover
|
|
@@ -158,13 +160,17 @@ if __name__ == "__main__": # pragma: no cover
|
|
|
158
160
|
tests = "FileUtils"
|
|
159
161
|
begin = time()
|
|
160
162
|
|
|
161
|
-
test_file_dir = "
|
|
163
|
+
test_file_dir = "../tests/_test_files/"
|
|
162
164
|
yaml_file_path = test_file_dir + "RUDI producer internal API - 1.3.0.yml"
|
|
163
165
|
right_path = test_file_dir + "unicorn.png"
|
|
164
166
|
bin_file = test_file_dir + "WERTSTOFFE.m8s"
|
|
165
167
|
tar_gz_file = test_file_dir + "rudi-node-read.tar.gz"
|
|
166
168
|
txt_file = test_file_dir + "RUDI producer internal API - 1.3.0.yml"
|
|
169
|
+
csv_file = test_file_dir + "dummy.csv"
|
|
167
170
|
wrong_path = test_file_dir + "toto"
|
|
171
|
+
unicode_file_path = test_file_dir + "unicode_chars.txt"
|
|
172
|
+
write_file(unicode_file_path, "tut0156êµîƒfiÌπÏ“{ëôøÇ¡¶{µœ≤é≤")
|
|
173
|
+
|
|
168
174
|
log_d(tests, "exists file OK", exists_file(right_path))
|
|
169
175
|
log_d(tests, "exists file KO", exists_file(wrong_path))
|
|
170
176
|
log_d(tests, "is file OK", is_file(right_path))
|
|
@@ -172,22 +178,38 @@ if __name__ == "__main__": # pragma: no cover
|
|
|
172
178
|
log_d(tests, "exists file (dir)", exists_file(test_file_dir))
|
|
173
179
|
log_d(tests, "is dir a file", is_file(test_file_dir))
|
|
174
180
|
# log_d(here, 'check_is_file', check_is_file(test_file_dir))
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
181
|
+
for file in [
|
|
182
|
+
right_path,
|
|
183
|
+
"foobar.tar.gz",
|
|
184
|
+
"foo.bar.tar.gz",
|
|
185
|
+
".bashrc",
|
|
186
|
+
yaml_file_path,
|
|
187
|
+
csv_file,
|
|
188
|
+
bin_file,
|
|
189
|
+
tar_gz_file,
|
|
190
|
+
txt_file,
|
|
191
|
+
unicode_file_path,
|
|
192
|
+
]:
|
|
193
|
+
if exists_file(file):
|
|
194
|
+
log_d(
|
|
195
|
+
tests,
|
|
196
|
+
f"file: '{file}' ",
|
|
197
|
+
f"\n- exists_file: {exists_file(file)}",
|
|
198
|
+
f"\n- extension: {get_file_extension(file)}",
|
|
199
|
+
f"\n- size: {get_file_size(file)}",
|
|
200
|
+
f"\n- MIME: {get_file_mime(file)}",
|
|
201
|
+
f"\n- charset: {get_file_charset(file)}",
|
|
202
|
+
f"\n- MD5: {get_file_hash(file)}",
|
|
203
|
+
f"\n- info: {FileDetails(file)}",
|
|
204
|
+
)
|
|
205
|
+
else:
|
|
206
|
+
log_d(
|
|
207
|
+
tests,
|
|
208
|
+
f"file: '{file}' ",
|
|
209
|
+
f"\n- exists_file: {exists_file(file)}",
|
|
210
|
+
f"\n- extension: {get_file_extension(file)}",
|
|
211
|
+
)
|
|
188
212
|
# log_d(here, 'file info', get_file_info(wrong_path))
|
|
189
|
-
unicode_file_path = test_file_dir + "unicode_chars.txt"
|
|
190
|
-
write_file(unicode_file_path, "tut0156êµîƒfiÌπÏ“{ëôøÇ¡¶{µœ≤é≤")
|
|
191
213
|
|
|
192
214
|
log_d(tests, "unicode file MD5 hash", get_file_hash(unicode_file_path))
|
|
193
215
|
log_d(tests, "unicode file SHA-256 hash", get_file_hash(unicode_file_path, "sha256"))
|
|
@@ -101,16 +101,16 @@ def clean_nones(value):
|
|
|
101
101
|
return value
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
#
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
#
|
|
104
|
+
if __name__ == "__main__": # pragma: no cover
|
|
105
|
+
tests = "tests"
|
|
106
|
+
# begin = time()
|
|
107
|
+
a = [1, 2, 3, 4, 5]
|
|
108
|
+
b = [9, 8, 7, 6, {"r": [5, 6]}]
|
|
109
|
+
c = [8, 7, 6, {"r": [5, 6]}, 9]
|
|
110
|
+
print(tests, f"{a} Δ {b}", list_diff(a, b))
|
|
111
|
+
print(tests, f"{a} Δ {c}", list_diff(a, c))
|
|
112
|
+
print(tests, f"{b} Δ {c}", list_diff(b, c))
|
|
113
|
+
print(tests, "b == c", are_list_equal(b, c))
|
|
114
|
+
# print(tests, f'{b} == {c} ->', are_list_equal(b, c))
|
|
115
|
+
# print(tests, f'dict eq ->', {'r': [6, 5]} != {'r': [5, 6]})
|
|
116
|
+
# log_d(tests, "exec. time", time() - begin)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: rudi-node-write
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
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,6 +17,29 @@ 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==8.0.1
|
|
23
|
+
Requires-Dist: puremagic==1.28
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: black==24.10.0; extra == "dev"
|
|
26
|
+
Requires-Dist: build==1.2.2.post1; extra == "dev"
|
|
27
|
+
Requires-Dist: commitizen==3.29.1; extra == "dev"
|
|
28
|
+
Requires-Dist: importlib-resources==6.4.5; extra == "dev"
|
|
29
|
+
Requires-Dist: inflect==7.3.1; extra == "dev"
|
|
30
|
+
Requires-Dist: ipykernel==6.29.5; extra == "dev"
|
|
31
|
+
Requires-Dist: jaraco.collections==5.1.0; extra == "dev"
|
|
32
|
+
Requires-Dist: nbstripout==0.7.1; extra == "dev"
|
|
33
|
+
Requires-Dist: ordered-set==4.1.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pip-autoremove==0.10.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pip-chill==1.0.3; extra == "dev"
|
|
36
|
+
Requires-Dist: pip-upgrade-outdated==1.5; extra == "dev"
|
|
37
|
+
Requires-Dist: pip3-autoremove==1.2.2; extra == "dev"
|
|
38
|
+
Requires-Dist: pre-commit==4.0.1; extra == "dev"
|
|
39
|
+
Requires-Dist: pyright==1.1.386; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-cov==5.0.0; extra == "dev"
|
|
41
|
+
Requires-Dist: tomli==2.0.2; extra == "dev"
|
|
42
|
+
Requires-Dist: twine==5.1.1; extra == "dev"
|
|
20
43
|
|
|
21
44
|
[](https://github.com/psf/black)
|
|
22
45
|
[](https://microsoft.github.io/pyright/)
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
LICENCE.md
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
|
+
requirements-dev.txt
|
|
4
5
|
requirements.txt
|
|
5
6
|
src/rudi_node_write/__init__.py
|
|
6
7
|
src/rudi_node_write/rudi_node_writer.py
|
|
7
8
|
src/rudi_node_write.egg-info/PKG-INFO
|
|
8
9
|
src/rudi_node_write.egg-info/SOURCES.txt
|
|
9
10
|
src/rudi_node_write.egg-info/dependency_links.txt
|
|
11
|
+
src/rudi_node_write.egg-info/requires.txt
|
|
10
12
|
src/rudi_node_write.egg-info/top_level.txt
|
|
11
13
|
src/rudi_node_write/conf/meta_defaults.py
|
|
12
14
|
src/rudi_node_write/connectors/io_connector.py
|
|
@@ -37,6 +39,4 @@ src/rudi_node_write/utils/str_utils.py
|
|
|
37
39
|
src/rudi_node_write/utils/type_date.py
|
|
38
40
|
src/rudi_node_write/utils/typing_utils.py
|
|
39
41
|
src/rudi_node_write/utils/url_utils.py
|
|
40
|
-
src/rudi_node_write/wip/federation_backup.py
|
|
41
|
-
src/rudi_node_write/wip/rudinode_federation.py
|
|
42
42
|
tests/test_rudi_node_write.py
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
beautifulsoup4==4.12.3
|
|
2
|
+
chardet==5.2.0
|
|
3
|
+
deepdiff==8.0.1
|
|
4
|
+
puremagic==1.28
|
|
5
|
+
|
|
6
|
+
[dev]
|
|
7
|
+
black==24.10.0
|
|
8
|
+
build==1.2.2.post1
|
|
9
|
+
commitizen==3.29.1
|
|
10
|
+
importlib-resources==6.4.5
|
|
11
|
+
inflect==7.3.1
|
|
12
|
+
ipykernel==6.29.5
|
|
13
|
+
jaraco.collections==5.1.0
|
|
14
|
+
nbstripout==0.7.1
|
|
15
|
+
ordered-set==4.1.0
|
|
16
|
+
pip-autoremove==0.10.0
|
|
17
|
+
pip-chill==1.0.3
|
|
18
|
+
pip-upgrade-outdated==1.5
|
|
19
|
+
pip3-autoremove==1.2.2
|
|
20
|
+
pre-commit==4.0.1
|
|
21
|
+
pyright==1.1.386
|
|
22
|
+
pytest-cov==5.0.0
|
|
23
|
+
tomli==2.0.2
|
|
24
|
+
twine==5.1.1
|
|
File without changes
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
from concurrent.futures import as_completed, ThreadPoolExecutor
|
|
2
|
-
from os import cpu_count
|
|
3
|
-
from time import sleep, time
|
|
4
|
-
from urllib.request import urlopen
|
|
5
|
-
|
|
6
|
-
from rudi_node_write.connectors.io_rudi_manager_write import RudiNodeManagerConnector
|
|
7
|
-
from rudi_node_write.connectors.rudi_node_auth import RudiNodeAuth
|
|
8
|
-
from rudi_node_write.rudi_node_writer import RudiNodeWriter
|
|
9
|
-
from rudi_node_write.utils.file_utils import check_is_dir, check_is_file, read_json_file, write_json_file
|
|
10
|
-
from rudi_node_write.utils.log import log_d, log_e, log_w
|
|
11
|
-
from rudi_node_write.utils.str_utils import slash_join
|
|
12
|
-
from rudi_node_write.utils.type_date import Date
|
|
13
|
-
|
|
14
|
-
federation_data = {}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
MAX_WORKERS = 0
|
|
18
|
-
DEFAULT_WAIT_TIME = 0.3
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def try_request(req, wait_time=DEFAULT_WAIT_TIME, quit_on_fail=True):
|
|
22
|
-
fun = "try_request"
|
|
23
|
-
sleep(wait_time)
|
|
24
|
-
try:
|
|
25
|
-
return req
|
|
26
|
-
except Exception as err:
|
|
27
|
-
if quit_on_fail:
|
|
28
|
-
return "FAIL"
|
|
29
|
-
# Let's try one more time
|
|
30
|
-
return try_request(req, wait_time, True)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def backup_node(node_name, node_info):
|
|
34
|
-
node_url = node_info["url"]
|
|
35
|
-
pm_url = node_info["pmback"] if node_info.get("pmback") is not None else (slash_join(node_url, "prodmanager"))
|
|
36
|
-
if not pm_url.startswith("http"):
|
|
37
|
-
pm_url = "https://" + pm_url
|
|
38
|
-
|
|
39
|
-
node = RudiNodeWriter(pm_url=pm_url, auth=auth, keep_connection=True)
|
|
40
|
-
|
|
41
|
-
node_data = {"node_name": node_name, "pm_url": node.pm_url}
|
|
42
|
-
node_data["media_url"] = try_request(node.media_url)
|
|
43
|
-
node_data["init_data"] = try_request(node.init_data)
|
|
44
|
-
node_data["organization_list"] = try_request(node.organization_list)
|
|
45
|
-
node_data["contact_list"] = try_request(node.contact_list)
|
|
46
|
-
node_data["metadata_list"] = try_request(node.metadata_list)
|
|
47
|
-
node_data["enums"] = try_request(node.enums)
|
|
48
|
-
node_data["media_list"] = try_request(node.media_list)
|
|
49
|
-
|
|
50
|
-
node.close_connection()
|
|
51
|
-
return node_data
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if __name__ == "__main__": # pragma: no cover
|
|
55
|
-
begin = time()
|
|
56
|
-
here = "RudiNodeFederation backup"
|
|
57
|
-
|
|
58
|
-
test_dir = check_is_dir("../dwnld")
|
|
59
|
-
data_dir = check_is_dir("../data")
|
|
60
|
-
creds_file = check_is_file("../creds/test_creds_manager.json")
|
|
61
|
-
servers_file = check_is_file("../creds/servers.json")
|
|
62
|
-
|
|
63
|
-
servers_list = read_json_file(servers_file)
|
|
64
|
-
nodes = servers_list.get("nodes")
|
|
65
|
-
|
|
66
|
-
# nodes = {"exatow": nodes["exatow"]}
|
|
67
|
-
|
|
68
|
-
nb_nodes = len(nodes.keys())
|
|
69
|
-
|
|
70
|
-
rudi_node_creds = read_json_file(creds_file)
|
|
71
|
-
auth = RudiNodeAuth.from_json(rudi_node_creds)
|
|
72
|
-
url = rudi_node_creds["url"]
|
|
73
|
-
pm_url = url if url.endswith("prodmanager") else slash_join(url, "prodmanager")
|
|
74
|
-
assert isinstance(auth, RudiNodeAuth)
|
|
75
|
-
errors = {}
|
|
76
|
-
if MAX_WORKERS:
|
|
77
|
-
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
|
|
78
|
-
futures = {executor.submit(backup_node, node_name, nodes[node_name]): node_name for node_name in nodes}
|
|
79
|
-
for future in as_completed(futures, timeout=120):
|
|
80
|
-
try:
|
|
81
|
-
future_result = future.result()
|
|
82
|
-
except Exception as err:
|
|
83
|
-
node_name = futures[future]
|
|
84
|
-
log_e(here, "Backup failed for node:", futures[future])
|
|
85
|
-
future_result = {}
|
|
86
|
-
errors[node_name] = err
|
|
87
|
-
node_name = futures[future]
|
|
88
|
-
future_result["node_name"] = None
|
|
89
|
-
federation_data[node_name] = future_result
|
|
90
|
-
else:
|
|
91
|
-
# federation_info = {nodes[node_name] for node_name in nodes}
|
|
92
|
-
|
|
93
|
-
for node_name in nodes:
|
|
94
|
-
log_d(here, "----- node_name", node_name)
|
|
95
|
-
node_info = nodes[node_name]
|
|
96
|
-
try:
|
|
97
|
-
node_data = backup_node(node_name, node_info)
|
|
98
|
-
federation_data[node_name] = node_data
|
|
99
|
-
|
|
100
|
-
write_json_file(destination_file_path=slash_join(data_dir, node_name + ".json"), json_dict=node_data)
|
|
101
|
-
except Exception as e:
|
|
102
|
-
errors[node_name] = e
|
|
103
|
-
node_data = None
|
|
104
|
-
|
|
105
|
-
federation_data[node_name] = node_data
|
|
106
|
-
|
|
107
|
-
write_json_file(destination_file_path=slash_join(data_dir, "_federation.json"), json_dict=federation_data)
|
|
108
|
-
if len(errors.keys()) > 0:
|
|
109
|
-
log_e(here, "Backup failed for some nodes:", errors)
|
|
110
|
-
else:
|
|
111
|
-
log_d(here, "All nodes were successfully backed up")
|
|
112
|
-
|
|
113
|
-
log_d(here, "exec. time", time() - begin)
|
|
114
|
-
|
|
115
|
-
# urls = [
|
|
116
|
-
# # "http://www.foxnews.com/",
|
|
117
|
-
# "http://www.cnn.com/",
|
|
118
|
-
# "http://europe.wsj.com/",
|
|
119
|
-
# "http://www.bbc.co.uk/",
|
|
120
|
-
# "http://some-made-up-domain.com/",
|
|
121
|
-
# ]
|
|
122
|
-
|
|
123
|
-
# url_dict = {url + "r": url for url in urls}
|
|
124
|
-
# print(url_dict)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
# def load_url(url, timeout):
|
|
128
|
-
# with urlopen(url, timeout=timeout) as conn:
|
|
129
|
-
# return conn.read()
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
# with ThreadPoolExecutor(max_workers=5) as executor:
|
|
133
|
-
# future_to_url = {executor.submit(load_url, url, 60): url for url in urls}
|
|
134
|
-
# for future in as_completed(future_to_url):
|
|
135
|
-
# url = future_to_url[future]
|
|
136
|
-
# try:
|
|
137
|
-
# data = future.result()
|
|
138
|
-
# except Exception as exc:
|
|
139
|
-
# print("%r generated an exception: %s" % (url, exc))
|
|
140
|
-
# else:
|
|
141
|
-
# print("%r page is %d bytes" % (url, len(data)))
|
|
142
|
-
|
|
143
|
-
# print(cpu_count() * 5)
|
|
144
|
-
# pool = ThreadPoolExecutor()
|
|
145
|
-
# print(pool._max_workers)
|
|
@@ -1,412 +0,0 @@
|
|
|
1
|
-
from abc import ABC
|
|
2
|
-
from concurrent.futures import as_completed, ThreadPoolExecutor
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from os import cpu_count
|
|
5
|
-
from time import sleep, time
|
|
6
|
-
from typing import Callable
|
|
7
|
-
from urllib.request import urlopen
|
|
8
|
-
|
|
9
|
-
from rudi_node_write.connectors.io_connector import HTTP_REQUEST_METHODS
|
|
10
|
-
from rudi_node_write.connectors.io_rudi_api_write import RudiNodeApiConnector
|
|
11
|
-
from rudi_node_write.connectors.io_rudi_jwt_factory import RudiNodeJwtFactory
|
|
12
|
-
from rudi_node_write.connectors.io_rudi_manager_write import RudiNodeManagerConnector
|
|
13
|
-
from rudi_node_write.connectors.rudi_node_auth import RudiNodeAuth
|
|
14
|
-
from rudi_node_write.rudi_node_writer import RudiNodeWriter
|
|
15
|
-
from rudi_node_write.utils.file_utils import check_is_dir, check_is_file, read_json_file, write_json_file
|
|
16
|
-
from rudi_node_write.utils.log import log_d, log_e, log_w
|
|
17
|
-
from rudi_node_write.utils.str_utils import slash_join
|
|
18
|
-
from rudi_node_write.utils.type_date import Date
|
|
19
|
-
from rudi_node_write.utils.url_utils import ensure_http, ensure_url_startswith
|
|
20
|
-
|
|
21
|
-
federation_data = {}
|
|
22
|
-
|
|
23
|
-
DEFAULT_WAIT_TIME = 0.3
|
|
24
|
-
|
|
25
|
-
NO_PORTAL = "No portal connected"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def ensure_url_startswith_api(url):
|
|
29
|
-
return ensure_url_startswith(url, "api")
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class Module:
|
|
33
|
-
MANAGER = "manager"
|
|
34
|
-
CATALOG = "catalog"
|
|
35
|
-
STORAGE = "storage"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class HttpMethods:
|
|
39
|
-
GET = "GET"
|
|
40
|
-
POST = "POST"
|
|
41
|
-
PUT = "PUT"
|
|
42
|
-
DELETE = "DELETE"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@dataclass
|
|
46
|
-
class ModuleRequest:
|
|
47
|
-
action_name: str
|
|
48
|
-
module_name: Module = Module.MANAGER
|
|
49
|
-
req_method: HttpMethods = HttpMethods.GET
|
|
50
|
-
url_bit: str = "/"
|
|
51
|
-
post_treatment: Callable = lambda x: x
|
|
52
|
-
can_fail: bool = False
|
|
53
|
-
keep_alive: bool = False
|
|
54
|
-
should_log_request: bool = False
|
|
55
|
-
should_log_response: bool = False
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# TODO: envoyer une suite/liste de traitements sur les nœuds:
|
|
59
|
-
# - quel connecteur cible (node_write ? catalog ? media ?)
|
|
60
|
-
# - quelle URL requête (url_bit )
|
|
61
|
-
# - quel post-traitement
|
|
62
|
-
#
|
|
63
|
-
# => Input: objet à définir
|
|
64
|
-
# #
|
|
65
|
-
# [{
|
|
66
|
-
# target: Module.MANAGER | Module.CATALOG | MEDIA,
|
|
67
|
-
# url_bit,
|
|
68
|
-
# post_treatment on answer (defaulted to x=>x)
|
|
69
|
-
# opts: keep_alive, log request, log response (defaulted to False)
|
|
70
|
-
# }]
|
|
71
|
-
class NodeConnector:
|
|
72
|
-
def __init__(
|
|
73
|
-
self,
|
|
74
|
-
node_name,
|
|
75
|
-
catalog_url,
|
|
76
|
-
catalog_auth,
|
|
77
|
-
manager_url,
|
|
78
|
-
manager_auth,
|
|
79
|
-
):
|
|
80
|
-
self._node_name = node_name
|
|
81
|
-
self._catalog = CatalogConnector(node_name=node_name, module_url=catalog_url, module_auth=catalog_auth)
|
|
82
|
-
self._manager = ManagerConnector(node_name=node_name, module_url=manager_url, module_auth=manager_auth)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
class ModuleConnector(ABC):
|
|
86
|
-
def __init__(self, node_name: str, get_connector: Callable, get_headers: Callable):
|
|
87
|
-
self._node_name = node_name
|
|
88
|
-
self._get_connector = get_connector
|
|
89
|
-
self._get_headers = get_headers
|
|
90
|
-
|
|
91
|
-
@property
|
|
92
|
-
def node_name(self):
|
|
93
|
-
return self._node_name
|
|
94
|
-
|
|
95
|
-
@property
|
|
96
|
-
def module_name(self):
|
|
97
|
-
raise NotImplementedError("This function should be implemented")
|
|
98
|
-
|
|
99
|
-
@property
|
|
100
|
-
def connector(self):
|
|
101
|
-
return self._get_connector()
|
|
102
|
-
|
|
103
|
-
@property
|
|
104
|
-
def headers(self):
|
|
105
|
-
return self._get_headers()
|
|
106
|
-
|
|
107
|
-
@staticmethod
|
|
108
|
-
def create_connector(node_name, module_name, module_url, module_auth):
|
|
109
|
-
if module_name == Module.MANAGER:
|
|
110
|
-
return ManagerConnector(node_name=node_name, module_url=module_url, module_auth=module_auth)
|
|
111
|
-
if module_name == Module.CATALOG:
|
|
112
|
-
return CatalogConnector(node_name=node_name, module_url=module_url, module_auth=module_auth)
|
|
113
|
-
|
|
114
|
-
def module_request(
|
|
115
|
-
self,
|
|
116
|
-
request: ModuleRequest,
|
|
117
|
-
):
|
|
118
|
-
return self.request(
|
|
119
|
-
req_method=request.req_method,
|
|
120
|
-
url_bit=request.url_bit,
|
|
121
|
-
post_treatment=request.post_treatment,
|
|
122
|
-
can_fail=request.can_fail,
|
|
123
|
-
keep_alive=request.keep_alive,
|
|
124
|
-
should_log_request=request.should_log_request,
|
|
125
|
-
should_log_response=request.should_log_response,
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
def request(
|
|
129
|
-
self,
|
|
130
|
-
req_method: HttpMethods,
|
|
131
|
-
url_bit: str,
|
|
132
|
-
post_treatment=lambda x: x,
|
|
133
|
-
can_fail: bool = False,
|
|
134
|
-
keep_alive: bool = False,
|
|
135
|
-
should_log_request: bool = False,
|
|
136
|
-
should_log_response: bool = False,
|
|
137
|
-
):
|
|
138
|
-
try:
|
|
139
|
-
node_res = self.connector.request(
|
|
140
|
-
req_method=req_method,
|
|
141
|
-
relative_url=ensure_url_startswith_api(url_bit),
|
|
142
|
-
headers=self.headers,
|
|
143
|
-
keep_alive=keep_alive,
|
|
144
|
-
should_log_request=should_log_request,
|
|
145
|
-
should_log_response=should_log_response,
|
|
146
|
-
)
|
|
147
|
-
return post_treatment(node_res)
|
|
148
|
-
except Exception as err:
|
|
149
|
-
if can_fail:
|
|
150
|
-
raise err
|
|
151
|
-
return f"ERR: {err}"
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
class ManagerConnector(ModuleConnector):
|
|
155
|
-
def __init__(self, node_name, module_url, module_auth):
|
|
156
|
-
self._node_name = node_name
|
|
157
|
-
self._node_writer = RudiNodeWriter(pm_url=module_url, auth=module_auth, headers_user_agent="ManagerConnector")
|
|
158
|
-
super().__init__(
|
|
159
|
-
node_name=node_name,
|
|
160
|
-
get_connector=lambda: self._node_writer._pm_connector,
|
|
161
|
-
get_headers=lambda: self._node_writer._pm_connector._pm_headers,
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
class CatalogConnector(ModuleConnector):
|
|
166
|
-
def __init__(self, node_name, module_url, module_auth):
|
|
167
|
-
self._node_name = node_name
|
|
168
|
-
self._catalog_connector = RudiNodeApiConnector(
|
|
169
|
-
server_url=module_url, jwt_factory=module_auth, headers_user_agent="CatalogConnector"
|
|
170
|
-
)
|
|
171
|
-
super().__init__(
|
|
172
|
-
node_name=node_name,
|
|
173
|
-
get_connector=lambda: self._catalog_connector,
|
|
174
|
-
get_headers=lambda: self._catalog_connector._headers,
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
class RudiNodeFederation:
|
|
179
|
-
def __init__(self, config_file_path, credentials_file_path, local_jwt_factory):
|
|
180
|
-
self.jwt_factory = local_jwt_factory
|
|
181
|
-
self.load_federation_conf(config_file_path, credentials_file_path)
|
|
182
|
-
|
|
183
|
-
def load_federation_conf(self, config_file_path, credentials_file_path):
|
|
184
|
-
self._conf = read_json_file(config_file_path)
|
|
185
|
-
self._creds = read_json_file(credentials_file_path)
|
|
186
|
-
self._connectors = {}
|
|
187
|
-
|
|
188
|
-
# self._conf["nodes"] = {"exatow": self._conf["nodes"]["exatow"], "rm": self._conf["nodes"]["rm"]}
|
|
189
|
-
|
|
190
|
-
for node_name, node_conf in self.nodes_conf.items():
|
|
191
|
-
print(node_name)
|
|
192
|
-
node_url = ensure_http(node_conf["url"])
|
|
193
|
-
|
|
194
|
-
pm_url = ensure_http(
|
|
195
|
-
node_conf["pmback"] if node_conf.get("pmback") is not None else slash_join(node_url, "prodmanager")
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
self._connectors[node_name] = {
|
|
199
|
-
Module.MANAGER: ManagerConnector(
|
|
200
|
-
node_name=node_name,
|
|
201
|
-
module_url=pm_url,
|
|
202
|
-
module_auth=RudiNodeAuth(b64url_auth=self.get_creds(node_name).get("b64auth")),
|
|
203
|
-
),
|
|
204
|
-
Module.CATALOG: CatalogConnector(
|
|
205
|
-
node_name=node_name, module_url=node_url, module_auth=self.jwt_factory
|
|
206
|
-
),
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
@property
|
|
210
|
-
def nodes_conf(self):
|
|
211
|
-
return self.get_node_conf()
|
|
212
|
-
|
|
213
|
-
def get_node_conf(self, node_name=None):
|
|
214
|
-
if node_name is None:
|
|
215
|
-
return self._conf["nodes"]
|
|
216
|
-
return self._conf["nodes"].get(node_name)
|
|
217
|
-
|
|
218
|
-
def get_creds(self, node_name):
|
|
219
|
-
if self._creds.get(node_name):
|
|
220
|
-
return self._creds[node_name]
|
|
221
|
-
return self._creds["defaults"]
|
|
222
|
-
|
|
223
|
-
def request(
|
|
224
|
-
self,
|
|
225
|
-
node_name,
|
|
226
|
-
module_name,
|
|
227
|
-
req_method,
|
|
228
|
-
url_bit,
|
|
229
|
-
post_treatment=lambda x: x,
|
|
230
|
-
can_fail=False,
|
|
231
|
-
keep_alive=False,
|
|
232
|
-
should_log_request=False,
|
|
233
|
-
should_log_response=False,
|
|
234
|
-
):
|
|
235
|
-
if module_name == Module.MANAGER:
|
|
236
|
-
connector = self._managers[node_name]
|
|
237
|
-
headers = connector._pm_headers
|
|
238
|
-
url_bit = ensure_url_startswith_api(url_bit)
|
|
239
|
-
elif module_name == Module.CATALOG:
|
|
240
|
-
connector = self._catalogs[node_name]
|
|
241
|
-
headers = connector._headers
|
|
242
|
-
url_bit = ensure_url_startswith_api(url_bit)
|
|
243
|
-
else:
|
|
244
|
-
raise NotImplementedError("Not yet available")
|
|
245
|
-
try:
|
|
246
|
-
node_res = connector.request(
|
|
247
|
-
req_method=req_method,
|
|
248
|
-
relative_url=url_bit,
|
|
249
|
-
headers=headers,
|
|
250
|
-
keep_alive=keep_alive,
|
|
251
|
-
should_log_request=should_log_request,
|
|
252
|
-
should_log_response=should_log_response,
|
|
253
|
-
)
|
|
254
|
-
return post_treatment(node_res)
|
|
255
|
-
except Exception as err:
|
|
256
|
-
if can_fail:
|
|
257
|
-
raise err
|
|
258
|
-
return f"ERR: {err}"
|
|
259
|
-
|
|
260
|
-
def batch_request_node(self, connectors, request_list, max_workers=0):
|
|
261
|
-
res = {}
|
|
262
|
-
err = {}
|
|
263
|
-
for request in request_list:
|
|
264
|
-
if isinstance(request, ModuleRequest):
|
|
265
|
-
res[request.action_name] = connectors[request.module_name].module_request(request)
|
|
266
|
-
else:
|
|
267
|
-
(action_name, module_name, req_method, url_bit, post_treatment) = request
|
|
268
|
-
res[action_name] = connectors[module_name].request(
|
|
269
|
-
req_method=req_method,
|
|
270
|
-
url_bit=url_bit,
|
|
271
|
-
post_treatment=post_treatment,
|
|
272
|
-
)
|
|
273
|
-
return res
|
|
274
|
-
|
|
275
|
-
def batch_requests(self, request_list, max_workers=0):
|
|
276
|
-
"""
|
|
277
|
-
@param request_dict: a dict with module names as keys and a list of actions {action_name: (module_name, req_method , url_bit , post_treatment)}}
|
|
278
|
-
"""
|
|
279
|
-
res = {}
|
|
280
|
-
err = {}
|
|
281
|
-
if not max_workers:
|
|
282
|
-
for node_name, connectors in self._connectors.items():
|
|
283
|
-
res[node_name] = self.batch_request_node(connectors, request_list)
|
|
284
|
-
else:
|
|
285
|
-
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
286
|
-
futures = {
|
|
287
|
-
executor.submit(connectors[request.module_name].module_request, request): request
|
|
288
|
-
for request in request_list
|
|
289
|
-
}
|
|
290
|
-
for future in as_completed(futures, timeout=120):
|
|
291
|
-
try:
|
|
292
|
-
future_result = future.result()
|
|
293
|
-
except Exception as err:
|
|
294
|
-
node_name = futures[future]
|
|
295
|
-
log_e(here, "Request failed:", futures[future])
|
|
296
|
-
future_result = {}
|
|
297
|
-
errors[request.node_name] = err
|
|
298
|
-
node_name = futures[future]
|
|
299
|
-
future_result["node_name"] = None
|
|
300
|
-
federation_data[node_name] = future_result
|
|
301
|
-
return res
|
|
302
|
-
|
|
303
|
-
def try_request(req, wait_time=DEFAULT_WAIT_TIME, quit_on_fail=True):
|
|
304
|
-
fun = "try_request"
|
|
305
|
-
sleep(wait_time)
|
|
306
|
-
try:
|
|
307
|
-
return req
|
|
308
|
-
except Exception as err:
|
|
309
|
-
if quit_on_fail:
|
|
310
|
-
return "FAIL"
|
|
311
|
-
# Let's try one more time
|
|
312
|
-
return try_request(req, wait_time, True)
|
|
313
|
-
|
|
314
|
-
def backup_node(node_name, node_info):
|
|
315
|
-
node_url = node_info["url"]
|
|
316
|
-
pm_url = node_info["pmback"] if node_info.get("pmback") is not None else (slash_join(node_url, "prodmanager"))
|
|
317
|
-
ensure_http(pm_url)
|
|
318
|
-
|
|
319
|
-
node = RudiNodeWriter(pm_url=pm_url, auth=auth, keep_connection=True)
|
|
320
|
-
|
|
321
|
-
node_data = {"node_name": node_name, "pm_url": node.pm_url}
|
|
322
|
-
node_data["media_url"] = try_request(node.media_url)
|
|
323
|
-
node_data["init_data"] = try_request(node.init_data)
|
|
324
|
-
node_data["organization_list"] = try_request(node.organization_list)
|
|
325
|
-
node_data["contact_list"] = try_request(node.contact_list)
|
|
326
|
-
node_data["metadata_list"] = try_request(node.metadata_list)
|
|
327
|
-
node_data["enums"] = try_request(node.enums)
|
|
328
|
-
node_data["media_list"] = try_request(node.media_list)
|
|
329
|
-
|
|
330
|
-
node.close_connection()
|
|
331
|
-
return node_data
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if __name__ == "__main__": # pragma: no cover
|
|
335
|
-
begin = time()
|
|
336
|
-
here = "RudiNodeFederation"
|
|
337
|
-
|
|
338
|
-
test_dir = check_is_dir("../dwnld")
|
|
339
|
-
data_dir = check_is_dir("../data")
|
|
340
|
-
creds_file = check_is_file("../creds/federation_creds.json")
|
|
341
|
-
servers_file = check_is_file("../creds/servers.json")
|
|
342
|
-
|
|
343
|
-
try:
|
|
344
|
-
local_jwt_factory = RudiNodeJwtFactory("http://localhost:4040", {"sub": "rudi_api_pm"})
|
|
345
|
-
except:
|
|
346
|
-
raise ConnectionError("Local crypto module is apparently not launched!")
|
|
347
|
-
|
|
348
|
-
rudi_federation = RudiNodeFederation(
|
|
349
|
-
config_file_path=servers_file, credentials_file_path=creds_file, local_jwt_factory=local_jwt_factory
|
|
350
|
-
)
|
|
351
|
-
|
|
352
|
-
def get_token(x):
|
|
353
|
-
if x == NO_PORTAL:
|
|
354
|
-
return NO_PORTAL
|
|
355
|
-
try:
|
|
356
|
-
return x["access_token"].replace(r"[\w-]+(\.[\w-]+){2}", "valid_jwt")
|
|
357
|
-
except:
|
|
358
|
-
return x
|
|
359
|
-
|
|
360
|
-
res = rudi_federation.batch_requests(
|
|
361
|
-
[
|
|
362
|
-
ModuleRequest(
|
|
363
|
-
action_name="portal_url",
|
|
364
|
-
module_name=Module.MANAGER,
|
|
365
|
-
url_bit="front/portal-url",
|
|
366
|
-
can_fail=True,
|
|
367
|
-
should_log_request=True,
|
|
368
|
-
),
|
|
369
|
-
ModuleRequest(
|
|
370
|
-
action_name="portal_token",
|
|
371
|
-
module_name=Module.CATALOG,
|
|
372
|
-
url_bit="admin/portal/token",
|
|
373
|
-
post_treatment=get_token,
|
|
374
|
-
),
|
|
375
|
-
]
|
|
376
|
-
)
|
|
377
|
-
portal_connections_file = "rudi_node_write/wip/portal_connect.json"
|
|
378
|
-
reworked_portal_connect_file = "rudi_node_write/wip/portal_connect_final.json"
|
|
379
|
-
# res = read_json_file("rudi_node_write/wip/portal_connect.json")
|
|
380
|
-
print(res)
|
|
381
|
-
write_json_file(destination_file_path=portal_connections_file, json_dict=res)
|
|
382
|
-
final_res = {}
|
|
383
|
-
for node_name, node_res in res.items():
|
|
384
|
-
portal_url = node_res["portal_url"]
|
|
385
|
-
print(portal_url)
|
|
386
|
-
portal_connection = (
|
|
387
|
-
node_res["portal_token"]
|
|
388
|
-
if not node_res["portal_token"].startswith("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9")
|
|
389
|
-
else "OK"
|
|
390
|
-
)
|
|
391
|
-
if final_res.get(portal_url) is None:
|
|
392
|
-
final_res[portal_url] = {node_name: portal_connection}
|
|
393
|
-
final_res[portal_url][node_name] = portal_connection
|
|
394
|
-
# print(res)
|
|
395
|
-
# portal_urls = rudi_federation.manager_get("front/portal-url")
|
|
396
|
-
# node_catalog_url = rudi_federation.manager_get("front/ext-api-url")
|
|
397
|
-
# catalog_portal_token = rudi_federation.catalog_get("portal/token", extract_token_from_http_res)
|
|
398
|
-
# node_portals = {}
|
|
399
|
-
# for node_name, portal_url in portal_urls.items():
|
|
400
|
-
# node_info = {"catalog_url": node_catalog_url[node_name], "portal_token": catalog_portal_token}
|
|
401
|
-
# if node_portals.get(portal_url) is None:
|
|
402
|
-
# node_portals[portal_url] = {node_name: node_info}
|
|
403
|
-
# else:
|
|
404
|
-
# node_portals[portal_url][node_name] = node_info
|
|
405
|
-
|
|
406
|
-
# print(node_portals)
|
|
407
|
-
print(final_res)
|
|
408
|
-
write_json_file(destination_file_path=reworked_portal_connect_file, json_dict=final_res)
|
|
409
|
-
|
|
410
|
-
print()
|
|
411
|
-
|
|
412
|
-
# print(chr(sum(range(ord(min(str(not ())))))))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/connectors/io_connector.py
RENAMED
|
File without changes
|
{rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/connectors/io_rudi_api_write.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/connectors/rudi_node_auth.py
RENAMED
|
File without changes
|
|
File without changes
|
{rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_const.py
RENAMED
|
File without changes
|
{rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_contact.py
RENAMED
|
File without changes
|
{rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_dates.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_licence.py
RENAMED
|
File without changes
|
|
File without changes
|
{rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/rudi_meta_misc.py
RENAMED
|
File without changes
|
|
File without changes
|
{rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write/rudi_types/serializable.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rudi_node_write-1.2.0 → rudi_node_write-1.2.2}/src/rudi_node_write.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|