digitalhub 0.13.3__py3-none-any.whl → 0.14.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.

Files changed (116) hide show
  1. digitalhub/__init__.py +3 -8
  2. digitalhub/context/api.py +1 -5
  3. digitalhub/context/builder.py +1 -5
  4. digitalhub/context/context.py +15 -9
  5. digitalhub/entities/_base/_base/entity.py +0 -15
  6. digitalhub/entities/_base/context/entity.py +1 -1
  7. digitalhub/entities/_base/entity/builder.py +5 -5
  8. digitalhub/entities/_base/entity/entity.py +0 -8
  9. digitalhub/entities/_base/executable/entity.py +169 -79
  10. digitalhub/entities/_base/material/entity.py +6 -22
  11. digitalhub/entities/_base/material/utils.py +1 -4
  12. digitalhub/entities/_base/runtime_entity/builder.py +53 -18
  13. digitalhub/entities/_base/unversioned/entity.py +1 -1
  14. digitalhub/entities/_base/versioned/entity.py +1 -1
  15. digitalhub/entities/_commons/enums.py +1 -31
  16. digitalhub/entities/_commons/utils.py +83 -21
  17. digitalhub/entities/_constructors/_resources.py +151 -0
  18. digitalhub/entities/{_base/entity/_constructors → _constructors}/name.py +18 -0
  19. digitalhub/entities/_processors/base/__init__.py +3 -0
  20. digitalhub/entities/_processors/{base.py → base/crud.py} +14 -226
  21. digitalhub/entities/_processors/base/import_export.py +123 -0
  22. digitalhub/entities/_processors/base/processor.py +302 -0
  23. digitalhub/entities/_processors/base/special_ops.py +108 -0
  24. digitalhub/entities/_processors/context/__init__.py +3 -0
  25. digitalhub/entities/_processors/context/crud.py +652 -0
  26. digitalhub/entities/_processors/context/import_export.py +242 -0
  27. digitalhub/entities/_processors/context/material.py +123 -0
  28. digitalhub/entities/_processors/context/processor.py +400 -0
  29. digitalhub/entities/_processors/context/special_ops.py +476 -0
  30. digitalhub/entities/_processors/processors.py +12 -0
  31. digitalhub/entities/_processors/utils.py +12 -11
  32. digitalhub/entities/artifact/crud.py +58 -22
  33. digitalhub/entities/artifact/utils.py +3 -3
  34. digitalhub/entities/dataitem/crud.py +63 -20
  35. digitalhub/entities/dataitem/table/entity.py +24 -22
  36. digitalhub/entities/dataitem/utils.py +15 -15
  37. digitalhub/entities/function/_base/entity.py +3 -3
  38. digitalhub/entities/function/crud.py +55 -24
  39. digitalhub/entities/model/_base/entity.py +62 -20
  40. digitalhub/entities/model/crud.py +58 -22
  41. digitalhub/entities/model/utils.py +3 -3
  42. digitalhub/entities/project/_base/entity.py +321 -152
  43. digitalhub/entities/project/crud.py +15 -23
  44. digitalhub/entities/run/_base/builder.py +0 -4
  45. digitalhub/entities/run/_base/entity.py +70 -63
  46. digitalhub/entities/run/crud.py +79 -26
  47. digitalhub/entities/secret/_base/entity.py +1 -5
  48. digitalhub/entities/secret/crud.py +29 -26
  49. digitalhub/entities/task/_base/builder.py +0 -4
  50. digitalhub/entities/task/_base/entity.py +5 -5
  51. digitalhub/entities/task/_base/models.py +13 -16
  52. digitalhub/entities/task/crud.py +61 -29
  53. digitalhub/entities/trigger/_base/entity.py +1 -5
  54. digitalhub/entities/trigger/crud.py +64 -24
  55. digitalhub/entities/workflow/_base/entity.py +3 -3
  56. digitalhub/entities/workflow/crud.py +55 -21
  57. digitalhub/factory/entity.py +283 -0
  58. digitalhub/factory/enums.py +18 -0
  59. digitalhub/factory/registry.py +197 -0
  60. digitalhub/factory/runtime.py +44 -0
  61. digitalhub/factory/utils.py +3 -54
  62. digitalhub/runtimes/_base.py +2 -2
  63. digitalhub/stores/client/_base/enums.py +39 -0
  64. digitalhub/stores/client/_base/key_builder.py +2 -2
  65. digitalhub/stores/client/_base/params_builder.py +48 -0
  66. digitalhub/stores/client/api.py +6 -10
  67. digitalhub/stores/client/builder.py +4 -4
  68. digitalhub/stores/client/dhcore/api_builder.py +2 -1
  69. digitalhub/stores/client/dhcore/client.py +85 -429
  70. digitalhub/stores/client/dhcore/configurator.py +109 -328
  71. digitalhub/stores/client/dhcore/enums.py +0 -16
  72. digitalhub/stores/client/dhcore/error_parser.py +0 -4
  73. digitalhub/stores/client/dhcore/header_manager.py +61 -0
  74. digitalhub/stores/client/dhcore/http_handler.py +133 -0
  75. digitalhub/stores/client/dhcore/params_builder.py +147 -134
  76. digitalhub/stores/client/dhcore/response_processor.py +102 -0
  77. digitalhub/stores/client/dhcore/utils.py +6 -72
  78. digitalhub/stores/client/local/api_builder.py +1 -1
  79. digitalhub/stores/client/local/client.py +79 -47
  80. digitalhub/stores/client/local/params_builder.py +18 -41
  81. digitalhub/stores/credentials/api.py +0 -4
  82. digitalhub/stores/credentials/configurator.py +2 -28
  83. digitalhub/stores/credentials/enums.py +3 -0
  84. digitalhub/stores/credentials/handler.py +0 -12
  85. digitalhub/stores/credentials/ini_module.py +0 -22
  86. digitalhub/stores/credentials/store.py +0 -4
  87. digitalhub/stores/data/_base/store.py +0 -16
  88. digitalhub/stores/data/builder.py +1 -5
  89. digitalhub/stores/data/local/store.py +0 -103
  90. digitalhub/stores/data/remote/store.py +0 -4
  91. digitalhub/stores/data/s3/configurator.py +60 -14
  92. digitalhub/stores/data/s3/store.py +49 -16
  93. digitalhub/stores/data/sql/configurator.py +0 -8
  94. digitalhub/stores/data/sql/store.py +21 -10
  95. digitalhub/stores/readers/data/factory.py +0 -8
  96. digitalhub/stores/readers/data/pandas/reader.py +0 -16
  97. digitalhub/utils/file_utils.py +0 -17
  98. digitalhub/utils/generic_utils.py +0 -12
  99. digitalhub/utils/git_utils.py +0 -8
  100. digitalhub/utils/io_utils.py +0 -12
  101. digitalhub/utils/store_utils.py +44 -0
  102. {digitalhub-0.13.3.dist-info → digitalhub-0.14.0.dist-info}/METADATA +3 -2
  103. {digitalhub-0.13.3.dist-info → digitalhub-0.14.0.dist-info}/RECORD +111 -95
  104. digitalhub/entities/_processors/context.py +0 -1450
  105. digitalhub/entities/task/_base/utils.py +0 -22
  106. digitalhub/factory/factory.py +0 -381
  107. digitalhub/stores/client/dhcore/models.py +0 -40
  108. digitalhub/stores/data/s3/utils.py +0 -78
  109. /digitalhub/entities/{_base/entity/_constructors → _constructors}/__init__.py +0 -0
  110. /digitalhub/entities/{_base/entity/_constructors → _constructors}/metadata.py +0 -0
  111. /digitalhub/entities/{_base/entity/_constructors → _constructors}/spec.py +0 -0
  112. /digitalhub/entities/{_base/entity/_constructors → _constructors}/status.py +0 -0
  113. /digitalhub/entities/{_base/entity/_constructors → _constructors}/uuid.py +0 -0
  114. {digitalhub-0.13.3.dist-info → digitalhub-0.14.0.dist-info}/WHEEL +0 -0
  115. {digitalhub-0.13.3.dist-info → digitalhub-0.14.0.dist-info}/licenses/AUTHORS +0 -0
  116. {digitalhub-0.13.3.dist-info → digitalhub-0.14.0.dist-info}/licenses/LICENSE +0 -0
