digitalhub 0.12.0__py3-none-any.whl → 0.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of digitalhub might be problematic. Click here for more details.
- digitalhub/__init__.py +1 -1
- digitalhub/context/api.py +5 -5
- digitalhub/context/builder.py +3 -5
- digitalhub/context/context.py +9 -1
- digitalhub/entities/_base/executable/entity.py +105 -57
- digitalhub/entities/_base/material/entity.py +11 -18
- digitalhub/entities/_base/material/utils.py +1 -1
- digitalhub/entities/_commons/metrics.py +64 -30
- digitalhub/entities/_commons/utils.py +36 -9
- digitalhub/entities/_processors/base.py +150 -79
- digitalhub/entities/_processors/context.py +366 -215
- digitalhub/entities/_processors/utils.py +74 -30
- digitalhub/entities/artifact/crud.py +4 -0
- digitalhub/entities/artifact/utils.py +28 -13
- digitalhub/entities/dataitem/crud.py +14 -2
- digitalhub/entities/dataitem/table/entity.py +3 -3
- digitalhub/entities/dataitem/utils.py +84 -35
- digitalhub/entities/model/crud.py +4 -0
- digitalhub/entities/model/utils.py +28 -13
- digitalhub/entities/project/_base/entity.py +0 -2
- digitalhub/entities/run/_base/entity.py +2 -2
- digitalhub/entities/task/_base/models.py +12 -3
- digitalhub/entities/trigger/_base/entity.py +11 -0
- digitalhub/factory/factory.py +25 -3
- digitalhub/factory/utils.py +11 -3
- digitalhub/runtimes/_base.py +1 -1
- digitalhub/runtimes/builder.py +18 -1
- digitalhub/stores/client/__init__.py +12 -0
- digitalhub/stores/client/_base/api_builder.py +14 -0
- digitalhub/stores/client/_base/client.py +93 -0
- digitalhub/stores/client/_base/key_builder.py +28 -0
- digitalhub/stores/client/_base/params_builder.py +14 -0
- digitalhub/stores/client/api.py +10 -5
- digitalhub/stores/client/builder.py +3 -1
- digitalhub/stores/client/dhcore/api_builder.py +17 -0
- digitalhub/stores/client/dhcore/client.py +325 -70
- digitalhub/stores/client/dhcore/configurator.py +485 -193
- digitalhub/stores/client/dhcore/enums.py +3 -0
- digitalhub/stores/client/dhcore/error_parser.py +35 -1
- digitalhub/stores/client/dhcore/params_builder.py +113 -17
- digitalhub/stores/client/dhcore/utils.py +40 -22
- digitalhub/stores/client/local/api_builder.py +17 -0
- digitalhub/stores/client/local/client.py +6 -8
- digitalhub/stores/credentials/api.py +35 -0
- digitalhub/stores/credentials/configurator.py +210 -0
- digitalhub/stores/credentials/enums.py +68 -0
- digitalhub/stores/credentials/handler.py +176 -0
- digitalhub/stores/{configurator → credentials}/ini_module.py +60 -28
- digitalhub/stores/credentials/store.py +81 -0
- digitalhub/stores/data/_base/store.py +27 -9
- digitalhub/stores/data/api.py +49 -9
- digitalhub/stores/data/builder.py +90 -41
- digitalhub/stores/data/local/store.py +4 -7
- digitalhub/stores/data/remote/store.py +4 -7
- digitalhub/stores/data/s3/configurator.py +65 -80
- digitalhub/stores/data/s3/store.py +69 -81
- digitalhub/stores/data/s3/utils.py +10 -10
- digitalhub/stores/data/sql/configurator.py +76 -73
- digitalhub/stores/data/sql/store.py +191 -102
- digitalhub/utils/exceptions.py +6 -0
- digitalhub/utils/file_utils.py +53 -30
- digitalhub/utils/generic_utils.py +41 -33
- digitalhub/utils/git_utils.py +24 -14
- digitalhub/utils/io_utils.py +19 -18
- digitalhub/utils/uri_utils.py +31 -31
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/METADATA +1 -1
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/RECORD +71 -74
- digitalhub/entities/_commons/types.py +0 -9
- digitalhub/stores/configurator/api.py +0 -35
- digitalhub/stores/configurator/configurator.py +0 -202
- digitalhub/stores/configurator/credentials_store.py +0 -69
- digitalhub/stores/configurator/enums.py +0 -25
- digitalhub/stores/data/s3/enums.py +0 -20
- digitalhub/stores/data/sql/enums.py +0 -20
- digitalhub/stores/data/utils.py +0 -38
- /digitalhub/stores/{configurator → credentials}/__init__.py +0 -0
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/WHEEL +0 -0
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/licenses/AUTHORS +0 -0
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,6 +6,7 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
import typing
|
|
8
8
|
from typing import Any
|
|
9
|
+
from warnings import warn
|
|
9
10
|
|
|
10
11
|
from requests import request
|
|
11
12
|
from requests.exceptions import JSONDecodeError
|
|
@@ -16,30 +17,91 @@ from digitalhub.stores.client.dhcore.configurator import ClientDHCoreConfigurato
|
|
|
16
17
|
from digitalhub.stores.client.dhcore.error_parser import ErrorParser
|
|
17
18
|
from digitalhub.stores.client.dhcore.key_builder import ClientDHCoreKeyBuilder
|
|
18
19
|
from digitalhub.stores.client.dhcore.params_builder import ClientDHCoreParametersBuilder
|
|
19
|
-
from digitalhub.utils.exceptions import BackendError
|
|
20
|
+
from digitalhub.utils.exceptions import BackendError, ClientError
|
|
20
21
|
from digitalhub.utils.generic_utils import dump_json
|
|
21
22
|
|
|
22
23
|
if typing.TYPE_CHECKING:
|
|
23
24
|
from requests import Response
|
|
24
25
|
|
|
25
26
|
|
|
27
|
+
# API levels that are supported
|
|
28
|
+
MAX_API_LEVEL = 20
|
|
29
|
+
MIN_API_LEVEL = 13
|
|
30
|
+
LIB_VERSION = 13
|
|
31
|
+
|
|
32
|
+
|
|
26
33
|
class ClientDHCore(Client):
|
|
27
34
|
"""
|
|
28
|
-
DHCore client.
|
|
29
|
-
|
|
30
|
-
The DHCore client is used to communicate with the
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
DHCore client for remote DigitalHub Core backend communication.
|
|
36
|
+
|
|
37
|
+
The DHCore client is used to communicate with the DigitalHub Core
|
|
38
|
+
backend API via REST. The client supports multiple authentication methods:
|
|
39
|
+
- Basic authentication (username/password)
|
|
40
|
+
- OAuth2 token authentication with automatic token refresh
|
|
41
|
+
- Personal access token exchange
|
|
42
|
+
|
|
43
|
+
At initialization, the client attempts to load endpoint and authentication
|
|
44
|
+
parameters from environment variables and the .dhcore configuration file.
|
|
45
|
+
If authentication or endpoint errors occur during client creation, users
|
|
46
|
+
can update the configuration using the `set_dhcore_env` function from
|
|
47
|
+
the utils module.
|
|
48
|
+
|
|
49
|
+
The client automatically handles:
|
|
50
|
+
- API version compatibility checking
|
|
51
|
+
- Pagination for list operations
|
|
52
|
+
- Token refresh on authentication errors
|
|
53
|
+
- Error parsing and exception mapping
|
|
54
|
+
- JSON serialization/deserialization
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
config : dict, optional
|
|
59
|
+
DHCore environment configuration. If None, configuration will
|
|
60
|
+
be loaded from environment variables and configuration files.
|
|
61
|
+
|
|
62
|
+
Attributes
|
|
63
|
+
----------
|
|
64
|
+
_api_builder : ClientDHCoreApiBuilder
|
|
65
|
+
Builds API endpoint URLs for different operations.
|
|
66
|
+
_key_builder : ClientDHCoreKeyBuilder
|
|
67
|
+
Builds storage keys for entities.
|
|
68
|
+
_params_builder : ClientDHCoreParametersBuilder
|
|
69
|
+
Builds request parameters for API calls.
|
|
70
|
+
_error_parser : ErrorParser
|
|
71
|
+
Parses backend responses and raises appropriate exceptions.
|
|
72
|
+
_configurator : ClientDHCoreConfigurator
|
|
73
|
+
Manages client configuration and authentication.
|
|
74
|
+
|
|
75
|
+
Notes
|
|
76
|
+
-----
|
|
77
|
+
Supported DHCore API versions: {MIN_API_LEVEL} to {MAX_API_LEVEL}
|
|
78
|
+
Current library API version: {LIB_VERSION}
|
|
79
|
+
|
|
80
|
+
Examples
|
|
81
|
+
--------
|
|
82
|
+
>>> from digitalhub.stores.client.api import get_client
|
|
83
|
+
>>> client = get_client(local=False)
|
|
84
|
+
>>> # Client is now ready for API operations
|
|
40
85
|
"""
|
|
41
86
|
|
|
42
87
|
def __init__(self, config: dict | None = None) -> None:
|
|
88
|
+
"""
|
|
89
|
+
Initialize DHCore client.
|
|
90
|
+
|
|
91
|
+
Creates a new DHCore client instance with all necessary components
|
|
92
|
+
for communicating with the DigitalHub Core backend. Sets up API
|
|
93
|
+
builders, configurators, and error handling.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
config : dict, optional
|
|
98
|
+
DHCore environment configuration. If None, configuration will
|
|
99
|
+
be loaded from environment variables and configuration files.
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
None
|
|
104
|
+
"""
|
|
43
105
|
super().__init__()
|
|
44
106
|
|
|
45
107
|
# API builder
|
|
@@ -56,7 +118,6 @@ class ClientDHCore(Client):
|
|
|
56
118
|
|
|
57
119
|
# Client Configurator
|
|
58
120
|
self._configurator = ClientDHCoreConfigurator()
|
|
59
|
-
self._configurator.configure(config)
|
|
60
121
|
|
|
61
122
|
##############################
|
|
62
123
|
# CRUD methods
|
|
@@ -66,19 +127,31 @@ class ClientDHCore(Client):
|
|
|
66
127
|
"""
|
|
67
128
|
Create an object in DHCore.
|
|
68
129
|
|
|
130
|
+
Sends a POST request to the DHCore backend to create a new object.
|
|
131
|
+
Automatically sets the appropriate Content-Type header and serializes
|
|
132
|
+
the object to JSON format.
|
|
133
|
+
|
|
69
134
|
Parameters
|
|
70
135
|
----------
|
|
71
136
|
api : str
|
|
72
|
-
|
|
137
|
+
The API endpoint path for creating the object.
|
|
73
138
|
obj : Any
|
|
74
|
-
|
|
139
|
+
The object to create. Will be serialized to JSON.
|
|
75
140
|
**kwargs : dict
|
|
76
|
-
|
|
141
|
+
Additional keyword arguments to pass to the HTTP request,
|
|
142
|
+
such as headers, params, etc.
|
|
77
143
|
|
|
78
144
|
Returns
|
|
79
145
|
-------
|
|
80
146
|
dict
|
|
81
|
-
|
|
147
|
+
The created object as returned by the backend.
|
|
148
|
+
|
|
149
|
+
Raises
|
|
150
|
+
------
|
|
151
|
+
BackendError
|
|
152
|
+
If the backend returns an error response.
|
|
153
|
+
ClientError
|
|
154
|
+
If there are client-side configuration issues.
|
|
82
155
|
"""
|
|
83
156
|
if "headers" not in kwargs:
|
|
84
157
|
kwargs["headers"] = {}
|
|
@@ -90,17 +163,27 @@ class ClientDHCore(Client):
|
|
|
90
163
|
"""
|
|
91
164
|
Get an object from DHCore.
|
|
92
165
|
|
|
166
|
+
Sends a GET request to the DHCore backend to retrieve an existing object.
|
|
167
|
+
|
|
93
168
|
Parameters
|
|
94
169
|
----------
|
|
95
170
|
api : str
|
|
96
|
-
|
|
171
|
+
The API endpoint path for reading the object.
|
|
97
172
|
**kwargs : dict
|
|
98
|
-
|
|
173
|
+
Additional keyword arguments to pass to the HTTP request,
|
|
174
|
+
such as headers, params, etc.
|
|
99
175
|
|
|
100
176
|
Returns
|
|
101
177
|
-------
|
|
102
178
|
dict
|
|
103
|
-
|
|
179
|
+
The retrieved object as returned by the backend.
|
|
180
|
+
|
|
181
|
+
Raises
|
|
182
|
+
------
|
|
183
|
+
BackendError
|
|
184
|
+
If the backend returns an error response.
|
|
185
|
+
EntityNotExistsError
|
|
186
|
+
If the requested object does not exist.
|
|
104
187
|
"""
|
|
105
188
|
return self._prepare_call("GET", api, **kwargs)
|
|
106
189
|
|
|
@@ -108,19 +191,31 @@ class ClientDHCore(Client):
|
|
|
108
191
|
"""
|
|
109
192
|
Update an object in DHCore.
|
|
110
193
|
|
|
194
|
+
Sends a PUT request to the DHCore backend to update an existing object.
|
|
195
|
+
Automatically sets the appropriate Content-Type header and serializes
|
|
196
|
+
the object to JSON format.
|
|
197
|
+
|
|
111
198
|
Parameters
|
|
112
199
|
----------
|
|
113
200
|
api : str
|
|
114
|
-
|
|
115
|
-
obj :
|
|
116
|
-
|
|
201
|
+
The API endpoint path for updating the object.
|
|
202
|
+
obj : Any
|
|
203
|
+
The updated object data. Will be serialized to JSON.
|
|
117
204
|
**kwargs : dict
|
|
118
|
-
|
|
205
|
+
Additional keyword arguments to pass to the HTTP request,
|
|
206
|
+
such as headers, params, etc.
|
|
119
207
|
|
|
120
208
|
Returns
|
|
121
209
|
-------
|
|
122
210
|
dict
|
|
123
|
-
|
|
211
|
+
The updated object as returned by the backend.
|
|
212
|
+
|
|
213
|
+
Raises
|
|
214
|
+
------
|
|
215
|
+
BackendError
|
|
216
|
+
If the backend returns an error response.
|
|
217
|
+
EntityNotExistsError
|
|
218
|
+
If the object to update does not exist.
|
|
124
219
|
"""
|
|
125
220
|
if "headers" not in kwargs:
|
|
126
221
|
kwargs["headers"] = {}
|
|
@@ -132,17 +227,30 @@ class ClientDHCore(Client):
|
|
|
132
227
|
"""
|
|
133
228
|
Delete an object from DHCore.
|
|
134
229
|
|
|
230
|
+
Sends a DELETE request to the DHCore backend to remove an object.
|
|
231
|
+
If the backend returns a boolean response, it will be wrapped in
|
|
232
|
+
a dictionary with a "deleted" key.
|
|
233
|
+
|
|
135
234
|
Parameters
|
|
136
235
|
----------
|
|
137
236
|
api : str
|
|
138
|
-
|
|
237
|
+
The API endpoint path for deleting the object.
|
|
139
238
|
**kwargs : dict
|
|
140
|
-
|
|
239
|
+
Additional keyword arguments to pass to the HTTP request,
|
|
240
|
+
such as headers, params, cascade options, etc.
|
|
141
241
|
|
|
142
242
|
Returns
|
|
143
243
|
-------
|
|
144
244
|
dict
|
|
145
|
-
|
|
245
|
+
The deletion result. Either the backend response or
|
|
246
|
+
{"deleted": True/False} if backend returns a boolean.
|
|
247
|
+
|
|
248
|
+
Raises
|
|
249
|
+
------
|
|
250
|
+
BackendError
|
|
251
|
+
If the backend returns an error response.
|
|
252
|
+
EntityNotExistsError
|
|
253
|
+
If the object to delete does not exist.
|
|
146
254
|
"""
|
|
147
255
|
resp = self._prepare_call("DELETE", api, **kwargs)
|
|
148
256
|
if isinstance(resp, bool):
|
|
@@ -153,21 +261,33 @@ class ClientDHCore(Client):
|
|
|
153
261
|
"""
|
|
154
262
|
List objects from DHCore.
|
|
155
263
|
|
|
264
|
+
Sends GET requests to the DHCore backend to retrieve a paginated list
|
|
265
|
+
of objects. Automatically handles pagination by making multiple requests
|
|
266
|
+
until all objects are retrieved.
|
|
267
|
+
|
|
156
268
|
Parameters
|
|
157
269
|
----------
|
|
158
270
|
api : str
|
|
159
|
-
|
|
271
|
+
The API endpoint path for listing objects.
|
|
160
272
|
**kwargs : dict
|
|
161
|
-
|
|
273
|
+
Additional keyword arguments to pass to the HTTP request.
|
|
274
|
+
Can include 'params' dict with pagination parameters.
|
|
162
275
|
|
|
163
276
|
Returns
|
|
164
277
|
-------
|
|
165
278
|
list[dict]
|
|
166
|
-
|
|
167
|
-
"""
|
|
168
|
-
if kwargs is None:
|
|
169
|
-
kwargs = {}
|
|
279
|
+
A list containing all objects from all pages.
|
|
170
280
|
|
|
281
|
+
Raises
|
|
282
|
+
------
|
|
283
|
+
BackendError
|
|
284
|
+
If the backend returns an error response.
|
|
285
|
+
|
|
286
|
+
Notes
|
|
287
|
+
-----
|
|
288
|
+
This method automatically handles pagination starting from page 0
|
|
289
|
+
and continues until all pages are retrieved.
|
|
290
|
+
"""
|
|
171
291
|
if "params" not in kwargs:
|
|
172
292
|
kwargs["params"] = {}
|
|
173
293
|
|
|
@@ -189,19 +309,27 @@ class ClientDHCore(Client):
|
|
|
189
309
|
|
|
190
310
|
def list_first_object(self, api: str, **kwargs) -> dict:
|
|
191
311
|
"""
|
|
192
|
-
|
|
312
|
+
Get the first object from a list in DHCore.
|
|
313
|
+
|
|
314
|
+
Retrieves the first object from a paginated list by calling
|
|
315
|
+
list_objects and returning the first item.
|
|
193
316
|
|
|
194
317
|
Parameters
|
|
195
318
|
----------
|
|
196
319
|
api : str
|
|
197
|
-
The
|
|
320
|
+
The API endpoint path for listing objects.
|
|
198
321
|
**kwargs : dict
|
|
199
|
-
|
|
322
|
+
Additional keyword arguments to pass to the HTTP request.
|
|
200
323
|
|
|
201
324
|
Returns
|
|
202
325
|
-------
|
|
203
326
|
dict
|
|
204
|
-
The
|
|
327
|
+
The first object from the list.
|
|
328
|
+
|
|
329
|
+
Raises
|
|
330
|
+
------
|
|
331
|
+
BackendError
|
|
332
|
+
If no objects are found or if the backend returns an error.
|
|
205
333
|
"""
|
|
206
334
|
try:
|
|
207
335
|
return self.list_objects(api, **kwargs)[0]
|
|
@@ -212,21 +340,35 @@ class ClientDHCore(Client):
|
|
|
212
340
|
"""
|
|
213
341
|
Search objects from DHCore.
|
|
214
342
|
|
|
343
|
+
Performs a search query against the DHCore backend using Solr search
|
|
344
|
+
capabilities. Handles pagination and removes search highlights from
|
|
345
|
+
the returned objects.
|
|
346
|
+
|
|
215
347
|
Parameters
|
|
216
348
|
----------
|
|
217
349
|
api : str
|
|
218
|
-
|
|
350
|
+
The API endpoint path for searching objects (usually Solr search).
|
|
219
351
|
**kwargs : dict
|
|
220
|
-
|
|
352
|
+
Additional keyword arguments to pass to the HTTP request.
|
|
353
|
+
Can include search parameters, filters, pagination options, etc.
|
|
221
354
|
|
|
222
355
|
Returns
|
|
223
356
|
-------
|
|
224
357
|
list[dict]
|
|
225
|
-
|
|
358
|
+
A list of objects matching the search criteria, with search
|
|
359
|
+
highlights removed.
|
|
360
|
+
|
|
361
|
+
Raises
|
|
362
|
+
------
|
|
363
|
+
BackendError
|
|
364
|
+
If the backend returns an error response.
|
|
365
|
+
|
|
366
|
+
Notes
|
|
367
|
+
-----
|
|
368
|
+
This method sets default values for pagination (page=0, size=10)
|
|
369
|
+
and sorting (by metadata.updated descending) if not provided.
|
|
370
|
+
Search highlights are automatically removed from results.
|
|
226
371
|
"""
|
|
227
|
-
if kwargs is None:
|
|
228
|
-
kwargs = {}
|
|
229
|
-
|
|
230
372
|
if "params" not in kwargs:
|
|
231
373
|
kwargs["params"] = {}
|
|
232
374
|
|
|
@@ -266,73 +408,177 @@ class ClientDHCore(Client):
|
|
|
266
408
|
"""
|
|
267
409
|
Prepare a call to the DHCore API.
|
|
268
410
|
|
|
411
|
+
Handles the preparation of an API call by checking configuration,
|
|
412
|
+
building the URL, and adding authentication parameters.
|
|
413
|
+
|
|
269
414
|
Parameters
|
|
270
415
|
----------
|
|
271
416
|
call_type : str
|
|
272
|
-
The type
|
|
417
|
+
The HTTP method type (GET, POST, PUT, DELETE, etc.).
|
|
273
418
|
api : str
|
|
274
|
-
The
|
|
419
|
+
The API endpoint path to call.
|
|
275
420
|
**kwargs : dict
|
|
276
|
-
|
|
421
|
+
Additional keyword arguments to pass to the HTTP request.
|
|
277
422
|
|
|
278
423
|
Returns
|
|
279
424
|
-------
|
|
280
425
|
dict
|
|
281
|
-
|
|
426
|
+
The response from the API call.
|
|
427
|
+
|
|
428
|
+
Raises
|
|
429
|
+
------
|
|
430
|
+
ClientError
|
|
431
|
+
If the client configuration is invalid.
|
|
432
|
+
BackendError
|
|
433
|
+
If the backend returns an error response.
|
|
282
434
|
"""
|
|
283
435
|
self._configurator.check_config()
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
436
|
+
url = self._build_url(api)
|
|
437
|
+
full_kwargs = self._configurator.get_auth_parameters(kwargs)
|
|
438
|
+
return self._make_call(call_type, url, **full_kwargs)
|
|
439
|
+
|
|
440
|
+
def _build_url(self, api: str) -> str:
|
|
441
|
+
"""
|
|
442
|
+
Build the complete URL for an API call.
|
|
443
|
+
|
|
444
|
+
Combines the configured endpoint with the API path to create
|
|
445
|
+
the full URL for the HTTP request.
|
|
289
446
|
|
|
290
|
-
|
|
447
|
+
Parameters
|
|
448
|
+
----------
|
|
449
|
+
api : str
|
|
450
|
+
The API endpoint path. Leading slashes are automatically handled.
|
|
451
|
+
|
|
452
|
+
Returns
|
|
453
|
+
-------
|
|
454
|
+
str
|
|
455
|
+
The complete URL for the API call.
|
|
456
|
+
|
|
457
|
+
Notes
|
|
458
|
+
-----
|
|
459
|
+
This method automatically removes leading slashes from the API path
|
|
460
|
+
to ensure proper URL construction.
|
|
461
|
+
"""
|
|
462
|
+
endpoint = self._configurator.get_endpoint()
|
|
463
|
+
return f"{endpoint}/{api.removeprefix('/')}"
|
|
464
|
+
|
|
465
|
+
def _make_call(self, call_type: str, url: str, refresh: bool = True, **kwargs) -> dict:
|
|
291
466
|
"""
|
|
292
467
|
Make a call to the DHCore API.
|
|
293
468
|
|
|
469
|
+
Executes the actual HTTP request to the DHCore backend, handles
|
|
470
|
+
API version checking, automatic token refresh on 401 errors,
|
|
471
|
+
and error parsing.
|
|
472
|
+
|
|
294
473
|
Parameters
|
|
295
474
|
----------
|
|
296
475
|
call_type : str
|
|
297
|
-
The type
|
|
476
|
+
The HTTP method type (GET, POST, PUT, DELETE, etc.).
|
|
298
477
|
url : str
|
|
299
|
-
The URL to call.
|
|
478
|
+
The complete URL to call.
|
|
479
|
+
refresh : bool, default True
|
|
480
|
+
Whether to attempt token refresh on authentication errors.
|
|
481
|
+
Set to False to prevent infinite recursion during refresh.
|
|
300
482
|
**kwargs : dict
|
|
301
|
-
|
|
483
|
+
Additional keyword arguments to pass to the HTTP request.
|
|
302
484
|
|
|
303
485
|
Returns
|
|
304
486
|
-------
|
|
305
487
|
dict
|
|
306
|
-
|
|
488
|
+
The parsed response from the backend as a dictionary.
|
|
489
|
+
|
|
490
|
+
Raises
|
|
491
|
+
------
|
|
492
|
+
ClientError
|
|
493
|
+
If the backend API version is not supported.
|
|
494
|
+
BackendError
|
|
495
|
+
If the backend returns an error response or response parsing fails.
|
|
496
|
+
UnauthorizedError
|
|
497
|
+
If authentication fails and token refresh is not possible.
|
|
498
|
+
|
|
499
|
+
Notes
|
|
500
|
+
-----
|
|
501
|
+
This method automatically handles:
|
|
502
|
+
- API version compatibility checking
|
|
503
|
+
- OAuth2 token refresh on 401 errors
|
|
504
|
+
- Response parsing and error handling
|
|
505
|
+
- 60-second timeout for all requests
|
|
307
506
|
"""
|
|
308
507
|
# Call the API
|
|
309
508
|
response = request(call_type, url, timeout=60, **kwargs)
|
|
310
509
|
|
|
311
510
|
# Evaluate DHCore API version
|
|
312
|
-
self.
|
|
511
|
+
self._check_core_version(response)
|
|
313
512
|
|
|
314
|
-
# Handle token refresh
|
|
315
|
-
if response.status_code in [401] and
|
|
316
|
-
self._configurator.
|
|
317
|
-
kwargs = self._configurator.
|
|
318
|
-
return self._make_call(call_type, url,
|
|
513
|
+
# Handle token refresh (redo call)
|
|
514
|
+
if (response.status_code in [401]) and (refresh) and self._configurator.refreshable_auth_types():
|
|
515
|
+
self._configurator.refresh_credentials(change_origin=True)
|
|
516
|
+
kwargs = self._configurator.get_auth_parameters(kwargs)
|
|
517
|
+
return self._make_call(call_type, url, refresh=False, **kwargs)
|
|
319
518
|
|
|
320
519
|
self._error_parser.parse(response)
|
|
321
520
|
return self._dictify_response(response)
|
|
322
521
|
|
|
522
|
+
def _check_core_version(self, response: Response) -> None:
|
|
523
|
+
"""
|
|
524
|
+
Check DHCore API version compatibility.
|
|
525
|
+
|
|
526
|
+
Validates that the DHCore backend API version is compatible with
|
|
527
|
+
this client library. Issues warnings if the backend version is
|
|
528
|
+
newer than the library version.
|
|
529
|
+
|
|
530
|
+
Parameters
|
|
531
|
+
----------
|
|
532
|
+
response : Response
|
|
533
|
+
The HTTP response object containing the X-Api-Level header.
|
|
534
|
+
|
|
535
|
+
Returns
|
|
536
|
+
-------
|
|
537
|
+
None
|
|
538
|
+
|
|
539
|
+
Raises
|
|
540
|
+
------
|
|
541
|
+
ClientError
|
|
542
|
+
If the backend API level is not supported by this client.
|
|
543
|
+
|
|
544
|
+
Notes
|
|
545
|
+
-----
|
|
546
|
+
Supported API levels: {MIN_API_LEVEL} to {MAX_API_LEVEL}
|
|
547
|
+
Current library version: {LIB_VERSION}
|
|
548
|
+
"""
|
|
549
|
+
if "X-Api-Level" in response.headers:
|
|
550
|
+
core_api_level = int(response.headers["X-Api-Level"])
|
|
551
|
+
if not (MIN_API_LEVEL <= core_api_level <= MAX_API_LEVEL):
|
|
552
|
+
raise ClientError("Backend API level not supported.")
|
|
553
|
+
if LIB_VERSION < core_api_level:
|
|
554
|
+
warn("Backend API level is higher than library version. You should consider updating the library.")
|
|
555
|
+
|
|
323
556
|
def _dictify_response(self, response: Response) -> dict:
|
|
324
557
|
"""
|
|
325
|
-
|
|
558
|
+
Parse HTTP response to dictionary.
|
|
559
|
+
|
|
560
|
+
Converts the HTTP response body from JSON to a Python dictionary.
|
|
561
|
+
Handles empty responses gracefully.
|
|
326
562
|
|
|
327
563
|
Parameters
|
|
328
564
|
----------
|
|
329
565
|
response : Response
|
|
330
|
-
The response object.
|
|
566
|
+
The HTTP response object to parse.
|
|
331
567
|
|
|
332
568
|
Returns
|
|
333
569
|
-------
|
|
334
570
|
dict
|
|
335
|
-
The parsed response
|
|
571
|
+
The parsed response body as a dictionary. Returns empty dict
|
|
572
|
+
if response body is empty.
|
|
573
|
+
|
|
574
|
+
Raises
|
|
575
|
+
------
|
|
576
|
+
BackendError
|
|
577
|
+
If the response cannot be parsed as JSON.
|
|
578
|
+
|
|
579
|
+
Notes
|
|
580
|
+
-----
|
|
581
|
+
Empty response bodies are treated as valid and return an empty dict.
|
|
336
582
|
"""
|
|
337
583
|
try:
|
|
338
584
|
return response.json()
|
|
@@ -348,11 +594,20 @@ class ClientDHCore(Client):
|
|
|
348
594
|
@staticmethod
|
|
349
595
|
def is_local() -> bool:
|
|
350
596
|
"""
|
|
351
|
-
|
|
597
|
+
Check if this client operates locally.
|
|
598
|
+
|
|
599
|
+
Returns a flag indicating whether this client instance operates
|
|
600
|
+
on local data or communicates with a remote backend.
|
|
352
601
|
|
|
353
602
|
Returns
|
|
354
603
|
-------
|
|
355
604
|
bool
|
|
356
|
-
False
|
|
605
|
+
False, indicating this client communicates with a remote
|
|
606
|
+
DHCore backend, not local storage.
|
|
607
|
+
|
|
608
|
+
Notes
|
|
609
|
+
-----
|
|
610
|
+
This method is used to distinguish between ClientDHCore (returns False)
|
|
611
|
+
and ClientLocal (returns True) implementations.
|
|
357
612
|
"""
|
|
358
613
|
return False
|