digitalhub 0.13.0b3__py3-none-any.whl → 0.14.9__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.
- digitalhub/__init__.py +3 -8
- digitalhub/context/api.py +43 -6
- digitalhub/context/builder.py +1 -5
- digitalhub/context/context.py +28 -13
- digitalhub/entities/_base/_base/entity.py +0 -15
- digitalhub/entities/_base/context/entity.py +1 -4
- digitalhub/entities/_base/entity/builder.py +5 -5
- digitalhub/entities/_base/entity/entity.py +0 -8
- digitalhub/entities/_base/executable/entity.py +195 -87
- digitalhub/entities/_base/material/entity.py +11 -23
- digitalhub/entities/_base/material/utils.py +28 -4
- digitalhub/entities/_base/runtime_entity/builder.py +53 -18
- digitalhub/entities/_base/unversioned/entity.py +1 -1
- digitalhub/entities/_base/versioned/entity.py +1 -1
- digitalhub/entities/_commons/enums.py +1 -31
- digitalhub/entities/_commons/metrics.py +64 -30
- digitalhub/entities/_commons/utils.py +119 -30
- digitalhub/entities/_constructors/_resources.py +151 -0
- digitalhub/entities/{_base/entity/_constructors → _constructors}/name.py +18 -0
- digitalhub/entities/_processors/base/crud.py +381 -0
- digitalhub/entities/_processors/base/import_export.py +118 -0
- digitalhub/entities/_processors/base/processor.py +299 -0
- digitalhub/entities/_processors/base/special_ops.py +104 -0
- digitalhub/entities/_processors/context/crud.py +652 -0
- digitalhub/entities/_processors/context/import_export.py +242 -0
- digitalhub/entities/_processors/context/material.py +123 -0
- digitalhub/entities/_processors/context/processor.py +400 -0
- digitalhub/entities/_processors/context/special_ops.py +476 -0
- digitalhub/entities/_processors/processors.py +12 -0
- digitalhub/entities/_processors/utils.py +38 -102
- digitalhub/entities/artifact/crud.py +58 -22
- digitalhub/entities/artifact/utils.py +28 -13
- digitalhub/entities/builders.py +2 -0
- digitalhub/entities/dataitem/crud.py +63 -20
- digitalhub/entities/dataitem/table/entity.py +27 -22
- digitalhub/entities/dataitem/utils.py +82 -32
- digitalhub/entities/function/_base/entity.py +3 -6
- digitalhub/entities/function/crud.py +55 -24
- digitalhub/entities/model/_base/entity.py +62 -20
- digitalhub/entities/model/crud.py +59 -23
- digitalhub/entities/model/mlflow/utils.py +29 -20
- digitalhub/entities/model/utils.py +28 -13
- digitalhub/entities/project/_base/builder.py +0 -6
- digitalhub/entities/project/_base/entity.py +337 -164
- digitalhub/entities/project/_base/spec.py +4 -4
- digitalhub/entities/project/crud.py +28 -71
- digitalhub/entities/project/utils.py +7 -3
- digitalhub/entities/run/_base/builder.py +0 -4
- digitalhub/entities/run/_base/entity.py +70 -63
- digitalhub/entities/run/crud.py +79 -26
- digitalhub/entities/secret/_base/entity.py +1 -5
- digitalhub/entities/secret/crud.py +31 -28
- digitalhub/entities/task/_base/builder.py +0 -4
- digitalhub/entities/task/_base/entity.py +5 -5
- digitalhub/entities/task/_base/models.py +13 -16
- digitalhub/entities/task/crud.py +61 -29
- digitalhub/entities/trigger/_base/entity.py +1 -5
- digitalhub/entities/trigger/crud.py +89 -30
- digitalhub/entities/workflow/_base/entity.py +3 -8
- digitalhub/entities/workflow/crud.py +55 -24
- digitalhub/factory/entity.py +283 -0
- digitalhub/factory/enums.py +18 -0
- digitalhub/factory/registry.py +197 -0
- digitalhub/factory/runtime.py +44 -0
- digitalhub/factory/utils.py +3 -54
- digitalhub/runtimes/_base.py +2 -2
- digitalhub/stores/client/{dhcore/api_builder.py → api_builder.py} +3 -3
- digitalhub/stores/client/builder.py +19 -31
- digitalhub/stores/client/client.py +322 -0
- digitalhub/stores/client/configurator.py +408 -0
- digitalhub/stores/client/enums.py +50 -0
- digitalhub/stores/client/{dhcore/error_parser.py → error_parser.py} +0 -4
- digitalhub/stores/client/header_manager.py +61 -0
- digitalhub/stores/client/http_handler.py +152 -0
- digitalhub/stores/client/{_base/key_builder.py → key_builder.py} +14 -14
- digitalhub/stores/client/params_builder.py +330 -0
- digitalhub/stores/client/response_processor.py +102 -0
- digitalhub/stores/client/utils.py +35 -0
- digitalhub/stores/{credentials → configurator}/api.py +5 -9
- digitalhub/stores/configurator/configurator.py +123 -0
- digitalhub/stores/{credentials → configurator}/enums.py +27 -10
- digitalhub/stores/configurator/handler.py +213 -0
- digitalhub/stores/{credentials → configurator}/ini_module.py +31 -22
- digitalhub/stores/data/_base/store.py +0 -20
- digitalhub/stores/data/api.py +5 -7
- digitalhub/stores/data/builder.py +53 -27
- digitalhub/stores/data/local/store.py +0 -103
- digitalhub/stores/data/remote/store.py +0 -4
- digitalhub/stores/data/s3/configurator.py +39 -77
- digitalhub/stores/data/s3/store.py +57 -37
- digitalhub/stores/data/sql/configurator.py +66 -46
- digitalhub/stores/data/sql/store.py +171 -104
- digitalhub/stores/readers/data/factory.py +0 -8
- digitalhub/stores/readers/data/pandas/reader.py +9 -19
- digitalhub/utils/file_utils.py +0 -17
- digitalhub/utils/generic_utils.py +1 -14
- digitalhub/utils/git_utils.py +0 -8
- digitalhub/utils/io_utils.py +0 -12
- digitalhub/utils/store_utils.py +44 -0
- {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/METADATA +5 -4
- {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/RECORD +112 -113
- {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/WHEEL +1 -1
- digitalhub/entities/_commons/types.py +0 -9
- digitalhub/entities/_processors/base.py +0 -531
- digitalhub/entities/_processors/context.py +0 -1299
- digitalhub/entities/task/_base/utils.py +0 -22
- digitalhub/factory/factory.py +0 -381
- digitalhub/stores/client/_base/api_builder.py +0 -34
- digitalhub/stores/client/_base/client.py +0 -243
- digitalhub/stores/client/_base/params_builder.py +0 -34
- digitalhub/stores/client/api.py +0 -36
- digitalhub/stores/client/dhcore/client.py +0 -613
- digitalhub/stores/client/dhcore/configurator.py +0 -675
- digitalhub/stores/client/dhcore/enums.py +0 -34
- digitalhub/stores/client/dhcore/key_builder.py +0 -62
- digitalhub/stores/client/dhcore/models.py +0 -40
- digitalhub/stores/client/dhcore/params_builder.py +0 -278
- digitalhub/stores/client/dhcore/utils.py +0 -94
- digitalhub/stores/client/local/api_builder.py +0 -116
- digitalhub/stores/client/local/client.py +0 -573
- digitalhub/stores/client/local/enums.py +0 -15
- digitalhub/stores/client/local/key_builder.py +0 -62
- digitalhub/stores/client/local/params_builder.py +0 -120
- digitalhub/stores/credentials/__init__.py +0 -3
- digitalhub/stores/credentials/configurator.py +0 -210
- digitalhub/stores/credentials/handler.py +0 -176
- digitalhub/stores/credentials/store.py +0 -81
- digitalhub/stores/data/enums.py +0 -15
- digitalhub/stores/data/s3/utils.py +0 -78
- /digitalhub/entities/{_base/entity/_constructors → _constructors}/__init__.py +0 -0
- /digitalhub/entities/{_base/entity/_constructors → _constructors}/metadata.py +0 -0
- /digitalhub/entities/{_base/entity/_constructors → _constructors}/spec.py +0 -0
- /digitalhub/entities/{_base/entity/_constructors → _constructors}/status.py +0 -0
- /digitalhub/entities/{_base/entity/_constructors → _constructors}/uuid.py +0 -0
- /digitalhub/{stores/client/_base → entities/_processors/base}/__init__.py +0 -0
- /digitalhub/{stores/client/dhcore → entities/_processors/context}/__init__.py +0 -0
- /digitalhub/stores/{client/local → configurator}/__init__.py +0 -0
- {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/licenses/AUTHORS +0 -0
- {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
from digitalhub.entities._commons.enums import ApiCategories
|
|
7
|
+
from digitalhub.stores.client.enums import ApiCategories
|
|
10
8
|
|
|
11
9
|
|
|
12
10
|
class ClientKeyBuilder:
|
|
@@ -36,7 +34,6 @@ class ClientKeyBuilder:
|
|
|
36
34
|
return self.base_entity_key(*args, **kwargs)
|
|
37
35
|
return self.context_entity_key(*args, **kwargs)
|
|
38
36
|
|
|
39
|
-
@abstractmethod
|
|
40
37
|
def base_entity_key(self, entity_id: str) -> str:
|
|
41
38
|
"""
|
|
42
39
|
Build for base entity key.
|
|
@@ -44,15 +41,15 @@ class ClientKeyBuilder:
|
|
|
44
41
|
Parameters
|
|
45
42
|
----------
|
|
46
43
|
entity_id : str
|
|
47
|
-
|
|
44
|
+
Entity id.
|
|
48
45
|
|
|
49
46
|
Returns
|
|
50
47
|
-------
|
|
51
48
|
str
|
|
52
|
-
|
|
49
|
+
Key.
|
|
53
50
|
"""
|
|
51
|
+
return f"store://{entity_id}"
|
|
54
52
|
|
|
55
|
-
@abstractmethod
|
|
56
53
|
def context_entity_key(
|
|
57
54
|
self,
|
|
58
55
|
project: str,
|
|
@@ -67,18 +64,21 @@ class ClientKeyBuilder:
|
|
|
67
64
|
Parameters
|
|
68
65
|
----------
|
|
69
66
|
project : str
|
|
70
|
-
|
|
67
|
+
Project name.
|
|
71
68
|
entity_type : str
|
|
72
|
-
|
|
69
|
+
Entity type.
|
|
73
70
|
entity_kind : str
|
|
74
|
-
|
|
71
|
+
Entity kind.
|
|
75
72
|
entity_name : str
|
|
76
|
-
|
|
77
|
-
entity_id : str
|
|
78
|
-
|
|
73
|
+
Entity name.
|
|
74
|
+
entity_id : str
|
|
75
|
+
Entity ID.
|
|
79
76
|
|
|
80
77
|
Returns
|
|
81
78
|
-------
|
|
82
79
|
str
|
|
83
|
-
|
|
80
|
+
Key.
|
|
84
81
|
"""
|
|
82
|
+
if entity_id is None:
|
|
83
|
+
return f"store://{project}/{entity_type}/{entity_kind}/{entity_name}"
|
|
84
|
+
return f"store://{project}/{entity_type}/{entity_kind}/{entity_name}:{entity_id}"
|
|
@@ -0,0 +1,330 @@
|
|
|
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 typing import Any
|
|
8
|
+
|
|
9
|
+
from digitalhub.stores.client.enums import ApiCategories, BackendOperations
|
|
10
|
+
|
|
11
|
+
DEFAULT_START_PAGE = 0
|
|
12
|
+
DEFAULT_SIZE = 25
|
|
13
|
+
DEFAULT_SORT = "metadata.updated,DESC"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ClientParametersBuilder:
|
|
17
|
+
"""
|
|
18
|
+
Parameter builder for DHCore client API calls.
|
|
19
|
+
|
|
20
|
+
Constructs HTTP request parameters for DHCore API operations, handling
|
|
21
|
+
parameter formats and query structures for both base-level operations
|
|
22
|
+
(project management) and context-level operations (entity operations
|
|
23
|
+
within projects). Supports query parameter formatting, search filter
|
|
24
|
+
construction for Solr-based searches, cascade deletion options,
|
|
25
|
+
versioning parameters, and entity sharing parameters.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def build_parameters(self, category: str, operation: str, **kwargs) -> dict:
|
|
29
|
+
"""
|
|
30
|
+
Build HTTP request parameters for DHCore API calls.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
category : str
|
|
35
|
+
API category: 'base' for project-level operations or 'context'
|
|
36
|
+
for entity operations within projects.
|
|
37
|
+
operation : str
|
|
38
|
+
Specific API operation (create, read, update, delete, list, search, etc.).
|
|
39
|
+
**kwargs : dict
|
|
40
|
+
Raw parameters to transform including entity identifiers, filter
|
|
41
|
+
criteria, pagination options, etc.
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
dict
|
|
46
|
+
Formatted parameters dictionary with 'params' key for query parameters
|
|
47
|
+
and other request-specific parameters.
|
|
48
|
+
"""
|
|
49
|
+
if category == ApiCategories.BASE.value:
|
|
50
|
+
return self.build_parameters_base(operation, **kwargs)
|
|
51
|
+
return self.build_parameters_context(operation, **kwargs)
|
|
52
|
+
|
|
53
|
+
def build_parameters_base(self, operation: str, **kwargs) -> dict:
|
|
54
|
+
"""
|
|
55
|
+
Constructs HTTP request parameters for project operations.
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
operation : str
|
|
60
|
+
API operation.
|
|
61
|
+
**kwargs : dict
|
|
62
|
+
Operation-specific parameters.
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
dict
|
|
66
|
+
Formatted parameters with 'params' containing query parameters.
|
|
67
|
+
"""
|
|
68
|
+
kwargs = self._ensure_params(**kwargs)
|
|
69
|
+
|
|
70
|
+
# Handle delete
|
|
71
|
+
if operation == BackendOperations.DELETE.value:
|
|
72
|
+
if (cascade := kwargs.pop("cascade", None)) is not None:
|
|
73
|
+
kwargs = self._add_param("cascade", str(cascade).lower(), **kwargs)
|
|
74
|
+
|
|
75
|
+
# Handle share
|
|
76
|
+
elif operation == BackendOperations.SHARE.value:
|
|
77
|
+
kwargs = self._add_param("user", kwargs.pop("user"), **kwargs)
|
|
78
|
+
if kwargs.pop("unshare", False):
|
|
79
|
+
kwargs = self._add_param("id", kwargs.pop("id"), **kwargs)
|
|
80
|
+
|
|
81
|
+
return kwargs
|
|
82
|
+
|
|
83
|
+
def build_parameters_context(self, operation: str, **kwargs) -> dict:
|
|
84
|
+
"""
|
|
85
|
+
Constructs HTTP request parameters for entity management and search within
|
|
86
|
+
projects.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
operation : str
|
|
91
|
+
API operation.
|
|
92
|
+
**kwargs : dict
|
|
93
|
+
Operation-specific parameters.
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
dict
|
|
98
|
+
Formatted parameters with 'params'.
|
|
99
|
+
"""
|
|
100
|
+
kwargs = self._ensure_params(**kwargs)
|
|
101
|
+
|
|
102
|
+
# Handle read
|
|
103
|
+
if operation == BackendOperations.READ.value:
|
|
104
|
+
if (name := kwargs.pop("name", None)) is not None:
|
|
105
|
+
kwargs = self._add_param("name", name, **kwargs)
|
|
106
|
+
|
|
107
|
+
# Handle read all versions
|
|
108
|
+
elif operation == BackendOperations.READ_ALL_VERSIONS.value:
|
|
109
|
+
kwargs = self._add_param("versions", "all", **kwargs)
|
|
110
|
+
kwargs = self._add_param("name", kwargs.pop("name"), **kwargs)
|
|
111
|
+
|
|
112
|
+
# Handle list
|
|
113
|
+
elif operation == BackendOperations.LIST.value:
|
|
114
|
+
possible_list_params = [
|
|
115
|
+
"q",
|
|
116
|
+
"name",
|
|
117
|
+
"kind",
|
|
118
|
+
"user",
|
|
119
|
+
"state",
|
|
120
|
+
"created",
|
|
121
|
+
"updated",
|
|
122
|
+
"versions",
|
|
123
|
+
"function",
|
|
124
|
+
"workflow",
|
|
125
|
+
"action",
|
|
126
|
+
"task",
|
|
127
|
+
]
|
|
128
|
+
list_params = {k: kwargs.get(k, None) for k in possible_list_params}
|
|
129
|
+
list_params = self._filter_none_params(**list_params)
|
|
130
|
+
for k, v in list_params.items():
|
|
131
|
+
kwargs = self._add_param(k, v, **kwargs)
|
|
132
|
+
for k in possible_list_params:
|
|
133
|
+
kwargs.pop(k, None)
|
|
134
|
+
|
|
135
|
+
# Handle delete
|
|
136
|
+
elif operation == BackendOperations.DELETE.value:
|
|
137
|
+
if (cascade := kwargs.pop("cascade", None)) is not None:
|
|
138
|
+
kwargs = self._add_param("cascade", str(cascade).lower(), **kwargs)
|
|
139
|
+
|
|
140
|
+
elif operation == BackendOperations.DELETE_ALL_VERSIONS.value:
|
|
141
|
+
if (cascade := kwargs.pop("cascade", None)) is not None:
|
|
142
|
+
kwargs = self._add_param("cascade", str(cascade).lower(), **kwargs)
|
|
143
|
+
kwargs = self._add_param("name", kwargs.pop("name"), **kwargs)
|
|
144
|
+
|
|
145
|
+
# Handle search
|
|
146
|
+
elif operation == BackendOperations.SEARCH.value:
|
|
147
|
+
# Handle fq
|
|
148
|
+
if (fq := kwargs.pop("fq", None)) is not None:
|
|
149
|
+
kwargs = self._add_param("fq", fq, **kwargs)
|
|
150
|
+
|
|
151
|
+
# Add search query
|
|
152
|
+
if (query := kwargs.pop("query", None)) is not None:
|
|
153
|
+
kwargs = self._add_param("q", query, **kwargs)
|
|
154
|
+
|
|
155
|
+
# Add search filters
|
|
156
|
+
fq = []
|
|
157
|
+
|
|
158
|
+
# Entity types
|
|
159
|
+
if (entity_types := kwargs.pop("entity_types", None)) is not None:
|
|
160
|
+
if not isinstance(entity_types, list):
|
|
161
|
+
entity_types = [entity_types]
|
|
162
|
+
if len(entity_types) == 1:
|
|
163
|
+
entity_types = entity_types[0]
|
|
164
|
+
else:
|
|
165
|
+
entity_types = " OR ".join(entity_types)
|
|
166
|
+
fq.append(f"type:({entity_types})")
|
|
167
|
+
|
|
168
|
+
# Name
|
|
169
|
+
if (name := kwargs.pop("name", None)) is not None:
|
|
170
|
+
fq.append(f'metadata.name:"{name}"')
|
|
171
|
+
|
|
172
|
+
# Kind
|
|
173
|
+
if (kind := kwargs.pop("kind", None)) is not None:
|
|
174
|
+
fq.append(f'kind:"{kind}"')
|
|
175
|
+
|
|
176
|
+
# Time
|
|
177
|
+
created = kwargs.pop("created", None)
|
|
178
|
+
updated = kwargs.pop("updated", None)
|
|
179
|
+
created = created if created is not None else "*"
|
|
180
|
+
updated = updated if updated is not None else "*"
|
|
181
|
+
fq.append(f"metadata.updated:[{created} TO {updated}]")
|
|
182
|
+
|
|
183
|
+
# Description
|
|
184
|
+
if (description := kwargs.pop("description", None)) is not None:
|
|
185
|
+
fq.append(f'metadata.description:"{description}"')
|
|
186
|
+
|
|
187
|
+
# Labels
|
|
188
|
+
if (labels := kwargs.pop("labels", None)) is not None:
|
|
189
|
+
if len(labels) == 1:
|
|
190
|
+
labels = labels[0]
|
|
191
|
+
else:
|
|
192
|
+
labels = " AND ".join(labels)
|
|
193
|
+
fq.append(f"metadata.labels:({labels})")
|
|
194
|
+
|
|
195
|
+
# Add filters
|
|
196
|
+
kwargs = self._add_param("fq", fq, **kwargs)
|
|
197
|
+
|
|
198
|
+
return kwargs
|
|
199
|
+
|
|
200
|
+
def set_pagination(self, partial: bool = False, **kwargs) -> dict:
|
|
201
|
+
"""
|
|
202
|
+
Ensure pagination parameters are set in kwargs.
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
**kwargs : dict
|
|
207
|
+
Keyword arguments to format. May be empty or contain various
|
|
208
|
+
parameters for API operations.
|
|
209
|
+
|
|
210
|
+
Returns
|
|
211
|
+
-------
|
|
212
|
+
dict
|
|
213
|
+
Pagination parameters set in 'params' of kwargs.
|
|
214
|
+
"""
|
|
215
|
+
kwargs = self._ensure_params(**kwargs)
|
|
216
|
+
|
|
217
|
+
if "page" not in kwargs["params"]:
|
|
218
|
+
kwargs["params"]["page"] = DEFAULT_START_PAGE
|
|
219
|
+
|
|
220
|
+
if partial:
|
|
221
|
+
return kwargs
|
|
222
|
+
|
|
223
|
+
if "size" not in kwargs["params"]:
|
|
224
|
+
kwargs["params"]["size"] = DEFAULT_SIZE
|
|
225
|
+
|
|
226
|
+
if "sort" not in kwargs["params"]:
|
|
227
|
+
kwargs["params"]["sort"] = DEFAULT_SORT
|
|
228
|
+
|
|
229
|
+
return kwargs
|
|
230
|
+
|
|
231
|
+
@staticmethod
|
|
232
|
+
def _ensure_params(**kwargs) -> dict:
|
|
233
|
+
"""
|
|
234
|
+
Initialize parameter dictionary with query parameters structure.
|
|
235
|
+
|
|
236
|
+
Ensures parameter dictionary has 'params' key for HTTP query parameters,
|
|
237
|
+
guaranteeing consistent structure for all parameter building methods.
|
|
238
|
+
|
|
239
|
+
Parameters
|
|
240
|
+
----------
|
|
241
|
+
**kwargs : dict
|
|
242
|
+
Keyword arguments to format. May be empty or contain various
|
|
243
|
+
parameters for API operations.
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
dict
|
|
248
|
+
Parameters dictionary with guaranteed 'params' key containing
|
|
249
|
+
empty dict if not already present.
|
|
250
|
+
"""
|
|
251
|
+
if "params" not in kwargs:
|
|
252
|
+
kwargs["params"] = {}
|
|
253
|
+
return kwargs
|
|
254
|
+
|
|
255
|
+
@staticmethod
|
|
256
|
+
def _add_param(key: str, value: Any | None, **kwargs) -> dict:
|
|
257
|
+
"""
|
|
258
|
+
Add a single query parameter to kwargs.
|
|
259
|
+
|
|
260
|
+
Parameters
|
|
261
|
+
----------
|
|
262
|
+
key : str
|
|
263
|
+
Parameter key.
|
|
264
|
+
value : Any
|
|
265
|
+
Parameter value.
|
|
266
|
+
**kwargs : dict
|
|
267
|
+
Keyword arguments to format. May be empty or contain various
|
|
268
|
+
parameters for API operations.
|
|
269
|
+
|
|
270
|
+
Returns
|
|
271
|
+
-------
|
|
272
|
+
dict
|
|
273
|
+
Parameters dictionary with added key-value pair in 'params'.
|
|
274
|
+
"""
|
|
275
|
+
kwargs["params"][key] = value
|
|
276
|
+
return kwargs
|
|
277
|
+
|
|
278
|
+
@staticmethod
|
|
279
|
+
def read_page_number(**kwargs) -> int:
|
|
280
|
+
"""
|
|
281
|
+
Read current page number from kwargs.
|
|
282
|
+
|
|
283
|
+
Parameters
|
|
284
|
+
----------
|
|
285
|
+
**kwargs : dict
|
|
286
|
+
Keyword arguments to format. May be empty or contain various
|
|
287
|
+
parameters for API operations.
|
|
288
|
+
|
|
289
|
+
Returns
|
|
290
|
+
-------
|
|
291
|
+
int
|
|
292
|
+
Current page number.
|
|
293
|
+
"""
|
|
294
|
+
return kwargs["params"]["page"]
|
|
295
|
+
|
|
296
|
+
@staticmethod
|
|
297
|
+
def increment_page_number(**kwargs) -> dict:
|
|
298
|
+
"""
|
|
299
|
+
Increment page number in kwargs.
|
|
300
|
+
|
|
301
|
+
Parameters
|
|
302
|
+
----------
|
|
303
|
+
**kwargs : dict
|
|
304
|
+
Keyword arguments to format. May be empty or contain various
|
|
305
|
+
parameters for API operations.
|
|
306
|
+
|
|
307
|
+
Returns
|
|
308
|
+
-------
|
|
309
|
+
dict
|
|
310
|
+
Parameters dictionary with incremented 'page' number in 'params'.
|
|
311
|
+
"""
|
|
312
|
+
kwargs["params"]["page"] += 1
|
|
313
|
+
return kwargs
|
|
314
|
+
|
|
315
|
+
@staticmethod
|
|
316
|
+
def _filter_none_params(**kwargs) -> dict:
|
|
317
|
+
"""
|
|
318
|
+
Filter out None values from kwargs.
|
|
319
|
+
|
|
320
|
+
Parameters
|
|
321
|
+
----------
|
|
322
|
+
**kwargs : dict
|
|
323
|
+
Keyword arguments to filter.
|
|
324
|
+
|
|
325
|
+
Returns
|
|
326
|
+
-------
|
|
327
|
+
dict
|
|
328
|
+
Filtered kwargs.
|
|
329
|
+
"""
|
|
330
|
+
return {k: v for k, v in kwargs.items() if v is not None}
|
|
@@ -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.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.")
|
|
@@ -0,0 +1,35 @@
|
|
|
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 digitalhub.stores.client.builder import get_client
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def refresh_token() -> None:
|
|
11
|
+
"""
|
|
12
|
+
Refresh the current OAuth2 access token.
|
|
13
|
+
|
|
14
|
+
Uses the refresh token stored in client configuration to obtain a new
|
|
15
|
+
access token. Requires OAuth2 authentication configuration.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
Raises
|
|
19
|
+
------
|
|
20
|
+
ClientError
|
|
21
|
+
If client not properly configured or token refresh fails.
|
|
22
|
+
"""
|
|
23
|
+
get_client().refresh_token()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_credentials_and_config() -> dict:
|
|
27
|
+
"""
|
|
28
|
+
Get current client credentials and configuration.
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
dict
|
|
33
|
+
Current client credentials and configuration details.
|
|
34
|
+
"""
|
|
35
|
+
return get_client().get_credentials_and_config()
|
|
@@ -4,23 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
-
from digitalhub.stores.
|
|
7
|
+
from digitalhub.stores.configurator.configurator import configurator
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def set_current_profile(
|
|
10
|
+
def set_current_profile(profile: str) -> None:
|
|
11
11
|
"""
|
|
12
12
|
Set the current credentials profile.
|
|
13
13
|
|
|
14
14
|
Parameters
|
|
15
15
|
----------
|
|
16
|
-
|
|
16
|
+
profile : str
|
|
17
17
|
Name of the credentials profile to set.
|
|
18
|
-
|
|
19
|
-
Returns
|
|
20
|
-
-------
|
|
21
|
-
None
|
|
22
18
|
"""
|
|
23
|
-
|
|
19
|
+
configurator.set_current_profile(profile)
|
|
24
20
|
|
|
25
21
|
|
|
26
22
|
def get_current_profile() -> str:
|
|
@@ -32,4 +28,4 @@ def get_current_profile() -> str:
|
|
|
32
28
|
str
|
|
33
29
|
Name of the current credentials profile.
|
|
34
30
|
"""
|
|
35
|
-
return
|
|
31
|
+
return configurator.get_current_profile()
|
|
@@ -0,0 +1,123 @@
|
|
|
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 digitalhub.stores.configurator.handler import ConfigurationHandler
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Configurator:
|
|
11
|
+
"""
|
|
12
|
+
Configurator class for configuration and credentials management.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self._handler = ConfigurationHandler()
|
|
17
|
+
self._reload_from_env = False
|
|
18
|
+
|
|
19
|
+
##############################
|
|
20
|
+
# Configuration
|
|
21
|
+
##############################
|
|
22
|
+
|
|
23
|
+
def get_configuration(self) -> dict:
|
|
24
|
+
"""
|
|
25
|
+
Retrieve the current configuration.
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
dict
|
|
30
|
+
Dictionary of configuration variables.
|
|
31
|
+
"""
|
|
32
|
+
return self._handler.get_configuration()
|
|
33
|
+
|
|
34
|
+
##############################
|
|
35
|
+
# Credentials
|
|
36
|
+
##############################
|
|
37
|
+
|
|
38
|
+
def get_credentials(self) -> dict:
|
|
39
|
+
"""
|
|
40
|
+
Retrieve the current credentials.
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
dict
|
|
45
|
+
Dictionary of credentials.
|
|
46
|
+
"""
|
|
47
|
+
return self._handler.get_credentials()
|
|
48
|
+
|
|
49
|
+
def eval_retry(self) -> bool:
|
|
50
|
+
"""
|
|
51
|
+
Evaluate credentials reload based on retry logic.
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
bool
|
|
56
|
+
True if a retry action was performed, otherwise False.
|
|
57
|
+
"""
|
|
58
|
+
current_creds = self.get_credentials()
|
|
59
|
+
reread_creds = self._handler.load_credentials()
|
|
60
|
+
|
|
61
|
+
# Compare cached and file credentials.
|
|
62
|
+
# If different, reload in cache.
|
|
63
|
+
if current_creds != reread_creds:
|
|
64
|
+
self.reload_credentials()
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
# Check if we need to reload from env only
|
|
68
|
+
if not self._reload_from_env:
|
|
69
|
+
self._handler.reload_credentials_from_env()
|
|
70
|
+
self._reload_from_env = True
|
|
71
|
+
return True
|
|
72
|
+
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
def reload_credentials(self) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Reload credentials from environment and file.
|
|
78
|
+
"""
|
|
79
|
+
self._handler.reload_credentials()
|
|
80
|
+
|
|
81
|
+
###############################
|
|
82
|
+
# Profile methods
|
|
83
|
+
###############################
|
|
84
|
+
|
|
85
|
+
def set_current_profile(self, profile: str) -> None:
|
|
86
|
+
"""
|
|
87
|
+
Set the current profile.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
profile : str
|
|
92
|
+
Name of the profile to set.
|
|
93
|
+
"""
|
|
94
|
+
self._handler.set_current_profile(profile)
|
|
95
|
+
|
|
96
|
+
################################
|
|
97
|
+
# Other methods
|
|
98
|
+
################################
|
|
99
|
+
|
|
100
|
+
def write_file(self, variables: dict) -> None:
|
|
101
|
+
"""
|
|
102
|
+
Write the current configuration and credentials to file.
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
variables : dict
|
|
107
|
+
Dictionary of variables to write.
|
|
108
|
+
"""
|
|
109
|
+
self._handler.write_file(variables)
|
|
110
|
+
|
|
111
|
+
def get_config_creds(self) -> dict:
|
|
112
|
+
"""
|
|
113
|
+
Get merged configuration and credentials.
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
dict
|
|
118
|
+
Merged configuration and credentials dictionary.
|
|
119
|
+
"""
|
|
120
|
+
return {**self.get_configuration(), **self.get_credentials()}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
configurator = Configurator()
|