@@ -4,120 +4,39 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- import typing
8
7
  from typing import Any
9
- from warnings import warn
10
-
11
- from requests import request
12
- from requests.exceptions import JSONDecodeError
13
8
 
14
9
  from digitalhub.stores.client._base.client import Client
15
10
  from digitalhub.stores.client.dhcore.api_builder import ClientDHCoreApiBuilder
16
- from digitalhub.stores.client.dhcore.configurator import ClientDHCoreConfigurator
17
- from digitalhub.stores.client.dhcore.error_parser import ErrorParser
11
+ from digitalhub.stores.client.dhcore.header_manager import HeaderManager
12
+ from digitalhub.stores.client.dhcore.http_handler import HttpRequestHandler
18
13
  from digitalhub.stores.client.dhcore.key_builder import ClientDHCoreKeyBuilder
19
14
  from digitalhub.stores.client.dhcore.params_builder import ClientDHCoreParametersBuilder
20
- from digitalhub.utils.exceptions import BackendError, ClientError
15
+ from digitalhub.utils.exceptions import BackendError
21
16
  from digitalhub.utils.generic_utils import dump_json
22
17
 
23
- if typing.TYPE_CHECKING:
24
- from requests import Response
25
-
26
-
27
- # API levels that are supported
28
- MAX_API_LEVEL = 20
29
- MIN_API_LEVEL = 13
30
- LIB_VERSION = 13
31
-
32
18
 
