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.
Files changed (46) hide show
  1. {rudi_node_write-1.1.0/src/rudi_node_write.egg-info → rudi_node_write-1.2.0}/PKG-INFO +2 -7
  2. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/README.md +1 -1
  3. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/pyproject.toml +3 -3
  4. rudi_node_write-1.2.0/src/rudi_node_write/__init__.py +0 -0
  5. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/io_connector.py +5 -4
  6. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/io_rudi_api_write.py +20 -13
  7. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/io_rudi_jwt_factory.py +7 -3
  8. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/io_rudi_manager_write.py +148 -64
  9. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/io_rudi_media_write.py +14 -8
  10. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/connectors/rudi_node_auth.py +3 -2
  11. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_node_writer.py +45 -13
  12. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/serializable.py +3 -3
  13. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/err.py +14 -11
  14. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/file_utils.py +1 -0
  15. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/str_utils.py +9 -0
  16. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/url_utils.py +9 -0
  17. rudi_node_write-1.2.0/src/rudi_node_write/wip/federation_backup.py +145 -0
  18. rudi_node_write-1.2.0/src/rudi_node_write/wip/rudinode_federation.py +412 -0
  19. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0/src/rudi_node_write.egg-info}/PKG-INFO +2 -7
  20. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write.egg-info/SOURCES.txt +2 -1
  21. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/tests/test_rudi_node_write.py +3 -2
  22. rudi_node_write-1.1.0/requirements.txt +0 -7
  23. rudi_node_write-1.1.0/src/rudi_node_write.egg-info/requires.txt +0 -5
  24. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/LICENCE.md +0 -0
  25. /rudi_node_write-1.1.0/src/rudi_node_write/__init__.py → /rudi_node_write-1.2.0/requirements.txt +0 -0
  26. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/setup.cfg +0 -0
  27. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/conf/meta_defaults.py +0 -0
  28. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_const.py +0 -0
  29. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_contact.py +0 -0
  30. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_dates.py +0 -0
  31. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_dictionary_entry.py +0 -0
  32. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_geo.py +0 -0
  33. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_licence.py +0 -0
  34. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_media.py +0 -0
  35. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_meta.py +0 -0
  36. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_meta_misc.py +0 -0
  37. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/rudi_types/rudi_org.py +0 -0
  38. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/dict_utils.py +0 -0
  39. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/html_utils.py +0 -0
  40. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/jwt.py +0 -0
  41. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/list_utils.py +0 -0
  42. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/log.py +0 -0
  43. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/type_date.py +0 -0
  44. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write/utils/typing_utils.py +0 -0
  45. {rudi_node_write-1.1.0 → rudi_node_write-1.2.0}/src/rudi_node_write.egg-info/dependency_links.txt +0 -0
  46. {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.1.0
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
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
27
22
  [![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](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/release/tests) can be analyzed for further
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/release/tests) can be analyzed for further
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.1.0"
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.1.0"
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
@@ -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:
@@ -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(self, url: str, keep_alive: bool = False) -> str | int | list | dict:
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=slash_join(_API_ADMIN_PATH, 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=slash_join(_API_ADMIN_PATH, 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=slash_join(_API_ADMIN_PATH, 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=slash_join(_API_ADMIN_PATH, 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 = "../../../creds/creds.json"
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 = "../../../dwnld"
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("c4c8e77b-d0a2-4875-ab88-9ef77e829926", test_dir),
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("782bab2d-7ee8-4633-9c0a-173649b4d879", test_dir),
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": "rudi_prod_token",
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 = "../../../creds/creds.json"
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)