rudi-node-write 0.1.0__tar.gz → 1.0.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-0.1.0/src/rudi_node_write.egg-info → rudi_node_write-1.0.0}/PKG-INFO +18 -19
- rudi_node_write-1.0.0/README.md +30 -0
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/pyproject.toml +28 -7
- rudi_node_write-1.0.0/requirements.txt +7 -0
- rudi_node_write-1.0.0/src/rudi_node_write/__init__.py +0 -0
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/conf/meta_defaults.py +1 -1
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/connectors/io_connector.py +81 -46
- rudi_node_write-1.0.0/src/rudi_node_write/connectors/io_rudi_manager_write.py +1308 -0
- rudi_node_write-1.0.0/src/rudi_node_write/rudi_node_writer.py +446 -0
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/rudi_types/rudi_const.py +85 -22
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/rudi_types/rudi_contact.py +7 -6
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/rudi_types/rudi_dates.py +12 -15
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/rudi_types/rudi_dictionary_entry.py +26 -19
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/rudi_types/rudi_geo.py +9 -9
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/rudi_types/rudi_licence.py +22 -34
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/rudi_types/rudi_media.py +139 -93
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/rudi_types/rudi_meta.py +44 -47
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/rudi_types/rudi_meta_misc.py +17 -15
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/rudi_types/rudi_org.py +5 -5
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/rudi_types/serializable.py +35 -26
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/utils/dict_utils.py +73 -10
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/utils/err.py +14 -5
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/utils/file_utils.py +41 -12
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/utils/html_utils.py +4 -5
- rudi_node_write-1.0.0/src/rudi_node_write/utils/jwt.py +77 -0
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/utils/list_utils.py +11 -7
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/utils/log.py +10 -6
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/utils/str_utils.py +16 -13
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/utils/type_date.py +63 -11
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/utils/typing_utils.py +45 -16
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/utils/url_utils.py +14 -1
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0/src/rudi_node_write.egg-info}/PKG-INFO +18 -19
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write.egg-info/SOURCES.txt +7 -5
- rudi_node_write-1.0.0/src/rudi_node_write.egg-info/requires.txt +5 -0
- rudi_node_write-1.0.0/tests/test_rudi_node_write.py +66 -0
- rudi-node-write-0.1.0/README.md +0 -35
- rudi-node-write-0.1.0/src/rudi_node_write/connectors/io_rudi_api_write.py +0 -799
- rudi-node-write-0.1.0/src/rudi_node_write/connectors/io_rudi_jwt_factory.py +0 -75
- rudi-node-write-0.1.0/src/rudi_node_write/connectors/io_rudi_media_write.py +0 -221
- rudi-node-write-0.1.0/src/rudi_node_write/utils/date_utils.py +0 -43
- rudi-node-write-0.1.0/src/rudi_node_write/utils/jwt.py +0 -41
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/LICENCE.md +0 -0
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/setup.cfg +0 -0
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write.egg-info/dependency_links.txt +0 -0
- {rudi-node-write-0.1.0 → rudi_node_write-1.0.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:
|
|
3
|
+
Version: 1.0.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>
|
|
@@ -9,49 +9,48 @@ Project-URL: Homepage, https://github.com/OlivierMartineau/rudi-node-write
|
|
|
9
9
|
Project-URL: Documentation, https://app.swaggerhub.com/apis/OlivierMartineau/RudiProducer-InternalAPI
|
|
10
10
|
Project-URL: Changelog, https://github.com/OlivierMartineau/rudi-node-write/blob/release/CHANGELOG.md
|
|
11
11
|
Project-URL: Repository, https://github.com/OlivierMartineau/rudi-node-write
|
|
12
|
-
Keywords: rudi-node-write,rudi-node-
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Keywords: rudi-node-write,rudi-node-put,RUDI,producer node,RUDI node,open-data,Univ. Rennes
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
15
|
Classifier: Natural Language :: English
|
|
17
16
|
Classifier: Operating System :: OS Independent
|
|
18
17
|
Classifier: License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1.2)
|
|
19
|
-
Requires-Python: >=3.
|
|
18
|
+
Requires-Python: >=3.11
|
|
20
19
|
Description-Content-Type: text/markdown
|
|
21
20
|
License-File: LICENCE.md
|
|
21
|
+
Requires-Dist: beautifulsoup4==4.12.3
|
|
22
|
+
Requires-Dist: chardet==5.2.0
|
|
23
|
+
Requires-Dist: deepdiff==7.0.1
|
|
24
|
+
Requires-Dist: defusedxml==0.7.1
|
|
25
|
+
Requires-Dist: puremagic==1.24
|
|
22
26
|
|
|
23
27
|
[](https://github.com/psf/black)
|
|
24
|
-
|
|
25
|
-
Caution: this librairy is still a work in progress.
|
|
28
|
+
[](https://microsoft.github.io/pyright/)
|
|
26
29
|
|
|
27
30
|
# RUDI Node tools: _rudi-node-write_ library
|
|
28
31
|
|
|
29
32
|
This library offers tools to take advantage of
|
|
30
33
|
the [internal API](https://app.swaggerhub.com/apis/OlivierMartineau/RudiProducer-InternalAPI) of a RUDI Producer node (
|
|
31
34
|
also
|
|
32
|
-
referred as RUDI node).
|
|
35
|
+
referred as RUDI node), through the API of the backend of the user interface, the "Producer node manager" or "Prodmanager" module.
|
|
33
36
|
|
|
34
37
|
## Installation
|
|
35
38
|
|
|
36
39
|
```bash
|
|
37
|
-
|
|
40
|
+
python3 -m venv .venv
|
|
41
|
+
source .venv/bin/activate
|
|
42
|
+
pip install rudi_node_write
|
|
38
43
|
```
|
|
39
44
|
|
|
40
|
-
## Usage
|
|
41
|
-
|
|
42
|
-
```python
|
|
43
|
-
from rudi_node_write.connectors.io_rudi_api_write import RudiNodeApiConnector
|
|
45
|
+
## Usage: RudiNodeWriter
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
print(rudi_api.metadata_count)
|
|
47
|
-
print(len(rudi_api.metadata_list))
|
|
48
|
-
print(rudi_api.producer_names)
|
|
49
|
-
print(rudi_api.find_metadata_with_media_name('toucan.jpg'))
|
|
50
|
-
|
|
51
|
-
```
|
|
47
|
+
The Jupyter notebook [`README.ipynb`](https://github.com/OlivierMartineau/rudi-node-write/blob/release/README.ipynb) details how to use the library through the [`RudiNodeWriter`](https://github.com/OlivierMartineau/rudi-node-write/blob/release/src/rudi_node_write/rudi_node_writer.py) object.
|
|
52
48
|
|
|
53
49
|
## Testing
|
|
54
50
|
|
|
51
|
+
The [tests](https://github.com/OlivierMartineau/rudi-node-write/tree/release/tests) can be analyzed for further
|
|
52
|
+
information about how to call the API
|
|
53
|
+
|
|
55
54
|
```bash
|
|
56
55
|
$ pytest
|
|
57
56
|
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[](https://github.com/psf/black)
|
|
2
|
+
[](https://microsoft.github.io/pyright/)
|
|
3
|
+
|
|
4
|
+
# RUDI Node tools: _rudi-node-write_ library
|
|
5
|
+
|
|
6
|
+
This library offers tools to take advantage of
|
|
7
|
+
the [internal API](https://app.swaggerhub.com/apis/OlivierMartineau/RudiProducer-InternalAPI) of a RUDI Producer node (
|
|
8
|
+
also
|
|
9
|
+
referred as RUDI node), through the API of the backend of the user interface, the "Producer node manager" or "Prodmanager" module.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
python3 -m venv .venv
|
|
15
|
+
source .venv/bin/activate
|
|
16
|
+
pip install rudi_node_write
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage: RudiNodeWriter
|
|
20
|
+
|
|
21
|
+
The Jupyter notebook [`README.ipynb`](https://github.com/OlivierMartineau/rudi-node-write/blob/release/README.ipynb) details how to use the library through the [`RudiNodeWriter`](https://github.com/OlivierMartineau/rudi-node-write/blob/release/src/rudi_node_write/rudi_node_writer.py) object.
|
|
22
|
+
|
|
23
|
+
## Testing
|
|
24
|
+
|
|
25
|
+
The [tests](https://github.com/OlivierMartineau/rudi-node-write/tree/release/tests) can be analyzed for further
|
|
26
|
+
information about how to call the API
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
$ pytest
|
|
30
|
+
```
|
|
@@ -4,21 +4,22 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rudi-node-write"
|
|
7
|
-
version = "
|
|
7
|
+
version = "1.0.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"
|
|
11
11
|
readme = "README.md"
|
|
12
|
-
requires-python = ">=3.
|
|
12
|
+
requires-python = ">=3.11"
|
|
13
13
|
license = { text = "EUPL-1.2" }
|
|
14
|
-
classifiers = ["Programming Language :: Python :: 3.
|
|
15
|
-
"Programming Language :: Python :: 3.11",
|
|
14
|
+
classifiers = ["Programming Language :: Python :: 3.11",
|
|
16
15
|
"Programming Language :: Python :: 3.12",
|
|
17
16
|
"Natural Language :: English",
|
|
18
17
|
"Operating System :: OS Independent",
|
|
19
18
|
"License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1.2)",
|
|
20
19
|
]
|
|
21
|
-
keywords = ["rudi-node-write", "rudi-node-
|
|
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", "defusedxml", "puremagic"]
|
|
22
23
|
|
|
23
24
|
[project.urls]
|
|
24
25
|
Homepage = "https://github.com/OlivierMartineau/rudi-node-write"
|
|
@@ -34,7 +35,7 @@ target-version = ['py311']
|
|
|
34
35
|
# ----- Tool: commitizen
|
|
35
36
|
[tool.commitizen]
|
|
36
37
|
name = "cz_conventional_commits"
|
|
37
|
-
version = "
|
|
38
|
+
version = "1.0.0"
|
|
38
39
|
version_files = ["pyproject.toml:version"]
|
|
39
40
|
|
|
40
41
|
# ----- Tool: pytest
|
|
@@ -42,4 +43,24 @@ version_files = ["pyproject.toml:version"]
|
|
|
42
43
|
pythonpath = ["src"]
|
|
43
44
|
norecursedirs = ["*.egg", ".eggs", "dist", "build"]
|
|
44
45
|
filterwarnings = ["ignore:.*pkg_resources.*:DeprecationWarning"]
|
|
45
|
-
addopts = ["--cov=rudi_node_write", "--cov-report=term-missing", "--cov-report=html"]
|
|
46
|
+
addopts = ["--cov=rudi_node_write", "--cov-report=term-missing", "--cov-report=html:reports/html_dir", "--cov-report=xml:reports/coverage.xml" ]
|
|
47
|
+
|
|
48
|
+
# ----- Tool: setuptools
|
|
49
|
+
[tool.setuptools.packages.find]
|
|
50
|
+
where = ["src"]
|
|
51
|
+
include = ["rudi_node_write*"]
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.dynamic]
|
|
54
|
+
dependencies = {file = ["requirements.txt"]}
|
|
55
|
+
|
|
56
|
+
# ----- Tool: pyright
|
|
57
|
+
[tool.pyright]
|
|
58
|
+
exclude = ["**/node_modules", "**/__pycache__"]
|
|
59
|
+
include = ["src"]
|
|
60
|
+
pythonVersion = "3.11"
|
|
61
|
+
reportMissingImports = "true"
|
|
62
|
+
reportDuplicateImport = "true"
|
|
63
|
+
reportUnusedImport = "true"
|
|
64
|
+
reportImportCycles = "true"
|
|
65
|
+
venvPath = "."
|
|
66
|
+
venv = ".venv"
|
|
File without changes
|
|
@@ -13,7 +13,7 @@ ODS_API_VERSION: str = "2.1"
|
|
|
13
13
|
|
|
14
14
|
# Default contact when none is provided in the source metadata
|
|
15
15
|
# Structure: https://app.swaggerhub.com/apis/OlivierMartineau/RUDI-PRODUCER/1.3.0#/Contact
|
|
16
|
-
DEFAULT_CONTACT: dict =
|
|
16
|
+
DEFAULT_CONTACT: dict = {"contact_name": "Rudi node admin", "email": "community@rudi-univ-rennes1.fr"}
|
|
17
17
|
|
|
18
18
|
# Default producer when none is provided in the source metadata
|
|
19
19
|
# Structure: https://app.swaggerhub.com/apis/OlivierMartineau/RUDI-PRODUCER/1.3.0#/Organization
|
{rudi-node-write-0.1.0 → rudi_node_write-1.0.0}/src/rudi_node_write/connectors/io_connector.py
RENAMED
|
@@ -4,18 +4,17 @@ from typing import Literal, get_args, BinaryIO, TextIO
|
|
|
4
4
|
from urllib.parse import urlsplit
|
|
5
5
|
|
|
6
6
|
from rudi_node_write.rudi_types.rudi_const import check_is_literal
|
|
7
|
-
from rudi_node_write.
|
|
7
|
+
from rudi_node_write.rudi_types.serializable import Serializable
|
|
8
|
+
from rudi_node_write.utils.dict_utils import is_dict, safe_get_key
|
|
8
9
|
from rudi_node_write.utils.err import HttpError
|
|
9
10
|
from rudi_node_write.utils.log import log_d_if, log_e, log_d
|
|
10
11
|
from rudi_node_write.utils.str_utils import slash_join
|
|
11
|
-
from rudi_node_write.utils.url_utils import url_encode_req_params
|
|
12
|
+
from rudi_node_write.utils.url_utils import get_response_cookies, url_encode_req_params
|
|
12
13
|
|
|
13
|
-
HttpRequestMethod = Literal["GET", "PUT", "
|
|
14
|
+
HttpRequestMethod = Literal["GET", "PUT", "DELETE", "POST"]
|
|
14
15
|
HTTP_REQUEST_METHODS = get_args(HttpRequestMethod)
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
DEFAULT_HEADERS = {CONTENT_TYPE_KEY: "text/plain; ", "Accept": "application/json"}
|
|
17
|
+
DEFAULT_HEADERS = {"Content-Type": "text/plain; ", "Accept": "application/json"}
|
|
19
18
|
|
|
20
19
|
STATUS = "status"
|
|
21
20
|
REDIRECTION = "redirection"
|
|
@@ -26,9 +25,9 @@ def https_download(resource_url: str, headers=None, should_show_debug_line: bool
|
|
|
26
25
|
headers = DEFAULT_HEADERS
|
|
27
26
|
here = "https_download"
|
|
28
27
|
(scheme, netloc, path, query, fragment) = urlsplit(resource_url)
|
|
29
|
-
if scheme
|
|
28
|
+
if scheme not in ("https", "http"):
|
|
30
29
|
raise NotImplementedError(f"only HTTPS protocol is supported, cannot treat this url: {resource_url}")
|
|
31
|
-
connection = HTTPSConnection(netloc)
|
|
30
|
+
connection = (HTTPSConnection if scheme == "https" else HTTPConnection)(netloc)
|
|
32
31
|
|
|
33
32
|
connection.request(method="GET", url=resource_url, headers=headers)
|
|
34
33
|
response = connection.getresponse()
|
|
@@ -53,18 +52,18 @@ class Connector:
|
|
|
53
52
|
self.connection = None
|
|
54
53
|
|
|
55
54
|
self._set_url(server_url)
|
|
55
|
+
self._cookies = None
|
|
56
56
|
|
|
57
57
|
def _set_url(self, server_url: str):
|
|
58
|
-
|
|
58
|
+
here = f"super.{self.class_name}._set_url"
|
|
59
59
|
(scheme, netloc, path, query, fragment) = urlsplit(server_url)
|
|
60
|
-
if scheme
|
|
60
|
+
if scheme not in ("https", "http"):
|
|
61
61
|
raise NotImplementedError(f"only http and https are supported, got '{scheme}'")
|
|
62
|
-
|
|
63
|
-
self.scheme = "https"
|
|
62
|
+
self.scheme = scheme
|
|
64
63
|
self.host = netloc
|
|
65
64
|
self.path = path
|
|
66
65
|
self.base_url = slash_join(f"{self.scheme}://{self.host}", self.path)
|
|
67
|
-
log_d(
|
|
66
|
+
log_d(here, "base_url", self.base_url)
|
|
68
67
|
|
|
69
68
|
@property
|
|
70
69
|
def class_name(self):
|
|
@@ -76,17 +75,23 @@ class Connector:
|
|
|
76
75
|
def full_path(self, relative_url: str = "/"):
|
|
77
76
|
return slash_join("/", self.path, url_encode_req_params(relative_url))
|
|
78
77
|
|
|
79
|
-
def test_connection(self):
|
|
80
|
-
return self.request()
|
|
78
|
+
def test_connection(self) -> bool | str | dict:
|
|
79
|
+
return self.request() # type: ignore
|
|
81
80
|
|
|
82
81
|
def close_connection(self):
|
|
82
|
+
if self.connection is None:
|
|
83
|
+
raise ConnectionAbortedError("The connection should be alive still")
|
|
83
84
|
try:
|
|
84
85
|
self.connection.close()
|
|
85
86
|
except Exception as e: # pragma: no cover
|
|
86
87
|
log_e(self.class_name, "close_connection ERROR", e)
|
|
87
88
|
|
|
88
89
|
def download(
|
|
89
|
-
self,
|
|
90
|
+
self,
|
|
91
|
+
relative_url: str,
|
|
92
|
+
headers: dict | None = None,
|
|
93
|
+
keep_alive: bool = False,
|
|
94
|
+
should_log_response: bool = False,
|
|
90
95
|
):
|
|
91
96
|
"""
|
|
92
97
|
Download a file on the connector server
|
|
@@ -98,6 +103,8 @@ class Connector:
|
|
|
98
103
|
:return: a status
|
|
99
104
|
"""
|
|
100
105
|
here = f"{self.class_name}.download"
|
|
106
|
+
if self.host is None:
|
|
107
|
+
raise AttributeError("The host was not defined")
|
|
101
108
|
if headers is None:
|
|
102
109
|
headers = DEFAULT_HEADERS
|
|
103
110
|
|
|
@@ -122,11 +129,12 @@ class Connector:
|
|
|
122
129
|
self,
|
|
123
130
|
relative_url: str = "/",
|
|
124
131
|
req_method: HttpRequestMethod = "GET",
|
|
125
|
-
body: dict | str | BinaryIO | TextIO = None,
|
|
132
|
+
body: dict | str | list | Serializable | BinaryIO | TextIO | None = None,
|
|
126
133
|
headers=None,
|
|
127
134
|
keep_alive: bool = False,
|
|
135
|
+
should_log_request: bool = False,
|
|
128
136
|
should_log_response: bool = False,
|
|
129
|
-
)
|
|
137
|
+
):
|
|
130
138
|
"""
|
|
131
139
|
Send a http(s) request
|
|
132
140
|
:param relative_url: a relative URL that will be joined to the connector's base URL to form the request URL
|
|
@@ -138,30 +146,35 @@ class Connector:
|
|
|
138
146
|
:param should_log_response: True if some log lines should be displayed (defaults to False).
|
|
139
147
|
:return: the data returned from the request
|
|
140
148
|
"""
|
|
141
|
-
|
|
149
|
+
here = f"{self.class_name}.request"
|
|
142
150
|
|
|
143
151
|
check_is_literal(req_method, HTTP_REQUEST_METHODS, "incorrect type for request method")
|
|
144
152
|
|
|
145
153
|
if headers is None:
|
|
146
154
|
headers = DEFAULT_HEADERS
|
|
147
|
-
if
|
|
148
|
-
headers[
|
|
149
|
-
|
|
155
|
+
if isinstance(body, dict):
|
|
156
|
+
headers["Content-Type"] = "application/json"
|
|
157
|
+
body_str = dumps(body)
|
|
158
|
+
elif isinstance(body, Serializable):
|
|
159
|
+
headers["Content-Type"] = "application/json"
|
|
160
|
+
body_str = body.to_json_str(ensure_ascii=True)
|
|
161
|
+
elif isinstance(body, list):
|
|
162
|
+
body_str = str(list)
|
|
163
|
+
else:
|
|
164
|
+
body_str = body
|
|
150
165
|
|
|
151
166
|
path_url = self.full_path(relative_url)
|
|
167
|
+
if self.host is None:
|
|
168
|
+
raise ConnectionError("The connector host should be defined")
|
|
169
|
+
self.connection = HTTPConnection(self.host) if self.scheme == "http" else HTTPSConnection(self.host)
|
|
152
170
|
|
|
153
|
-
|
|
154
|
-
self.connection = HTTPConnection(self.host)
|
|
155
|
-
else:
|
|
156
|
-
self.connection = HTTPSConnection(self.host)
|
|
157
|
-
log_d(fun, req_method, self.full_url(relative_url))
|
|
171
|
+
log_d_if(should_log_request, here, req_method, self.full_url(relative_url))
|
|
158
172
|
|
|
159
173
|
try:
|
|
160
|
-
|
|
161
|
-
self.connection.request(method=req_method, url=path_url, body=body, headers=headers)
|
|
174
|
+
self.connection.request(method=req_method, url=path_url, body=body_str, headers=headers) # type: ignore
|
|
162
175
|
except ConnectionRefusedError as e:
|
|
163
|
-
log_e(
|
|
164
|
-
log_e(
|
|
176
|
+
log_e(here, "Error on request", req_method, self.full_url(relative_url))
|
|
177
|
+
log_e(here, "ERR", e)
|
|
165
178
|
raise e
|
|
166
179
|
return self.parse_response(
|
|
167
180
|
relative_url=relative_url,
|
|
@@ -178,9 +191,12 @@ class Connector:
|
|
|
178
191
|
should_log_response: bool = True,
|
|
179
192
|
):
|
|
180
193
|
"""Basic parsing of the result"""
|
|
181
|
-
|
|
194
|
+
here = f"{self.class_name}.parse_response"
|
|
195
|
+
if self.connection is None:
|
|
196
|
+
raise ConnectionError("The connector HTTPSConnection should be defined")
|
|
182
197
|
response = self.connection.getresponse()
|
|
183
|
-
|
|
198
|
+
self._cookies = get_response_cookies(response)
|
|
199
|
+
|
|
184
200
|
if response.status in [301, 302]:
|
|
185
201
|
return {STATUS: response.status, REDIRECTION: response.getheader("location")}
|
|
186
202
|
if (
|
|
@@ -191,25 +207,26 @@ class Connector:
|
|
|
191
207
|
return None
|
|
192
208
|
|
|
193
209
|
rdata = response.read()
|
|
194
|
-
# log_d(
|
|
210
|
+
# log_d(here, "rdata", rdata)
|
|
195
211
|
try:
|
|
196
212
|
response_data = loads(rdata)
|
|
197
|
-
log_d_if(should_log_response,
|
|
213
|
+
log_d_if(should_log_response, here, "Response is a JSON", response_data)
|
|
198
214
|
except (TypeError, JSONDecodeError):
|
|
199
215
|
response_data = repr(rdata)
|
|
200
|
-
log_d_if(should_log_response,
|
|
216
|
+
log_d_if(should_log_response, here, "Response is not a JSON", response_data)
|
|
201
217
|
if not keep_alive:
|
|
202
218
|
self.close_connection()
|
|
203
219
|
|
|
204
220
|
if type(response_data) is str:
|
|
205
|
-
log_d_if(should_log_response,
|
|
221
|
+
log_d_if(should_log_response, here, "Response is a string", response_data)
|
|
206
222
|
if response.status == 200:
|
|
207
223
|
return rdata.decode("utf8")
|
|
208
224
|
if response.status == 200:
|
|
209
225
|
return response_data
|
|
210
226
|
if response.status >= 400:
|
|
211
|
-
log_e(
|
|
212
|
-
log_e(
|
|
227
|
+
log_e(here, "Connection error", response_data)
|
|
228
|
+
log_e(here, "Request in error", req_method, self.full_url(relative_url))
|
|
229
|
+
# log_e(here, "Headers", response.headers)
|
|
213
230
|
raise HttpError(response_data, req_method, self.base_url, relative_url)
|
|
214
231
|
|
|
215
232
|
|
|
@@ -217,13 +234,31 @@ if __name__ == "__main__": # pragma: no cover
|
|
|
217
234
|
tests = "Connector tests"
|
|
218
235
|
connector = Connector("https://bacasable.fenix.rudi-univ-rennes1.fr/api/version")
|
|
219
236
|
log_d(tests, "testing connection, got version", connector.test_connection())
|
|
220
|
-
# data = connector.request(relative_url="resources?limit=1")
|
|
221
|
-
# print(data)
|
|
222
237
|
|
|
223
238
|
connector = Connector("https://bacasable.fenix.rudi-univ-rennes1.fr/api/v1")
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
239
|
+
metadata_list = connector.request("resources")
|
|
240
|
+
if isinstance(metadata_list, dict) and metadata_list["total"] is not None:
|
|
241
|
+
log_d(tests, "number of resources declared", f"{metadata_list['total']}")
|
|
242
|
+
else:
|
|
243
|
+
log_e(
|
|
244
|
+
tests,
|
|
245
|
+
f"An error occurred, result should be a dict with 'total' and 'items' properties, got {metadata_list}",
|
|
246
|
+
)
|
|
227
247
|
|
|
228
|
-
url = "
|
|
229
|
-
|
|
248
|
+
url = "resources?available_formats.file_storage_status=available&fields=available_formats"
|
|
249
|
+
meta_list: list = connector.request(url)["items"] # type: ignore
|
|
250
|
+
# print(tests, "media_list", meta_list)
|
|
251
|
+
available_files = []
|
|
252
|
+
for meta in meta_list:
|
|
253
|
+
for media in meta["available_formats"]:
|
|
254
|
+
# log_d(tests, "media", media)
|
|
255
|
+
file_storage_status = safe_get_key(media, "file_storage_status")
|
|
256
|
+
if file_storage_status == "available":
|
|
257
|
+
available_files.append(media)
|
|
258
|
+
|
|
259
|
+
if len(available_files) == 0:
|
|
260
|
+
print("no media available, quiting")
|
|
261
|
+
else:
|
|
262
|
+
media_1 = available_files[0]
|
|
263
|
+
url = media_1["connector"]["url"]
|
|
264
|
+
log_d("https_utils", url, https_download(url))
|