33
19
  class ClientDHCore(Client):
34
20
  """
35
21
  DHCore client for remote DigitalHub Core backend communication.
36
22
 
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
23
+ Provides REST API communication with DigitalHub Core backend supporting
24
+ multiple authentication methods: Basic (username/password), OAuth2 (token
25
+ with refresh), and Personal Access Token exchange. Automatically handles
26
+ API version compatibility, pagination, token refresh, error parsing, and
27
+ JSON serialization.
85
28
  """
86
29
 
87
30
  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
- """
105
31
  super().__init__()
106
32
 
107
- # API builder
108
- self._api_builder = ClientDHCoreApiBuilder()
109
-
110
- # Key builder
111
- self._key_builder = ClientDHCoreKeyBuilder()
112
-
113
- # Parameters builder
114
- self._params_builder = ClientDHCoreParametersBuilder()
33
+ # API, key and parameters builders
34
+ self._api_builder: ClientDHCoreApiBuilder = ClientDHCoreApiBuilder()
35
+ self._key_builder: ClientDHCoreKeyBuilder = ClientDHCoreKeyBuilder()
36
+ self._params_builder: ClientDHCoreParametersBuilder = ClientDHCoreParametersBuilder()
115
37
 
116
- # Error parser
117
- self._error_parser = ErrorParser()
118
-
119
- # Client Configurator
120
- self._configurator = ClientDHCoreConfigurator()
38
+ # HTTP request handling
39
+ self._http_handler = HttpRequestHandler()
121
40
 
122
41
  ##############################
123
42
  # CRUD methods
@@ -125,39 +44,27 @@ class ClientDHCore(Client):
125
44
 
126
45
  def create_object(self, api: str, obj: Any, **kwargs) -> dict:
127
46
  """
128
- Create an object in DHCore.
47
+ Create an object in DHCore via POST request.
129
48
 
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.
49
+ Automatically sets Content-Type header and serializes object to JSON.
133
50
 
134
51
  Parameters
135
52
  ----------
136
53
  api : str
137
- The API endpoint path for creating the object.
54
+ API endpoint path for creating the object.
138
55
  obj : Any
139
- The object to create. Will be serialized to JSON.
56
+ Object to create. Will be serialized to JSON.
140
57
  **kwargs : dict
141
- Additional keyword arguments to pass to the HTTP request,
142
- such as headers, params, etc.
58
+ Additional HTTP request arguments.
143
59
 
144
60
  Returns
145
61
  -------
