digitalhub 0.13.4__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 +2 -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.4.dist-info → digitalhub-0.14.0.dist-info}/METADATA +3 -2
  103. {digitalhub-0.13.4.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.4.dist-info → digitalhub-0.14.0.dist-info}/WHEEL +0 -0
  115. {digitalhub-0.13.4.dist-info → digitalhub-0.14.0.dist-info}/licenses/AUTHORS +0 -0
  116. {digitalhub-0.13.4.dist-info → digitalhub-0.14.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,102 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from __future__ import annotations
6
+
7
+ import typing
8
+ from warnings import warn
9
+
10
+ from requests.exceptions import JSONDecodeError
11
+
12
+ from digitalhub.stores.client.dhcore.error_parser import ErrorParser
13
+ from digitalhub.utils.exceptions import BackendError, ClientError
14
+
15
+ if typing.TYPE_CHECKING:
16
+ from requests import Response
17
+
18
+
19
+ # API levels that are supported
20
+ MAX_API_LEVEL = 20
21
+ MIN_API_LEVEL = 14
22
+ LIB_VERSION = 14
23
+
24
+
25
+ class ResponseProcessor:
26
+ """
27
+ Processes and validates HTTP responses from DHCore backend.
28
+
29
+ Handles API version validation, error parsing, and response body parsing
30
+ to dictionary. Supports API versions {MIN_API_LEVEL} to {MAX_API_LEVEL}.
31
+ """
32
+
33
+ def __init__(self) -> None:
34
+ self._error_parser = ErrorParser()
35
+
36
+ def process(self, response: Response) -> dict:
37
+ """
38
+ Process HTTP response with validation and parsing.
39
+
40
+ Performs API version compatibility check, error parsing for failed
41
+ responses, and JSON deserialization.
42
+
43
+ Parameters
44
+ ----------
45
+ response : Response
46
+ HTTP response object from backend.
47
+
48
+ Returns
49
+ -------
50
+ dict
51
+ Parsed response body as dictionary.
52
+ """
53
+ self._check_api_version(response)
54
+ self._error_parser.parse(response)
55
+ return self._parse_json(response)
56
+
57
+ def _check_api_version(self, response: Response) -> None:
58
+ """
59
+ Validate DHCore API version compatibility.
60
+
61
+ Checks backend API version against supported range and warns if backend
62
+ version is newer than library. Supported: {MIN_API_LEVEL} to {MAX_API_LEVEL}.
63
+
64
+ Parameters
65
+ ----------
66
+ response : Response
67
+ HTTP response containing X-Api-Level header.
68
+ """
69
+ if "X-Api-Level" not in response.headers:
70
+ return
71
+
72
+ core_api_level = int(response.headers["X-Api-Level"])
73
+ if not (MIN_API_LEVEL <= core_api_level <= MAX_API_LEVEL):
74
+ raise ClientError("Backend API level not supported.")
75
+
76
+ if LIB_VERSION < core_api_level:
77
+ warn("Backend API level is higher than library version. You should consider updating the library.")
78
+
79
+ @staticmethod
80
+ def _parse_json(response: Response) -> dict:
81
+ """
82
+ Parse HTTP response body to dictionary.
83
+
84
+ Converts JSON response to Python dictionary, treating empty responses
85
+ as valid and returning empty dict.
86
+
87
+ Parameters
88
+ ----------
89
+ response : Response
90
+ HTTP response object to parse.
91
+
92
+ Returns
93
+ -------
94
+ dict
95
+ Parsed response body as dictionary, or empty dict if body is empty.
96
+ """
97
+ try:
98
+ return response.json()
99
+ except JSONDecodeError:
100
+ if response.text == "":
101
+ return {}
102
+ raise BackendError("Backend response could not be parsed.")
@@ -4,91 +4,25 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- import os
8
7
  import typing
9
8
 
10
9
  from digitalhub.stores.client.api import get_client
11
- from digitalhub.stores.credentials.enums import CredsEnvVar
12
10
 
13
11
  if typing.TYPE_CHECKING:
14
- from digitalhub.stores.client.dhcore.client import ClientDHCore
15
-
16
-
17
- def set_dhcore_env(
18
- endpoint: str | None = None,
19
- user: str | None = None,
20
- password: str | None = None,
21
- access_token: str | None = None,
22
- refresh_token: str | None = None,
23
- client_id: str | None = None,
24
- ) -> None:
25
- """
26
- Function to set environment variables for DHCore config.
27
-
28
- Sets the environment variables for DHCore configuration and
29
- reloads the client configurator to use the new settings.
30
- Note that if the environment variable is already set, it
31
- will be overwritten.
32
-
33
- Parameters
34
- ----------
35
- endpoint : str, optional
36
- The endpoint URL of the DHCore backend.
37
- user : str, optional
38
- The username for basic authentication.
39
- password : str, optional
40
- The password for basic authentication.
41
- access_token : str, optional
42
- The OAuth2 access token.
43
- refresh_token : str, optional
44
- The OAuth2 refresh token.
45
- client_id : str, optional
46
- The OAuth2 client identifier.
47
-
48
- Returns
49
- -------
50
- None
51
-
52
- Notes
53
- -----
54
- After setting the environment variables, this function automatically
55
- reloads the client configurator to apply the new configuration.
56
- """
57
- if endpoint is not None:
58
- os.environ[CredsEnvVar.DHCORE_ENDPOINT.value] = endpoint
59
- if user is not None:
60
- os.environ[CredsEnvVar.DHCORE_USER.value] = user
61
- if password is not None:
62
- os.environ[CredsEnvVar.DHCORE_PASSWORD.value] = password
63
- if access_token is not None:
64
- os.environ[CredsEnvVar.DHCORE_ACCESS_TOKEN.value] = access_token
65
- if refresh_token is not None:
66
- os.environ[CredsEnvVar.DHCORE_REFRESH_TOKEN.value] = refresh_token
67
- if client_id is not None:
68
- os.environ[CredsEnvVar.DHCORE_CLIENT_ID.value] = client_id
69
-
70
- client: ClientDHCore = get_client(local=False)
71
- client._configurator.load_env_vars()
12
+ pass
72
13
 
73
14
 
74
15
  def refresh_token() -> None:
75
16
  """
76
- Function to refresh the OAuth2 access token.
17
+ Refresh the current OAuth2 access token.
77
18
 
78
- Attempts to refresh the current OAuth2 access token using the
79
- refresh token stored in the client configuration. This function
80
- requires that the client be configured with OAuth2 authentication.
19
+ Uses the refresh token stored in client configuration to obtain a new
20
+ access token. Requires OAuth2 authentication configuration.
81
21
 
82
- Returns
83
- -------
84
- None
85
22
 
86
23
  Raises
87
24
  ------
88
25
  ClientError
89
- If the client is not properly configured or if the token
90
- refresh fails.
26
+ If client not properly configured or token refresh fails.
91
27
  """
92
- client: ClientDHCore = get_client(local=False)
93
- client._configurator.check_config()
94
- client._configurator.refresh_credentials()
28
+ get_client(local=False).refresh_token()
@@ -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.stores.client.local.enums import LocalClientVar
10
10
  from digitalhub.utils.exceptions import BackendError
11
11
 
@@ -207,7 +207,16 @@ class ClientLocal(Client):
207
207
 
208
208
  else:
209
209
  name = obj.get("name", entity_id)
210
- self._db[entity_type][name][entity_id] = obj
210
+ container = self._db[entity_type][name]
211
+ container[entity_id] = obj
212
+
213
+ # Keep the "latest" pointer consistent when updating the latest entity
214
+ try:
215
+ if container.get("latest", {}).get("id") == entity_id:
216
+ container["latest"] = obj
217
+ except AttributeError:
218
+ # In case "latest" is malformed, ignore and continue
219
+ pass
211
220
 
212
221
  except KeyError:
213
222
  msg = self._format_msg(3, entity_type=entity_type, entity_id=entity_id)
@@ -253,55 +262,51 @@ class ClientLocal(Client):
253
262
 
254
263
  # Name is optional and extracted from kwargs
255
264
  # "params": {"name": <name>}
256
- name = kwargs.get("params", {}).get("name")
265
+ name_param = kwargs.get("params", {}).get("name")
257
266
 
258
- # Delete by name
259
- if entity_id is None and name is not None:
260
- self._db[entity_type].pop(name, None)
267
+ # Delete by name (remove the whole named container)
268
+ if entity_id is None and name_param is not None:
269
+ self._db[entity_type].pop(name_param, None)
261
270
  return {"deleted": True}
262
271
 
263
272
  # Delete by id
264
- for _, v in self._db[entity_type].items():
273
+ found_name: str | None = None
274
+ container: dict | None = None
275
+ for n, v in self._db[entity_type].items():
265
276
  if entity_id in v:
266
- v.pop(entity_id)
267
-
268
- # Handle latest
269
- if v["latest"]["id"] == entity_id:
270
- name = v["latest"].get("name", entity_id)
271
- v.pop("latest")
272
- reset_latest = True
277
+ found_name = n
278
+ container = v
273
279
  break
274
280
  else:
275
281
  raise KeyError
276
282
 
277
- if name is not None:
278
- # Pop name if empty
279
- if not self._db[entity_type][name]:
280
- self._db[entity_type].pop(name)
281
-
282
- # Handle latest
283
- elif reset_latest:
284
- latest_uuid = None
285
- latest_date = None
286
- for k, v in self._db[entity_type][name].items():
287
- # Get created from metadata. If tzinfo is None, set it to UTC
288
- # If created is not in ISO format, use fallback
289
- fallback = datetime.fromtimestamp(0, timezone.utc)
290
- try:
291
- current_created = datetime.fromisoformat(v.get("metadata", {}).get("created"))
292
- if current_created.tzinfo is None:
293
- current_created = current_created.replace(tzinfo=timezone.utc)
294
- except ValueError:
295
- current_created = fallback
296
-
297
- # Update latest date and uuid
298
- if latest_date is None or current_created > latest_date:
299
- latest_uuid = k
300
- latest_date = current_created
301
-
302
- # Set new latest
303
- if latest_uuid is not None:
304
- self._db[entity_type][name]["latest"] = self._db[entity_type][name][latest_uuid]
283
+ # Remove the entity from the container
284
+ assert container is not None # for type checkers
285
+ container.pop(entity_id)
286
+
287
+ # Handle latest pointer if needed
288
+ if container.get("latest", {}).get("id") == entity_id:
289
+ # Remove stale latest
290
+ container.pop("latest", None)
291
+ reset_latest = True
292
+
293
+ # If container is now empty, drop it entirely
294
+ if not container:
295
+ assert found_name is not None
296
+ self._db[entity_type].pop(found_name, None)
297
+ # Otherwise, recompute latest if required
298
+ elif reset_latest:
299
+ latest_uuid = None
300
+ latest_date = None
301
+ for k, v in container.items():
302
+ # Parse creation time from metadata; tolerate various formats
303
+ current_created = self._safe_parse_created(v)
304
+ if latest_date is None or current_created > latest_date:
305
+ latest_uuid = k
306
+ latest_date = current_created
307
+
308
+ if latest_uuid is not None:
309
+ container["latest"] = container[latest_uuid]
305
310
 
306
311
  except KeyError:
307
312
  msg = self._format_msg(3, entity_type=entity_type, entity_id=entity_id)
@@ -330,7 +335,10 @@ class ClientLocal(Client):
330
335
  # "params": {"name": <name>}
331
336
  name = kwargs.get("params", {}).get("name")
332
337
  if name is not None:
333
- return [self._db[entity_type][name]["latest"]]
338
+ try:
339
+ return [self._db[entity_type][name]["latest"]]
340
+ except KeyError:
341
+ return []
334
342
 
335
343
  try:
336
344
  # If no name is provided, get latest objects
@@ -343,7 +351,7 @@ class ClientLocal(Client):
343
351
  if kind is not None:
344
352
  listed_objects = [obj for obj in listed_objects if obj["kind"] == kind]
345
353
 
346
- # If function is provided, return objects by function
354
+ # If function/task is provided, return objects by function/task
347
355
  spec_params = ["function", "task"]
348
356
  for i in spec_params:
349
357
  p = kwargs.get("params", {}).get(i)
@@ -489,14 +497,15 @@ class ClientLocal(Client):
489
497
  """
490
498
  # Deepcopy to avoid modifying the original object
491
499
  project = deepcopy(obj)
492
- spec = project.get("spec", {})
500
+ # Ensure spec exists on the returned project
501
+ spec = project.setdefault("spec", {})
493
502
 
494
503
  # Get all entities associated with the project specs
495
504
  projects_entities = [k for k, _ in self._db.items() if k not in ["projects", "runs", "tasks"]]
496
505
 
497
506
  for entity_type in projects_entities:
498
507
  # Get all objects of the entity type for the project
499
- objs = self._db[entity_type]
508
+ objs = self._db.get(entity_type, {})
500
509
 
501
510
  # Set empty list
502
511
  spec[entity_type] = []
@@ -521,6 +530,29 @@ class ClientLocal(Client):
521
530
 
522
531
  return project
523
532
 
533
+ @staticmethod
534
+ def _safe_parse_created(obj: dict) -> datetime:
535
+ """
536
+ Safely parse the creation datetime of an object.
537
+
538
+ - Accepts ISO format with optional 'Z'.
539
+ - If tzinfo is missing, assume UTC.
540
+ - Falls back to epoch if missing/invalid.
541
+ """
542
+ created_raw = obj.get("metadata", {}).get("created")
543
+ fallback = datetime.fromtimestamp(0, timezone.utc)
544
+ if not created_raw or not isinstance(created_raw, str):
545
+ return fallback
546
+ try:
547
+ # Support trailing 'Z'
548
+ ts = created_raw.replace("Z", "+00:00")
549
+ dt = datetime.fromisoformat(ts)
550
+ if dt.tzinfo is None:
551
+ dt = dt.replace(tzinfo=timezone.utc)
552
+ return dt
553
+ except Exception:
554
+ return fallback
555
+
524
556
  ##############################
525
557
  # Utils
526
558
  ##############################
@@ -538,9 +570,9 @@ class ClientLocal(Client):
538
570
  ----------
539
571
  error_code : int
540
572
  Error code identifying the type of error.
541
- entity_type : str, optional
573
+ entity_type : str
542
574
  Entity type that caused the error.
543
- entity_id : str, optional
575
+ entity_id : str
544
576
  Entity ID that caused the error.
545
577
 
546
578
  Returns
@@ -4,7 +4,7 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- from digitalhub.entities._commons.enums import ApiCategories, BackendOperations
7
+ from digitalhub.stores.client._base.enums import ApiCategories, BackendOperations
8
8
  from digitalhub.stores.client._base.params_builder import ClientParametersBuilder
9
9
 
10
10
 
@@ -51,10 +51,13 @@ class ClientLocalParametersBuilder(ClientParametersBuilder):
51
51
  dict
52
52
  Parameters formatted.
53
53
  """
54
- kwargs = self._set_params(**kwargs)
54
+ kwargs = self._ensure_params(**kwargs)
55
+
56
+ # Handle delete
55
57
  if operation == BackendOperations.DELETE.value:
56
58
  if (cascade := kwargs.pop("cascade", None)) is not None:
57
- kwargs["params"]["cascade"] = str(cascade).lower()
59
+ kwargs = self._add_param("cascade", str(cascade).lower(), **kwargs)
60
+
58
61
  return kwargs
59
62
 
60
63
  def build_parameters_context(self, operation: str, **kwargs) -> dict:
@@ -73,48 +76,22 @@ class ClientLocalParametersBuilder(ClientParametersBuilder):
73
76
  dict
74
77
  Parameters formatted.
75
78
  """
76
- kwargs = self._set_params(**kwargs)
79
+ kwargs = self._ensure_params(**kwargs)
77
80
 
78
- # Handle read
81
+ # Handle read all versions
79
82
  if operation == BackendOperations.READ_ALL_VERSIONS.value:
80
- kwargs["params"]["versions"] = "all"
81
- kwargs["params"]["name"] = kwargs.pop("entity_name")
83
+ kwargs = self._add_param("versions", "all", **kwargs)
84
+ kwargs = self._add_param("name", kwargs.pop("name"), **kwargs)
85
+
82
86
  # Handle delete
83
87
  elif operation == BackendOperations.DELETE.value:
84
- # Handle cascade
85
- if cascade := kwargs.pop("cascade", None) is not None:
86
- kwargs["params"]["cascade"] = str(cascade).lower()
87
-
88
- # Handle delete all versions
89
- entity_id = kwargs.pop("entity_id")
90
- entity_name = kwargs.pop("entity_name")
91
- if not kwargs.pop("delete_all_versions", False):
92
- if entity_id is None:
93
- raise ValueError(
94
- "If `delete_all_versions` is False, `entity_id` must be provided,"
95
- " either as an argument or in key `identifier`.",
96
- )
97
- else:
98
- kwargs["params"]["name"] = entity_name
99
- return kwargs
100
-
101
- @staticmethod
102
- def _set_params(**kwargs) -> dict:
103
- """
104
- Format params parameter.
88
+ if (cascade := kwargs.pop("cascade", None)) is not None:
89
+ kwargs = self._add_param("cascade", str(cascade).lower(), **kwargs)
105
90
 
106
- Parameters
107
- ----------
108
- **kwargs : dict
109
- Keyword arguments.
91
+ # Handle delete all versions
92
+ elif operation == BackendOperations.DELETE_ALL_VERSIONS.value:
93
+ if (cascade := kwargs.pop("cascade", None)) is not None:
94
+ kwargs = self._add_param("cascade", str(cascade).lower(), **kwargs)
95
+ kwargs = self._add_param("name", kwargs.pop("name"), **kwargs)
110
96
 
111
- Returns
112
- -------
113
- dict
114
- Parameters with initialized params.
115
- """
116
- if not kwargs:
117
- kwargs = {}
118
- if "params" not in kwargs:
119
- kwargs["params"] = {}
120
97
  return kwargs
@@ -15,10 +15,6 @@ def set_current_profile(environment: str) -> None:
15
15
  ----------
16
16
  environment : str
17
17
  Name of the credentials profile to set.
18
-
19
- Returns
20
- -------
21
- None
22
18
  """
23
19
  creds_handler.set_current_profile(environment)
24
20
 
@@ -53,30 +53,20 @@ class Configurator:
53
53
  def load_configs(self) -> None:
54
54
  """
55
55
  Load the configuration from both environment and file sources.
56
-
57
- Returns
58
- -------
59
- None
60
56
  """
61
57
  self.load_env_vars()
62
58
  self.load_file_vars()
63
59
 
64
60
  @abstractmethod
65
- def load_env_vars(self) -> None:
66
- ...
61
+ def load_env_vars(self) -> None: ...
67
62
 
68
63
  @abstractmethod
69
- def load_file_vars(self) -> None:
70
- ...
64
+ def load_file_vars(self) -> None: ...
71
65
 
72
66
  def check_config(self) -> None:
73
67
  """
74
68
  Check if the current profile has changed and reload
75
69
  the file credentials if needed.
76
-
77
- Returns
78
- -------
79
- None
80
70
  """
81
71
  if (current := self._creds_handler.get_current_profile()) != self._current_profile:
82
72
  self.load_file_vars()
@@ -121,10 +111,6 @@ class Configurator:
121
111
  """
122
112
  Attempt to change the origin of credentials.
123
113
  Raise error if already evaluated.
124
-
125
- Returns
126
- -------
127
- None
128
114
  """
129
115
  try:
130
116
  self.change_origin()
@@ -134,10 +120,6 @@ class Configurator:
134
120
  def change_origin(self) -> None:
135
121
  """
136
122
  Change the origin of credentials from env to file or vice versa.
137
-
138
- Returns
139
- -------
140
- None
141
123
  """
142
124
  if self._changed_origin:
143
125
  raise ConfigError("Origin has already been changed.")
@@ -149,10 +131,6 @@ class Configurator:
149
131
  def change_to_file(self) -> None:
150
132
  """
151
133
  Set the credentials origin to file.
152
-
153
- Returns
154
- -------
155
- None
156
134
  """
157
135
  if self._origin == self._env:
158
136
  self._changed_origin = True
@@ -161,10 +139,6 @@ class Configurator:
161
139
  def change_to_env(self) -> None:
162
140
  """
163
141
  Set the credentials origin to environment.
164
-
165
- Returns
166
- -------
167
- None
168
142
  """
169
143
  if self._origin == self._file:
170
144
  self._changed_origin = True
@@ -66,3 +66,6 @@ class CredsEnvVar(Enum):
66
66
  DHCORE_REFRESH_TOKEN = "DHCORE_REFRESH_TOKEN"
67
67
  DHCORE_PERSONAL_ACCESS_TOKEN = "DHCORE_PERSONAL_ACCESS_TOKEN"
68
68
  DHCORE_WORKFLOW_IMAGE = "DHCORE_WORKFLOW_IMAGE"
69
+
70
+ # OAUTH2
71
+ OAUTH2_TOKEN_ENDPOINT = "OAUTH2_TOKEN_ENDPOINT"
@@ -64,10 +64,6 @@ class CredentialHandler:
64
64
  ----------
65
65
  creds_set : str
66
66
  Name of the credentials profile to set.
67
-
68
- Returns
69
- -------
70
- None
71
67
  """
72
68
  self._profile = creds_set
73
69
  set_current_profile(creds_set)
@@ -127,10 +123,6 @@ class CredentialHandler:
127
123
  ----------
128
124
  creds : dict
129
125
  Credentials to write.
130
-
131
- Returns
132
- -------
133
- None
134
126
  """
135
127
  write_config(creds, self._profile)
136
128
 
@@ -148,10 +140,6 @@ class CredentialHandler:
148
140
  The origin of the credentials ('env' or 'file').
149
141
  creds : dict
150
142
  Credentials to set.
151
-
152
- Returns
153
- -------
154
- None
155
143
  """
156
144
  self._creds_store.set_credentials(self._profile, origin, creds)
157
145
 
@@ -92,9 +92,6 @@ def write_config(creds: dict, environment: str) -> None:
92
92
  environment : str
93
93
  Name of the credentials profile/environment.
94
94
 
95
- Returns
96
- -------
97
- None
98
95
 
99
96
  Raises
100
97
  ------
@@ -129,9 +126,6 @@ def set_current_profile(environment: str) -> None:
129
126
  environment : str
130
127
  Name of the credentials profile to set as current.
131
128
 
132
- Returns
133
- -------
134
- None
135
129
 
136
130
  Raises
137
131
  ------
@@ -146,19 +140,3 @@ def set_current_profile(environment: str) -> None:
146
140
 
147
141
  except Exception as e:
148
142
  raise ClientError(f"Failed to write env file: {e}")
149
-
150
-
151
- def read_env_from_file() -> str | None:
152
- """
153
- Read the current credentials profile name from the .dhcore.ini file.
154
-
155
- Returns
156
- -------
157
- str or None
158
- Name of the current credentials profile, or None if not found.
159
- """
160
- try:
161
- cfg = load_file()
162
- return cfg["DEFAULT"]["current_environment"]
163
- except Exception:
164
- return None
@@ -42,10 +42,6 @@ class CredentialsStore:
42
42
  Origin of the credentials ('env' or 'file').
43
43
  credentials : dict of str to Any
44
44
  Dictionary of credentials to set.
45
-
46
- Returns
47
- -------
48
- None
49
45
  """
50
46
  if origin == CredsOrigin.ENV.value:
51
47
  for key, value in credentials.items():