digitalhub 0.14.0b2__py3-none-any.whl → 0.14.0b4__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 (75) hide show
  1. digitalhub/context/builder.py +0 -4
  2. digitalhub/context/context.py +12 -8
  3. digitalhub/entities/_base/_base/entity.py +0 -4
  4. digitalhub/entities/_base/context/entity.py +1 -1
  5. digitalhub/entities/_base/entity/entity.py +0 -8
  6. digitalhub/entities/_base/executable/entity.py +161 -79
  7. digitalhub/entities/_base/material/entity.py +7 -23
  8. digitalhub/entities/_base/material/utils.py +28 -0
  9. digitalhub/entities/_base/unversioned/entity.py +1 -1
  10. digitalhub/entities/_base/versioned/entity.py +1 -1
  11. digitalhub/entities/_commons/enums.py +0 -31
  12. digitalhub/entities/_constructors/_resources.py +151 -0
  13. digitalhub/entities/_constructors/name.py +18 -0
  14. digitalhub/entities/_processors/base/__init__.py +3 -0
  15. digitalhub/entities/_processors/{base.py → base/crud.py} +7 -227
  16. digitalhub/entities/_processors/base/import_export.py +122 -0
  17. digitalhub/entities/_processors/base/processor.py +302 -0
  18. digitalhub/entities/_processors/base/special_ops.py +108 -0
  19. digitalhub/entities/_processors/context/__init__.py +3 -0
  20. digitalhub/entities/_processors/context/crud.py +654 -0
  21. digitalhub/entities/_processors/context/import_export.py +242 -0
  22. digitalhub/entities/_processors/context/material.py +123 -0
  23. digitalhub/entities/_processors/context/processor.py +400 -0
  24. digitalhub/entities/_processors/context/special_ops.py +476 -0
  25. digitalhub/entities/_processors/processors.py +12 -0
  26. digitalhub/entities/_processors/utils.py +2 -1
  27. digitalhub/entities/artifact/crud.py +45 -41
  28. digitalhub/entities/dataitem/crud.py +45 -37
  29. digitalhub/entities/dataitem/table/entity.py +5 -6
  30. digitalhub/entities/function/crud.py +47 -43
  31. digitalhub/entities/model/_base/entity.py +3 -23
  32. digitalhub/entities/model/crud.py +45 -39
  33. digitalhub/entities/project/_base/entity.py +45 -134
  34. digitalhub/entities/project/crud.py +13 -42
  35. digitalhub/entities/run/_base/builder.py +0 -4
  36. digitalhub/entities/run/_base/entity.py +4 -60
  37. digitalhub/entities/run/crud.py +61 -40
  38. digitalhub/entities/secret/_base/entity.py +1 -5
  39. digitalhub/entities/secret/crud.py +14 -42
  40. digitalhub/entities/task/_base/builder.py +0 -4
  41. digitalhub/entities/task/_base/entity.py +1 -1
  42. digitalhub/entities/task/crud.py +47 -44
  43. digitalhub/entities/trigger/_base/entity.py +1 -5
  44. digitalhub/entities/trigger/crud.py +51 -43
  45. digitalhub/entities/workflow/crud.py +47 -40
  46. digitalhub/factory/registry.py +0 -24
  47. digitalhub/stores/client/_base/enums.py +39 -0
  48. digitalhub/stores/client/_base/key_builder.py +1 -1
  49. digitalhub/stores/client/_base/params_builder.py +48 -0
  50. digitalhub/stores/client/dhcore/api_builder.py +2 -1
  51. digitalhub/stores/client/dhcore/client.py +67 -73
  52. digitalhub/stores/client/dhcore/configurator.py +5 -28
  53. digitalhub/stores/client/dhcore/error_parser.py +0 -4
  54. digitalhub/stores/client/dhcore/params_builder.py +130 -75
  55. digitalhub/stores/client/local/api_builder.py +1 -1
  56. digitalhub/stores/client/local/params_builder.py +18 -41
  57. digitalhub/stores/credentials/configurator.py +0 -24
  58. digitalhub/stores/credentials/handler.py +0 -12
  59. digitalhub/stores/credentials/store.py +0 -4
  60. digitalhub/stores/data/_base/store.py +0 -16
  61. digitalhub/stores/data/builder.py +0 -4
  62. digitalhub/stores/data/remote/store.py +0 -4
  63. digitalhub/stores/data/s3/configurator.py +0 -8
  64. digitalhub/stores/data/s3/store.py +8 -17
  65. digitalhub/stores/data/sql/configurator.py +0 -8
  66. digitalhub/stores/data/sql/store.py +0 -4
  67. digitalhub/stores/readers/data/factory.py +0 -8
  68. digitalhub/stores/readers/data/pandas/reader.py +0 -16
  69. digitalhub/utils/io_utils.py +0 -4
  70. {digitalhub-0.14.0b2.dist-info → digitalhub-0.14.0b4.dist-info}/METADATA +1 -1
  71. {digitalhub-0.14.0b2.dist-info → digitalhub-0.14.0b4.dist-info}/RECORD +74 -62
  72. digitalhub/entities/_processors/context.py +0 -1499
  73. {digitalhub-0.14.0b2.dist-info → digitalhub-0.14.0b4.dist-info}/WHEEL +0 -0
  74. {digitalhub-0.14.0b2.dist-info → digitalhub-0.14.0b4.dist-info}/licenses/AUTHORS +0 -0
  75. {digitalhub-0.14.0b2.dist-info → digitalhub-0.14.0b4.dist-info}/licenses/LICENSE +0 -0