146
62
  dict
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.
63
+ Created object as returned by the backend.
155
64
  """
156
- if "headers" not in kwargs:
157
- kwargs["headers"] = {}
158
- kwargs["headers"]["Content-Type"] = "application/json"
65
+ kwargs = HeaderManager.set_json_content_type(**kwargs)
159
66
  kwargs["data"] = dump_json(obj)
160
- return self._prepare_call("POST", api, **kwargs)
67
+ return self._http_handler.prepare_request("POST", api, **kwargs)
161
68
 
162
69
  def read_object(self, api: str, **kwargs) -> dict:
163
70
  """
@@ -168,15 +75,14 @@ class ClientDHCore(Client):
168
75
  Parameters
169
76
  ----------
170
77
  api : str
171
- The API endpoint path for reading the object.
78
+ API endpoint path for reading the object.
172
79
  **kwargs : dict
173
- Additional keyword arguments to pass to the HTTP request,
174
- such as headers, params, etc.
80
+ Additional HTTP request arguments.
175
81
 
176
82
  Returns
177
83
  -------
178
84
  dict
179
- The retrieved object as returned by the backend.
85
+ Retrieved object as returned by the backend.
180
86
 
181
87
  Raises
182
88
  ------
@@ -185,151 +91,108 @@ class ClientDHCore(Client):
185
91
  EntityNotExistsError
186
92
  If the requested object does not exist.
187
93
  """
188
- return self._prepare_call("GET", api, **kwargs)
94
+ return self._http_handler.prepare_request("GET", api, **kwargs)
189
95
 
190
96
  def update_object(self, api: str, obj: Any, **kwargs) -> dict:
191
97
  """
192
- Update an object in DHCore.
98
+ Update an object in DHCore via PUT request.
193
99
 
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.
100
+ Automatically sets Content-Type header and serializes object to JSON.
197
101
 
198
102
  Parameters
199
103
  ----------
200
104
  api : str
201
- The API endpoint path for updating the object.
105
+ API endpoint path for updating the object.
202
106
  obj : Any
203
- The updated object data. Will be serialized to JSON.
107
+ Updated object data. Will be serialized to JSON.
204
108
  **kwargs : dict
205
- Additional keyword arguments to pass to the HTTP request,
206
- such as headers, params, etc.
109
+ Additional HTTP request arguments.
207
110
 
208
111
  Returns
209
112
  -------
210
113
  dict
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.
114
+ Updated object as returned by the backend.
219
115
  """
220
- if "headers" not in kwargs:
221
- kwargs["headers"] = {}
222
- kwargs["headers"]["Content-Type"] = "application/json"
116
+ kwargs = HeaderManager.set_json_content_type(**kwargs)
223
117
  kwargs["data"] = dump_json(obj)
224
- return self._prepare_call("PUT", api, **kwargs)
118
+ return self._http_handler.prepare_request("PUT", api, **kwargs)
225
119
 
226
120
  def delete_object(self, api: str, **kwargs) -> dict:
227
121
  """
228
122
  Delete an object from DHCore.
229
123
 
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.
124
+ Sends DELETE request to remove an object. Wraps boolean responses
125
+ in {"deleted": True/False} dictionary.
233
126
 
234
127
  Parameters
235
128
  ----------
236
129
  api : str
237
- The API endpoint path for deleting the object.
130
+ API endpoint path for deleting the object.
238
131
  **kwargs : dict
239
- Additional keyword arguments to pass to the HTTP request,
240
- such as headers, params, cascade options, etc.
132
+ Additional HTTP request arguments.
241
133
 
242
134
  Returns
243
135
  -------
244
136
  dict
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.
137
+ Deletion result from backend or {"deleted": bool} wrapper.
254
138
  """
255
- resp = self._prepare_call("DELETE", api, **kwargs)
139
+ resp = self._http_handler.prepare_request("DELETE", api, **kwargs)
256
140
  if isinstance(resp, bool):
257
141
  resp = {"deleted": resp}
258
142
  return resp
259
143
 
260
144
  def list_objects(self, api: str, **kwargs) -> list[dict]:
261
145
  """
262
- List objects from DHCore.
146
+ List objects from DHCore with automatic pagination.
263
147
 
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.
148
+ Sends GET requests to retrieve paginated objects, automatically handling
149
+ pagination (starting from page 0) until all objects are retrieved.
267
150
 
268
151
  Parameters
269
152
  ----------
270
153
  api : str
271
- The API endpoint path for listing objects.
154
+ API endpoint path for listing objects.
272
155
  **kwargs : dict
273
- Additional keyword arguments to pass to the HTTP request.
274
- Can include 'params' dict with pagination parameters.
156
+ Additional HTTP request arguments. Can include 'params' dict
157
+ with pagination parameters.
275
158
 
276
159
  Returns
277
160
  -------
278
161
  list[dict]
279
- A list containing all objects from all pages.
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.
162
+ List containing all objects from all pages.
290
163
  """
291
- if "params" not in kwargs:
292
- kwargs["params"] = {}
293
-
294
- start_page = 0
295
- if "page" not in kwargs["params"]:
296
- kwargs["params"]["page"] = start_page
164
+ kwargs = self._params_builder.set_pagination(partial=True, **kwargs)
297
165
 
298
166
  objects = []
299
167
  while True:
300
- resp = self._prepare_call("GET", api, **kwargs)
168
+ resp = self._http_handler.prepare_request("GET", api, **kwargs)
301
169
  contents = resp["content"]
302
170
  total_pages = resp["totalPages"]
303
- if not contents or kwargs["params"]["page"] >= total_pages:
304
- break
305
171
  objects.extend(contents)
306
- kwargs["params"]["page"] += 1
172
+ if not contents or self._params_builder.read_page_number(**kwargs) >= (total_pages - 1):
173
+ break
174
+ self._params_builder.increment_page_number(**kwargs)
307
175
 
308
176
  return objects
309
177
 
310
178
  def list_first_object(self, api: str, **kwargs) -> dict:
311
179
  """
312
- Get the first object from a list in DHCore.
180
+ Get the first object from a DHCore list.
313
181
 
314
- Retrieves the first object from a paginated list by calling
315
- list_objects and returning the first item.
182
+ Retrieves the first object by calling list_objects and returning
183
+ the first item.
316
184
 
317
185
  Parameters
318
186
  ----------
319
187
  api : str
320
- The API endpoint path for listing objects.
188
+ API endpoint path for listing objects.
321
189
  **kwargs : dict
322
- Additional keyword arguments to pass to the HTTP request.
190
+ Additional HTTP request arguments.
323
191
 
324
192
  Returns
325
193
  -------
326
194
  dict
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.
195
+ First object from the list.
333
196
  """
334
197
  try:
335
198
  return self.list_objects(api, **kwargs)[0]
@@ -338,60 +201,35 @@ class ClientDHCore(Client):
338
201
 
339
202
  def search_objects(self, api: str, **kwargs) -> list[dict]:
340
203
  """
341
- Search objects from DHCore.
204
+ Search objects from DHCore using Solr capabilities.
342
205
 
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.
206
+ Performs search query with pagination and removes search highlights.
207
+ Sets default pagination (page=0, size=10) and sorting (metadata.updated DESC)
208
+ if not provided.
346
209
 
347
210
  Parameters
348
211
  ----------
349
212
  api : str
350
- The API endpoint path for searching objects (usually Solr search).
213
+ API endpoint path for searching objects (usually Solr search).
351
214
  **kwargs : dict
352
- Additional keyword arguments to pass to the HTTP request.
353
- Can include search parameters, filters, pagination options, etc.
215
+ Additional HTTP request arguments including search parameters,
216
+ filters, and pagination options.
354
217
 
355
218
  Returns
356
219
  -------