@@ -7,7 +7,7 @@ from __future__ import annotations
7
7
  import typing
8
8
 
9
9
  from digitalhub.entities._commons.enums import EntityTypes
10
- from digitalhub.entities._processors.context import context_processor
10
+ from digitalhub.entities._processors.processors import context_processor
11
11
 
12
12
  if typing.TYPE_CHECKING:
13
13
  from digitalhub.entities.workflow._base.entity import Workflow
@@ -75,7 +75,6 @@ def get_workflow(
75
75
  identifier: str,
76
76
  project: str | None = None,
77
77
  entity_id: str | None = None,
78
- **kwargs,
79
78
  ) -> Workflow:
80
79
  """
81
80
  Get object from backend.
@@ -88,8 +87,6 @@ def get_workflow(
88
87
  Project name.
89
88
  entity_id : str
90
89
  Entity ID.
91
- **kwargs : dict
92
- Parameters to pass to the API call.
93
90
 
94
91
  Returns
95
92
  -------
@@ -99,9 +96,7 @@ def get_workflow(
99
96
  Examples
100
97
  --------
101
98
  Using entity key:
102
- >>> obj = get_workflow(
103
- ... "store://my-workflow-key"
104
- ... )
99
+ >>> obj = get_workflow("store://my-workflow-key")
105
100
 
106
101
  Using entity name:
107
102
  >>> obj = get_workflow("my-workflow-name"
@@ -109,18 +104,16 @@ def get_workflow(
109
104
  >>> entity_id="my-workflow-id")
110
105
  """
111
106
  return context_processor.read_context_entity(
112
- identifier,
107
+ identifier=identifier,
113
108
  entity_type=ENTITY_TYPE,
114
109
  project=project,
115
110
  entity_id=entity_id,
116
- **kwargs,
117
111
  )
118
112
 
119
113
 
120
114
  def get_workflow_versions(
121
115
  identifier: str,
122
116
  project: str | None = None,
123
- **kwargs,
124
117
  ) -> list[Workflow]:
125
118
  """
126
119
  Get object versions from backend.
@@ -131,8 +124,6 @@ def get_workflow_versions(
131
124
  Entity key (store://...) or entity name.
132
125
  project : str
133
126
  Project name.
134
- **kwargs : dict
135
- Parameters to pass to the API call.
136
127
 
137
128
  Returns
138
129
  -------
@@ -142,23 +133,30 @@ def get_workflow_versions(
142
133
  Examples
143
134
  --------
144
135
  Using entity key:
145
- >>> obj = get_workflow_versions(
146
- ... "store://my-workflow-key"
147
- ... )
136
+ >>> obj = get_workflow_versions("store://my-workflow-key")
148
137
 
149
138
  Using entity name:
150
139
  >>> obj = get_workflow_versions("my-workflow-name"
151
140
  >>> project="my-project")
152
141
  """
153
142
  return context_processor.read_context_entity_versions(
154
- identifier,
143
+ identifier=identifier,
155
144
  entity_type=ENTITY_TYPE,
156
145
  project=project,
157
- **kwargs,
158
146
  )
159
147
 
160
148
 
161
- def list_workflows(project: str, **kwargs) -> list[Workflow]:
149
+ def list_workflows(
150
+ project: str,
151
+ q: str | None = None,
152
+ name: str | None = None,
153
+ kind: str | None = None,
154
+ user: str | None = None,
155
+ state: str | None = None,
156
+ created: str | None = None,
157
+ updated: str | None = None,
158
+ version: str | None = None,
159
+ ) -> list[Workflow]:
162
160
  """
163
161
  List all latest version objects from backend.
164
162
 
@@ -166,8 +164,22 @@ def list_workflows(project: str, **kwargs) -> list[Workflow]:
166
164
  ----------
167
165
  project : str
168
166
  Project name.
169
- **kwargs : dict
170
- Parameters to pass to the API call.
167
+ q : str
168
+ Query string to filter objects.
169
+ name : str
170
+ Object name.
171
+ kind : str
172
+ Kind of the object.
173
+ user : str
174
+ User that created the object.
175
+ state : str
176
+ Object state.
177
+ created : str
178
+ Creation date filter.
179
+ updated : str
180
+ Update date filter.
181
+ version : str
182
+ Object version, default is latest.
171
183
 
172
184
  Returns
173
185
  -------
@@ -176,14 +188,19 @@ def list_workflows(project: str, **kwargs) -> list[Workflow]:
176
188
 
177
189
  Examples
178
190
  --------
179
- >>> objs = list_workflows(
180
- ... project="my-project"
181
- ... )
191
+ >>> objs = list_workflows(project="my-project")
182
192
  """
183
193
  return context_processor.list_context_entities(
184
194
  project=project,
185
195
  entity_type=ENTITY_TYPE,
186
- **kwargs,
196
+ q=q,
197
+ name=name,
198
+ kind=kind,
199
+ user=user,
200
+ state=state,
201
+ created=created,
202
+ updated=updated,
203
+ version=version,
187
204
  )
188
205
 
189
206
 
@@ -214,9 +231,7 @@ def import_workflow(
214
231
 
215
232
  Examples
216
233
  --------
217
- >>> obj = import_workflow(
218
- ... "my-workflow.yaml"
219
- ... )
234
+ >>> obj = import_workflow("my-workflow.yaml")
220
235
  """
221
236
  return context_processor.import_executable_entity(file, key, reset_id, context)
222
237
 
@@ -237,9 +252,7 @@ def load_workflow(file: str) -> Workflow:
237
252
 
238
253
  Examples
239
254
  --------
240
- >>> obj = load_workflow(
241
- ... "my-workflow.yaml"
242
- ... )
255
+ >>> obj = load_workflow("my-workflow.yaml")
243
256
  """
244
257
  return context_processor.load_executable_entity(file)
245
258
 
@@ -260,11 +273,7 @@ def update_workflow(entity: Workflow) -> Workflow:
260
273
 
261
274
  Examples
262
275
  --------
263
- >>> obj = (
264
- ... update_workflow(
265
- ... obj
266
- ... )
267
- ... )
276
+ >>> obj = update_workflow(obj)
268
277
  """
269
278
  return context_processor.update_context_entity(
270
279
  project=entity.project,
@@ -294,7 +303,8 @@ def delete_workflow(
294
303
  entity_id : str
295
304
  Entity ID.
296
305
  delete_all_versions : bool
297
- Delete all versions of the named entity. If True, use entity name instead of entity key as identifier.
306
+ Delete all versions of the named entity.
307
+ If True, use entity name instead of entity key as identifier.
298
308
  cascade : bool
299
309
  Cascade delete.
300
310
  **kwargs : dict
@@ -308,9 +318,7 @@ def delete_workflow(
308
318
  Examples
309
319
  --------
310
320
  If delete_all_versions is False:
311
- >>> obj = delete_workflow(
312
- ... "store://my-workflow-key"
313
- ... )
321
+ >>> obj = delete_workflow("store://my-workflow-key")
314
322
 
315
323
  Otherwise:
316
324
  >>> obj = delete_workflow("workflow-name",
@@ -324,5 +332,4 @@ def delete_workflow(
324
332
  entity_id=entity_id,
325
333
  delete_all_versions=delete_all_versions,
326
334
  cascade=cascade,
327
- **kwargs,
328
335
  )
@@ -47,10 +47,6 @@ class BuilderRegistry:
47
47
  builder : type[EntityBuilder] | type[RuntimeEntityBuilder]
48
48
  The builder class to register. It will be instantiated immediately.
49
49
 
50
- Returns
51
- -------
52
- None
53
-
54
50
  Raises
55
51
  ------
56
52
  BuilderError
@@ -71,10 +67,6 @@ class BuilderRegistry:
71
67
  builder : type[RuntimeBuilder]
72
68
  The builder class to register. It will be instantiated immediately.
73
69
 
74
- Returns
75
- -------
76
- None
77
-
78
70
  Raises
79
71
  ------
80
72
  BuilderError
@@ -141,10 +133,6 @@ class BuilderRegistry:
141
133
  def _ensure_entities_registered(self) -> None:
142
134
  """
143
135
  Ensure core entities are registered on-demand.
144
-
145
- Returns
146
- -------
147
- None
148
136
  """
149
137
  if self._entities_registered:
150
138
  return
@@ -160,10 +148,6 @@ class BuilderRegistry:
160
148
 
161
149
  Imports the core entities module and registers all entity
162
150
  builders with the registry.
163
-
164
- Returns
165
- -------
166
- None
167
151
  """
168
152
  try:
169
153
  module = import_module(FactoryEnum.REG_ENTITIES.value)
@@ -178,10 +162,6 @@ class BuilderRegistry:
178
162
  def _ensure_runtimes_registered(self) -> None:
179
163
  """
180
164
  Ensure runtime entities are registered on-demand.
181
-
182
- Returns
183
- -------
184
- None
185
165
  """
186
166
  if self._runtimes_registered:
187
167
  return
@@ -197,10 +177,6 @@ class BuilderRegistry:
197
177
 
198
178
  Imports each runtime package and registers its entity and runtime
199
179
  builders with the registry.
200
-
201
- Returns
202
- -------
203
- None
204
180
  """
205
181
  try:
206
182
  for package in list_runtimes():
@@ -0,0 +1,39 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from __future__ import annotations
6
+
7
+ from enum import Enum
8
+
9
+
10
+ class ApiCategories(Enum):
11
+ """
12
+ API categories.
13
+ """
14
+
15
+ BASE = "base"
16
+ CONTEXT = "context"
17
+
18
+
19
+ class BackendOperations(Enum):
20
+ """
21
+ Backend operations.
22
+ """
23
+
24
+ CREATE = "create"
25
+ READ = "read"
26
+ READ_ALL_VERSIONS = "read_all_versions"
27
+ UPDATE = "update"
28
+ DELETE = "delete"
29
+ DELETE_ALL_VERSIONS = "delete_all_versions"
30
+ LIST = "list"
31
+ LIST_FIRST = "list_first"
32
+ STOP = "stop"
33
+ RESUME = "resume"
34
+ DATA = "data"
35
+ FILES = "files"
36
+ LOGS = "logs"
37
+ SEARCH = "search"
38
+ SHARE = "share"
39
+ METRICS = "metrics"
@@ -6,7 +6,7 @@ from __future__ import annotations
6
6
 
7
7
  from abc import abstractmethod
8
8
 
9
- from digitalhub.entities._commons.enums import ApiCategories
9
+ from digitalhub.stores.client._base.enums import ApiCategories
10
10
 
11
11
 
12
12
  class ClientKeyBuilder:
@@ -5,6 +5,7 @@
5
5
  from __future__ import annotations
6
6
 
7
7
  from abc import abstractmethod
8
+ from typing import Any
8
9
 
9
10
 
10
11
  class ClientParametersBuilder:
@@ -32,3 +33,50 @@ class ClientParametersBuilder:
32
33
  dict
33
34
  The formatted parameters for the client call.
34
35
  """
36
+
37
+ @staticmethod
38
+ def _ensure_params(**kwargs) -> dict:
39
+ """
40
+ Initialize parameter dictionary with query parameters structure.
41
+
42
+ Ensures parameter dictionary has 'params' key for HTTP query parameters,
43
+ guaranteeing consistent structure for all parameter building methods.
44
+
45
+ Parameters
46
+ ----------
47
+ **kwargs : dict
48
+ Keyword arguments to format. May be empty or contain various
49
+ parameters for API operations.
50
+
51
+ Returns
52
+ -------
53
+ dict
54
+ Parameters dictionary with guaranteed 'params' key containing
55
+ empty dict if not already present.
56
+ """
57
+ if "params" not in kwargs:
58
+ kwargs["params"] = {}
59
+ return kwargs
60
+
61
+ @staticmethod
62
+ def _add_param(key: str, value: Any | None, **kwargs) -> dict:
63
+ """
64
+ Add a single query parameter to kwargs.
65
+
66
+ Parameters
67
+ ----------
68
+ key : str
69
+ Parameter key.
70
+ value : Any
71
+ Parameter value.
72
+ **kwargs : dict
73
+ Keyword arguments to format. May be empty or contain various
74
+ parameters for API operations.
75
+
76
+ Returns
77
+ -------
78
+ dict
79
+ Parameters dictionary with added key-value pair in 'params'.
80
+ """
81
+ kwargs["params"][key] = value
82
+ return kwargs
@@ -4,8 +4,8 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- from digitalhub.entities._commons.enums import ApiCategories, BackendOperations
8
7
  from digitalhub.stores.client._base.api_builder import ClientApiBuilder
8
+ from digitalhub.stores.client._base.enums import ApiCategories, BackendOperations
9
9
  from digitalhub.utils.exceptions import BackendError
10
10
 
11
11
  API_BASE = "/api/v1"
@@ -100,6 +100,7 @@ class ClientDHCoreApiBuilder(ClientApiBuilder):
100
100
  if operation in (
101
101
  BackendOperations.CREATE.value,
102
102
  BackendOperations.LIST.value,
103
+ BackendOperations.DELETE_ALL_VERSIONS.value,
103
104
  ):
104
105
  return f"{API_CONTEXT}/{project}/{entity_type}"
105
106
  elif operation in (
@@ -29,6 +29,9 @@ MAX_API_LEVEL = 20
29
29
  MIN_API_LEVEL = 14
30
30
  LIB_VERSION = 14
31
31
 
32
+ # Default timeout for requests (in seconds)
33
+ DEFAULT_TIMEOUT = 60
34
+
32
35
 
33
36
  class ClientDHCore(Client):
34
37
  """
@@ -58,16 +61,6 @@ class ClientDHCore(Client):
58
61
  Parses backend responses and raises appropriate exceptions.
59
62
  _configurator : ClientDHCoreConfigurator
60
63
  Manages client configuration and authentication.
61
-
62
- Examples
63
- --------
64
- >>> from digitalhub.stores.client.api import (
65
- ... get_client,
66
- ... )
67
- >>> client = get_client(
68
- ... local=False
69
- ... )
70
- >>> # Client is now ready for API operations
71
64
  """
72
65
 
73
66
  def __init__(self, config: dict | None = None) -> None:
@@ -79,21 +72,17 @@ class ClientDHCore(Client):
79
72
  config : dict, optional
80
73
  DHCore environment configuration. If None, loads from environment
81
74
  variables and configuration files.
82
-
83
- Returns
84
- -------
85
- None
86
75
  """
87
76
  super().__init__()
88
77
 
89
78
  # API builder
90
- self._api_builder = ClientDHCoreApiBuilder()
79
+ self._api_builder: ClientDHCoreApiBuilder = ClientDHCoreApiBuilder()
91
80
 
92
81
  # Key builder
93
- self._key_builder = ClientDHCoreKeyBuilder()
82
+ self._key_builder: ClientDHCoreKeyBuilder = ClientDHCoreKeyBuilder()
94
83
 
95
84
  # Parameters builder
96
- self._params_builder = ClientDHCoreParametersBuilder()
85
+ self._params_builder: ClientDHCoreParametersBuilder = ClientDHCoreParametersBuilder()
97
86
 
98
87
  # Error parser
99
88
  self._error_parser = ErrorParser()
@@ -132,9 +121,7 @@ class ClientDHCore(Client):
132
121
  ClientError
133
122
  If there are client-side configuration issues.
134
123
  """
135
- if "headers" not in kwargs:
136
- kwargs["headers"] = {}
137
- kwargs["headers"]["Content-Type"] = "application/json"
124
+ kwargs = self._set_application_json_header(**kwargs)
138
125
  kwargs["data"] = dump_json(obj)
139
126
  return self._prepare_call("POST", api, **kwargs)
140
127
 
@@ -192,9 +179,7 @@ class ClientDHCore(Client):
192
179
  EntityNotExistsError
193
180
  If the object to update does not exist.
194
181
  """
195
- if "headers" not in kwargs:
196
- kwargs["headers"] = {}
197
- kwargs["headers"]["Content-Type"] = "application/json"
182
+ kwargs = self._set_application_json_header(**kwargs)
198
183
  kwargs["data"] = dump_json(obj)
199
184
  return self._prepare_call("PUT", api, **kwargs)
200
185
 
@@ -254,22 +239,17 @@ class ClientDHCore(Client):
254
239
  BackendError
255
240
  If the backend returns an error response.
256
241
  """
257
- if "params" not in kwargs:
258
- kwargs["params"] = {}
259
-
260
- start_page = 0
261
- if "page" not in kwargs["params"]:
262
- kwargs["params"]["page"] = start_page
242
+ kwargs = self._params_builder.set_pagination(partial=True, **kwargs)
263
243
 
264
244
  objects = []
265
245
  while True:
266
246
  resp = self._prepare_call("GET", api, **kwargs)
267
247
  contents = resp["content"]
268
248
  total_pages = resp["totalPages"]
269
- if not contents or kwargs["params"]["page"] >= total_pages:
270
- break
271
249
  objects.extend(contents)
272
- kwargs["params"]["page"] += 1
250
+ if not contents or self._params_builder.read_page_number(**kwargs) >= (total_pages - 1):
251
+ break
252
+ self._params_builder.increment_page_number(**kwargs)
273
253
 
274
254
  return objects
275
255
 
@@ -328,29 +308,16 @@ class ClientDHCore(Client):
328
308
  BackendError
329
309
  If the backend returns an error response.
330
310
  """
331
- if "params" not in kwargs:
332
- kwargs["params"] = {}
333
-
334
- start_page = 0
335
- if "page" not in kwargs["params"]:
336
- kwargs["params"]["page"] = start_page
337
-
338
- if "size" not in kwargs["params"]:
339
- kwargs["params"]["size"] = 10
340
-
341
- # Add sorting
342
- if "sort" not in kwargs["params"]:
343
- kwargs["params"]["sort"] = "metadata.updated,DESC"
344
-
311
+ kwargs = self._params_builder.set_pagination(**kwargs)
345
312
  objects_with_highlights: list[dict] = []
346
313
  while True:
347
314
  resp = self._prepare_call("GET", api, **kwargs)
348
315
  contents = resp["content"]
349
316
  total_pages = resp["totalPages"]
350
- if not contents or kwargs["params"]["page"] >= total_pages:
351
- break
352
317
  objects_with_highlights.extend(contents)
353
- kwargs["params"]["page"] += 1
318
+ if not contents or self._params_builder.read_page_number(**kwargs) >= (total_pages - 1):
319
+ break
320
+ self._params_builder.increment_page_number(**kwargs)
354
321
 
355
322
  objects = []
356
323
  for obj in objects_with_highlights:
@@ -395,25 +362,6 @@ class ClientDHCore(Client):
395
362
  full_kwargs = self._configurator.get_auth_parameters(kwargs)
396
363
  return self._make_call(call_type, url, **full_kwargs)
397
364
 
398
- def _build_url(self, api: str) -> str:
399
- """
400
- Build the complete URL for an API call.
401
-
402
- Combines the configured endpoint with the API path to create
403
- the full URL for the HTTP request. Automatically removes leading
404
- slashes from the API path to ensure proper URL construction.
405
-
406
- Parameters
407
- ----------
408
- api : str
409
- The API endpoint path. Leading slashes are automatically handled.
410
-
411
- Returns
412
- -------
413
- str
414
- The complete URL for the API call.
415
- """
416
-
417
365
  def _build_url(self, api: str) -> str:
418
366
  """
419
367
  Build complete URL for API call.
@@ -468,7 +416,7 @@ class ClientDHCore(Client):
468
416
  If authentication fails and token refresh not possible.
469
417
  """
470
418
  # Call the API
471
- response = request(call_type, url, timeout=60, **kwargs)
419
+ response = request(call_type, url, timeout=DEFAULT_TIMEOUT, **kwargs)
472
420
 
473
421
  # Evaluate DHCore API version
474
422
  self._check_core_version(response)
@@ -494,10 +442,6 @@ class ClientDHCore(Client):
494
442
  response : Response
495
443
  HTTP response containing X-Api-Level header.
496
444
 
497
- Returns
498
- -------
499
- None
500
-
501
445
  Raises
502
446
  ------
503
447
  ClientError
@@ -557,3 +501,53 @@ class ClientDHCore(Client):
557
501
  False, indicating this client communicates with remote DHCore backend.
558
502
  """
559
503
  return False
504
+
505
+ ##############################
506
+ # Utility methods
507
+ ##############################
508
+
509
+ @staticmethod
510
+ def _ensure_header(**kwargs) -> dict:
511
+ """
512
+ Initialize header dictionary.
513
+
514
+ Ensures parameter dictionary has 'headers' key for HTTP headers,
515
+ guaranteeing consistent structure for all parameter building methods.
516
+
517
+ Parameters
518
+ ----------
519
+ **kwargs : dict
520
+ Keyword arguments to format. May be empty or contain various
521
+ parameters for API operations.
522
+
523
+ Returns
524
+ -------
525
+ dict
526
+ Headers dictionary with guaranteed 'headers' key containing
527
+ empty dict if not already present.
528
+ """
529
+ if "headers" not in kwargs:
530
+ kwargs["headers"] = {}
531
+ return kwargs
532
+
533
+ def _set_application_json_header(self, **kwargs) -> dict:
534
+ """
535
+ Set Content-Type header to application/json.
536
+
537
+ Ensures that the 'Content-Type' header is set to 'application/json'
538
+ for requests that require JSON payloads.
539
+
540
+ Parameters
541
+ ----------
542
+ **kwargs : dict
543
+ Keyword arguments to format. May be empty or contain various
544
+ parameters for API operations.
545
+
546
+ Returns
547
+ -------
548
+ dict
549
+ Headers dictionary with 'Content-Type' set to 'application/json'.
550
+ """
551
+ kwargs = self._ensure_header(**kwargs)
552
+ kwargs["headers"]["Content-Type"] = "application/json"
553
+ return kwargs