357
220
  list[dict]
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.
221
+ List of matching objects with search highlights removed.
371
222
  """
372
- if "params" not in kwargs:
373
- kwargs["params"] = {}
374
-
375
- start_page = 0
376
- if "page" not in kwargs["params"]:
377
- kwargs["params"]["page"] = start_page
378
-
379
- if "size" not in kwargs["params"]:
380
- kwargs["params"]["size"] = 10
381
-
382
- # Add sorting
383
- if "sort" not in kwargs["params"]:
384
- kwargs["params"]["sort"] = "metadata.updated,DESC"
385
-
223
+ kwargs = self._params_builder.set_pagination(**kwargs)
386
224
  objects_with_highlights: list[dict] = []
387
225
  while True:
388
- resp = self._prepare_call("GET", api, **kwargs)
226
+ resp = self._http_handler.prepare_request("GET", api, **kwargs)
389
227
  contents = resp["content"]
390
228
  total_pages = resp["totalPages"]
391
- if not contents or kwargs["params"]["page"] >= total_pages:
392
- break
393
229
  objects_with_highlights.extend(contents)
394
- kwargs["params"]["page"] += 1
230
+ if not contents or self._params_builder.read_page_number(**kwargs) >= (total_pages - 1):
231
+ break
232
+ self._params_builder.increment_page_number(**kwargs)
395
233
 
396
234
  objects = []
397
235
  for obj in objects_with_highlights:
@@ -400,193 +238,6 @@ class ClientDHCore(Client):
400
238
 
401
239
  return objects
402
240
 
403
- ##############################
404
- # Call methods
405
- ##############################
406
-
407
- def _prepare_call(self, call_type: str, api: str, **kwargs) -> dict:
408
- """
409
- Prepare a call to the DHCore API.
410
-
411
- Handles the preparation of an API call by checking configuration,
412
- building the URL, and adding authentication parameters.
413
-
414
- Parameters
415
- ----------
416
- call_type : str
417
- The HTTP method type (GET, POST, PUT, DELETE, etc.).
418
- api : str
419
- The API endpoint path to call.
420
- **kwargs : dict
421
- Additional keyword arguments to pass to the HTTP request.
422
-
423
- Returns
424
- -------
425
- dict
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.
434
- """
435
- self._configurator.check_config()
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.
446
-
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:
466
- """
467
- Make a call to the DHCore API.
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
-
473
- Parameters
474
- ----------
475
- call_type : str
476
- The HTTP method type (GET, POST, PUT, DELETE, etc.).
477
- url : str
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.
482
- **kwargs : dict
483
- Additional keyword arguments to pass to the HTTP request.
484
-
485
- Returns
486
- -------
487
- dict
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
506
- """
507
- # Call the API
508
- response = request(call_type, url, timeout=60, **kwargs)
509
-
510
- # Evaluate DHCore API version
511
- self._check_core_version(response)
512
-
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)
518
-
519
- self._error_parser.parse(response)
520
- return self._dictify_response(response)
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
-
556
- def _dictify_response(self, response: Response) -> dict:
557
- """
558
- Parse HTTP response to dictionary.
559
-
560
- Converts the HTTP response body from JSON to a Python dictionary.
561
- Handles empty responses gracefully.
562
-
563
- Parameters
564
- ----------
565
- response : Response
566
- The HTTP response object to parse.
567
-
568
- Returns
569
- -------
570
- dict
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.
582
- """
583
- try:
584
- return response.json()
585
- except JSONDecodeError:
586
- if response.text == "":
587
- return {}
588
- raise BackendError("Backend response could not be parsed.")
589
-
590
241
  ##############################
591
242
  # Interface methods
592
243
  ##############################
@@ -596,18 +247,23 @@ class ClientDHCore(Client):
596
247
  """
597
248
  Check if this client operates locally.
598
249
 
599
- Returns a flag indicating whether this client instance operates
600
- on local data or communicates with a remote backend.
250
+ Used to distinguish between ClientDHCore (remote) and ClientLocal
251
+ implementations.
601
252
 
602
253
  Returns
603
254
  -------
604
255
  bool
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.
256
+ False, indicating this client communicates with remote DHCore backend.
612
257
  """
613
258
  return False
259
+
260
+ ##############################
261
+ # Utility methods
262
+ ##############################
263
+
264
+ def refresh_token(self) -> None:
265
+ """
266
+ Manually trigger OAuth2 token refresh.
267
+ """
268
+ self._http_handler._configurator.check_config()
269
+ self._http_handler._configurator.refresh_credentials()