digitalhub 0.12.0__py3-none-any.whl → 0.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of digitalhub might be problematic. Click here for more details.
- digitalhub/__init__.py +1 -1
- digitalhub/context/api.py +5 -5
- digitalhub/context/builder.py +3 -5
- digitalhub/context/context.py +9 -1
- digitalhub/entities/_base/executable/entity.py +105 -57
- digitalhub/entities/_base/material/entity.py +11 -18
- digitalhub/entities/_base/material/utils.py +1 -1
- digitalhub/entities/_commons/metrics.py +64 -30
- digitalhub/entities/_commons/utils.py +36 -9
- digitalhub/entities/_processors/base.py +150 -79
- digitalhub/entities/_processors/context.py +366 -215
- digitalhub/entities/_processors/utils.py +74 -30
- digitalhub/entities/artifact/crud.py +4 -0
- digitalhub/entities/artifact/utils.py +28 -13
- digitalhub/entities/dataitem/crud.py +14 -2
- digitalhub/entities/dataitem/table/entity.py +3 -3
- digitalhub/entities/dataitem/utils.py +84 -35
- digitalhub/entities/model/crud.py +4 -0
- digitalhub/entities/model/utils.py +28 -13
- digitalhub/entities/project/_base/entity.py +0 -2
- digitalhub/entities/run/_base/entity.py +2 -2
- digitalhub/entities/task/_base/models.py +12 -3
- digitalhub/entities/trigger/_base/entity.py +11 -0
- digitalhub/factory/factory.py +25 -3
- digitalhub/factory/utils.py +11 -3
- digitalhub/runtimes/_base.py +1 -1
- digitalhub/runtimes/builder.py +18 -1
- digitalhub/stores/client/__init__.py +12 -0
- digitalhub/stores/client/_base/api_builder.py +14 -0
- digitalhub/stores/client/_base/client.py +93 -0
- digitalhub/stores/client/_base/key_builder.py +28 -0
- digitalhub/stores/client/_base/params_builder.py +14 -0
- digitalhub/stores/client/api.py +10 -5
- digitalhub/stores/client/builder.py +3 -1
- digitalhub/stores/client/dhcore/api_builder.py +17 -0
- digitalhub/stores/client/dhcore/client.py +325 -70
- digitalhub/stores/client/dhcore/configurator.py +485 -193
- digitalhub/stores/client/dhcore/enums.py +3 -0
- digitalhub/stores/client/dhcore/error_parser.py +35 -1
- digitalhub/stores/client/dhcore/params_builder.py +113 -17
- digitalhub/stores/client/dhcore/utils.py +40 -22
- digitalhub/stores/client/local/api_builder.py +17 -0
- digitalhub/stores/client/local/client.py +6 -8
- digitalhub/stores/credentials/api.py +35 -0
- digitalhub/stores/credentials/configurator.py +210 -0
- digitalhub/stores/credentials/enums.py +68 -0
- digitalhub/stores/credentials/handler.py +176 -0
- digitalhub/stores/{configurator → credentials}/ini_module.py +60 -28
- digitalhub/stores/credentials/store.py +81 -0
- digitalhub/stores/data/_base/store.py +27 -9
- digitalhub/stores/data/api.py +49 -9
- digitalhub/stores/data/builder.py +90 -41
- digitalhub/stores/data/local/store.py +4 -7
- digitalhub/stores/data/remote/store.py +4 -7
- digitalhub/stores/data/s3/configurator.py +65 -80
- digitalhub/stores/data/s3/store.py +69 -81
- digitalhub/stores/data/s3/utils.py +10 -10
- digitalhub/stores/data/sql/configurator.py +76 -73
- digitalhub/stores/data/sql/store.py +191 -102
- digitalhub/utils/exceptions.py +6 -0
- digitalhub/utils/file_utils.py +53 -30
- digitalhub/utils/generic_utils.py +41 -33
- digitalhub/utils/git_utils.py +24 -14
- digitalhub/utils/io_utils.py +19 -18
- digitalhub/utils/uri_utils.py +31 -31
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/METADATA +1 -1
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/RECORD +71 -74
- digitalhub/entities/_commons/types.py +0 -9
- digitalhub/stores/configurator/api.py +0 -35
- digitalhub/stores/configurator/configurator.py +0 -202
- digitalhub/stores/configurator/credentials_store.py +0 -69
- digitalhub/stores/configurator/enums.py +0 -25
- digitalhub/stores/data/s3/enums.py +0 -20
- digitalhub/stores/data/sql/enums.py +0 -20
- digitalhub/stores/data/utils.py +0 -38
- /digitalhub/stores/{configurator → credentials}/__init__.py +0 -0
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/WHEEL +0 -0
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/licenses/AUTHORS +0 -0
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -19,6 +19,7 @@ class DhcoreEnvVar(Enum):
|
|
|
19
19
|
CLIENT_ID = "DHCORE_CLIENT_ID"
|
|
20
20
|
ACCESS_TOKEN = "DHCORE_ACCESS_TOKEN"
|
|
21
21
|
REFRESH_TOKEN = "DHCORE_REFRESH_TOKEN"
|
|
22
|
+
PERSONAL_ACCESS_TOKEN = "DHCORE_PERSONAL_ACCESS_TOKEN"
|
|
22
23
|
WORKFLOW_IMAGE = "DHCORE_WORKFLOW_IMAGE"
|
|
23
24
|
|
|
24
25
|
|
|
@@ -29,3 +30,5 @@ class AuthType(Enum):
|
|
|
29
30
|
|
|
30
31
|
BASIC = "basic"
|
|
31
32
|
OAUTH2 = "oauth2"
|
|
33
|
+
EXCHANGE = "exchange"
|
|
34
|
+
ACCESS_TOKEN = "access_token_only"
|
|
@@ -23,19 +23,53 @@ if typing.TYPE_CHECKING:
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class ErrorParser:
|
|
26
|
+
"""
|
|
27
|
+
Parser for DHCore API errors.
|
|
28
|
+
|
|
29
|
+
This class handles the parsing and translation of HTTP responses
|
|
30
|
+
from the DHCore backend into appropriate Python exceptions.
|
|
31
|
+
"""
|
|
32
|
+
|
|
26
33
|
@staticmethod
|
|
27
34
|
def parse(response: Response) -> None:
|
|
28
35
|
"""
|
|
29
36
|
Handle DHCore API errors.
|
|
30
37
|
|
|
38
|
+
Parses the HTTP response and raises appropriate exceptions based on
|
|
39
|
+
the status code and response content. Maps backend errors to specific
|
|
40
|
+
exception types for better error handling in the client code.
|
|
41
|
+
|
|
31
42
|
Parameters
|
|
32
43
|
----------
|
|
33
44
|
response : Response
|
|
34
|
-
The response object.
|
|
45
|
+
The HTTP response object from requests.
|
|
35
46
|
|
|
36
47
|
Returns
|
|
37
48
|
-------
|
|
38
49
|
None
|
|
50
|
+
|
|
51
|
+
Raises
|
|
52
|
+
------
|
|
53
|
+
TimeoutError
|
|
54
|
+
If the request timed out.
|
|
55
|
+
ConnectionError
|
|
56
|
+
If unable to connect to the backend.
|
|
57
|
+
MissingSpecError
|
|
58
|
+
If the backend reports a missing spec (400 status).
|
|
59
|
+
EntityAlreadyExistsError
|
|
60
|
+
If the entity already exists (400 status with specific message).
|
|
61
|
+
BadRequestError
|
|
62
|
+
For other 400 status codes.
|
|
63
|
+
UnauthorizedError
|
|
64
|
+
For 401 status codes.
|
|
65
|
+
ForbiddenError
|
|
66
|
+
For 403 status codes.
|
|
67
|
+
EntityNotExistsError
|
|
68
|
+
For 404 status codes with specific message.
|
|
69
|
+
BackendError
|
|
70
|
+
For other 404 status codes and general backend errors.
|
|
71
|
+
RuntimeError
|
|
72
|
+
For unexpected exceptions.
|
|
39
73
|
"""
|
|
40
74
|
try:
|
|
41
75
|
response.raise_for_status()
|
|
@@ -10,26 +10,65 @@ from digitalhub.stores.client._base.params_builder import ClientParametersBuilde
|
|
|
10
10
|
|
|
11
11
|
class ClientDHCoreParametersBuilder(ClientParametersBuilder):
|
|
12
12
|
"""
|
|
13
|
-
|
|
13
|
+
Parameter builder for DHCore client API calls.
|
|
14
|
+
|
|
15
|
+
This class constructs HTTP request parameters for different DHCore API
|
|
16
|
+
operations, handling the specific parameter formats and query structures
|
|
17
|
+
required by the DigitalHub Core backend. It supports both base-level
|
|
18
|
+
operations (like project management) and context-level operations
|
|
19
|
+
(entity operations within projects).
|
|
20
|
+
|
|
21
|
+
The builder handles various parameter transformations including:
|
|
22
|
+
- Query parameter formatting for different operations
|
|
23
|
+
- Search filter construction for Solr-based searches
|
|
24
|
+
- Cascade deletion options
|
|
25
|
+
- Versioning parameters
|
|
26
|
+
- Entity sharing parameters
|
|
27
|
+
|
|
28
|
+
Methods
|
|
29
|
+
-------
|
|
30
|
+
build_parameters(category, operation, **kwargs)
|
|
31
|
+
Main entry point for parameter building based on API category.
|
|
32
|
+
build_parameters_base(operation, **kwargs)
|
|
33
|
+
Builds parameters for base-level API operations.
|
|
34
|
+
build_parameters_context(operation, **kwargs)
|
|
35
|
+
Builds parameters for context-level API operations.
|
|
14
36
|
"""
|
|
15
37
|
|
|
16
38
|
def build_parameters(self, category: str, operation: str, **kwargs) -> dict:
|
|
17
39
|
"""
|
|
18
|
-
Build
|
|
40
|
+
Build HTTP request parameters for DHCore API calls.
|
|
41
|
+
|
|
42
|
+
Routes parameter building to the appropriate method based on the
|
|
43
|
+
API category (base or context operations) and applies operation-specific
|
|
44
|
+
parameter transformations.
|
|
19
45
|
|
|
20
46
|
Parameters
|
|
21
47
|
----------
|
|
22
48
|
category : str
|
|
23
|
-
API category
|
|
49
|
+
The API category, either 'base' for project-level operations
|
|
50
|
+
or 'context' for entity operations within projects.
|
|
24
51
|
operation : str
|
|
25
|
-
API operation
|
|
52
|
+
The specific API operation being performed (create, read, update,
|
|
53
|
+
delete, list, search, etc.).
|
|
26
54
|
**kwargs : dict
|
|
27
|
-
|
|
55
|
+
Raw parameters to be transformed into proper HTTP request format.
|
|
56
|
+
May include entity identifiers, filter criteria, pagination
|
|
57
|
+
options, etc.
|
|
28
58
|
|
|
29
59
|
Returns
|
|
30
60
|
-------
|
|
31
61
|
dict
|
|
32
|
-
|
|
62
|
+
Formatted parameters dictionary ready for HTTP request, typically
|
|
63
|
+
containing a 'params' key with query parameters and other
|
|
64
|
+
request-specific parameters.
|
|
65
|
+
|
|
66
|
+
Notes
|
|
67
|
+
-----
|
|
68
|
+
This method acts as a dispatcher, routing to either base or context
|
|
69
|
+
parameter building based on the category. All parameter dictionaries
|
|
70
|
+
are initialized with a 'params' key for query parameters if not
|
|
71
|
+
already present.
|
|
33
72
|
"""
|
|
34
73
|
if category == ApiCategories.BASE.value:
|
|
35
74
|
return self.build_parameters_base(operation, **kwargs)
|
|
@@ -37,19 +76,37 @@ class ClientDHCoreParametersBuilder(ClientParametersBuilder):
|
|
|
37
76
|
|
|
38
77
|
def build_parameters_base(self, operation: str, **kwargs) -> dict:
|
|
39
78
|
"""
|
|
40
|
-
Build
|
|
79
|
+
Build parameters for base-level API operations.
|
|
80
|
+
|
|
81
|
+
Constructs HTTP request parameters for operations that work at the
|
|
82
|
+
base level of the API, typically project-level operations and
|
|
83
|
+
entity sharing functionality.
|
|
41
84
|
|
|
42
85
|
Parameters
|
|
43
86
|
----------
|
|
44
87
|
operation : str
|
|
45
|
-
API operation.
|
|
88
|
+
The API operation being performed. Supported operations:
|
|
89
|
+
- DELETE: Project deletion with optional cascade
|
|
90
|
+
- SHARE: Entity sharing/unsharing with users
|
|
46
91
|
**kwargs : dict
|
|
47
|
-
|
|
92
|
+
Operation-specific parameters including:
|
|
93
|
+
- cascade (bool): For DELETE, whether to cascade delete
|
|
94
|
+
- user (str): For SHARE, target user for sharing
|
|
95
|
+
- unshare (bool): For SHARE, whether to unshare instead
|
|
96
|
+
- id (str): For SHARE unshare, entity ID to unshare
|
|
48
97
|
|
|
49
98
|
Returns
|
|
50
99
|
-------
|
|
51
100
|
dict
|
|
52
|
-
|
|
101
|
+
Formatted parameters with 'params' containing query parameters
|
|
102
|
+
and other request-specific parameters.
|
|
103
|
+
|
|
104
|
+
Notes
|
|
105
|
+
-----
|
|
106
|
+
Parameter transformations:
|
|
107
|
+
- CASCADE: Boolean values are converted to lowercase strings
|
|
108
|
+
- SHARE: User parameter is moved to query params
|
|
109
|
+
- UNSHARE: Requires both unshare=True and entity id
|
|
53
110
|
"""
|
|
54
111
|
kwargs = self._set_params(**kwargs)
|
|
55
112
|
if operation == BackendOperations.DELETE.value:
|
|
@@ -64,19 +121,46 @@ class ClientDHCoreParametersBuilder(ClientParametersBuilder):
|
|
|
64
121
|
|
|
65
122
|
def build_parameters_context(self, operation: str, **kwargs) -> dict:
|
|
66
123
|
"""
|
|
67
|
-
Build
|
|
124
|
+
Build parameters for context-level API operations.
|
|
125
|
+
|
|
126
|
+
Constructs HTTP request parameters for operations that work within
|
|
127
|
+
a specific context (project), including entity management and
|
|
128
|
+
search functionality.
|
|
68
129
|
|
|
69
130
|
Parameters
|
|
70
131
|
----------
|
|
71
132
|
operation : str
|
|
72
|
-
API operation.
|
|
133
|
+
The API operation being performed. Supported operations:
|
|
134
|
+
- SEARCH: Search entities with filtering and pagination
|
|
135
|
+
- READ_MANY: Retrieve multiple entities with pagination
|
|
136
|
+
- DELETE: Delete specific entity by ID
|
|
137
|
+
- READ: Read specific entity by ID (with optional embedded)
|
|
73
138
|
**kwargs : dict
|
|
74
|
-
|
|
139
|
+
Operation-specific parameters including:
|
|
140
|
+
- params (dict): Search filters and conditions
|
|
141
|
+
- page (int): Page number for pagination (default: 0)
|
|
142
|
+
- size (int): Number of items per page (default: 20)
|
|
143
|
+
- order_by (str): Field to order results by
|
|
144
|
+
- order (str): Order direction ('asc' or 'desc')
|
|
145
|
+
- embedded (bool): For READ, whether to include embedded entities
|
|
146
|
+
- id (str): For READ/DELETE, entity identifier
|
|
75
147
|
|
|
76
148
|
Returns
|
|
77
149
|
-------
|
|
78
150
|
dict
|
|
79
|
-
|
|
151
|
+
Formatted parameters with 'params' containing query parameters
|
|
152
|
+
and other request-specific parameters like 'id' for entity operations.
|
|
153
|
+
|
|
154
|
+
Notes
|
|
155
|
+
-----
|
|
156
|
+
Search and pagination:
|
|
157
|
+
- Filters are applied via 'filter' parameter in query string
|
|
158
|
+
- Pagination uses 'page' and 'size' parameters
|
|
159
|
+
- Results can be ordered using 'sort' parameter format
|
|
160
|
+
|
|
161
|
+
Entity operations:
|
|
162
|
+
- READ: Supports embedded entity inclusion via 'embedded' param
|
|
163
|
+
- DELETE: Requires entity 'id' parameter
|
|
80
164
|
"""
|
|
81
165
|
kwargs = self._set_params(**kwargs)
|
|
82
166
|
|
|
@@ -163,17 +247,29 @@ class ClientDHCoreParametersBuilder(ClientParametersBuilder):
|
|
|
163
247
|
@staticmethod
|
|
164
248
|
def _set_params(**kwargs) -> dict:
|
|
165
249
|
"""
|
|
166
|
-
|
|
250
|
+
Initialize parameter dictionary with query parameters structure.
|
|
251
|
+
|
|
252
|
+
Ensures that the parameter dictionary has a 'params' key for
|
|
253
|
+
HTTP query parameters. This is a utility method used by all
|
|
254
|
+
parameter building methods to guarantee consistent structure.
|
|
167
255
|
|
|
168
256
|
Parameters
|
|
169
257
|
----------
|
|
170
258
|
**kwargs : dict
|
|
171
|
-
Keyword arguments.
|
|
259
|
+
Keyword arguments to be formatted. May be empty or contain
|
|
260
|
+
various parameters for API operations.
|
|
172
261
|
|
|
173
262
|
Returns
|
|
174
263
|
-------
|
|
175
264
|
dict
|
|
176
|
-
Parameters with
|
|
265
|
+
Parameters dictionary with guaranteed 'params' key containing
|
|
266
|
+
an empty dict if not already present.
|
|
267
|
+
|
|
268
|
+
Notes
|
|
269
|
+
-----
|
|
270
|
+
This method is called at the beginning of all parameter building
|
|
271
|
+
methods to ensure consistent dictionary structure for query
|
|
272
|
+
parameter handling.
|
|
177
273
|
"""
|
|
178
274
|
if not kwargs:
|
|
179
275
|
kwargs = {}
|
|
@@ -8,7 +8,7 @@ import os
|
|
|
8
8
|
import typing
|
|
9
9
|
|
|
10
10
|
from digitalhub.stores.client.api import get_client
|
|
11
|
-
from digitalhub.stores.
|
|
11
|
+
from digitalhub.stores.credentials.enums import CredsEnvVar
|
|
12
12
|
|
|
13
13
|
if typing.TYPE_CHECKING:
|
|
14
14
|
from digitalhub.stores.client.dhcore.client import ClientDHCore
|
|
@@ -24,53 +24,71 @@ def set_dhcore_env(
|
|
|
24
24
|
) -> None:
|
|
25
25
|
"""
|
|
26
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.
|
|
27
30
|
Note that if the environment variable is already set, it
|
|
28
31
|
will be overwritten.
|
|
29
32
|
|
|
30
33
|
Parameters
|
|
31
34
|
----------
|
|
32
|
-
endpoint : str
|
|
33
|
-
The endpoint of DHCore.
|
|
34
|
-
user : str
|
|
35
|
-
The
|
|
36
|
-
password : str
|
|
37
|
-
The password
|
|
38
|
-
access_token : str
|
|
39
|
-
The access token
|
|
40
|
-
refresh_token : str
|
|
41
|
-
The refresh token
|
|
42
|
-
client_id : str
|
|
43
|
-
The client
|
|
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.
|
|
44
47
|
|
|
45
48
|
Returns
|
|
46
49
|
-------
|
|
47
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.
|
|
48
56
|
"""
|
|
49
57
|
if endpoint is not None:
|
|
50
|
-
os.environ[
|
|
58
|
+
os.environ[CredsEnvVar.DHCORE_ENDPOINT.value] = endpoint
|
|
51
59
|
if user is not None:
|
|
52
|
-
os.environ[
|
|
60
|
+
os.environ[CredsEnvVar.DHCORE_USER.value] = user
|
|
53
61
|
if password is not None:
|
|
54
|
-
os.environ[
|
|
62
|
+
os.environ[CredsEnvVar.DHCORE_PASSWORD.value] = password
|
|
55
63
|
if access_token is not None:
|
|
56
|
-
os.environ[
|
|
64
|
+
os.environ[CredsEnvVar.DHCORE_ACCESS_TOKEN.value] = access_token
|
|
57
65
|
if refresh_token is not None:
|
|
58
|
-
os.environ[
|
|
66
|
+
os.environ[CredsEnvVar.DHCORE_REFRESH_TOKEN.value] = refresh_token
|
|
59
67
|
if client_id is not None:
|
|
60
|
-
os.environ[
|
|
68
|
+
os.environ[CredsEnvVar.DHCORE_CLIENT_ID.value] = client_id
|
|
61
69
|
|
|
62
70
|
client: ClientDHCore = get_client(local=False)
|
|
63
|
-
client._configurator.
|
|
71
|
+
client._configurator.load_env_vars()
|
|
64
72
|
|
|
65
73
|
|
|
66
74
|
def refresh_token() -> None:
|
|
67
75
|
"""
|
|
68
|
-
Function to refresh token.
|
|
76
|
+
Function to refresh the OAuth2 access token.
|
|
77
|
+
|
|
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.
|
|
69
81
|
|
|
70
82
|
Returns
|
|
71
83
|
-------
|
|
72
84
|
None
|
|
85
|
+
|
|
86
|
+
Raises
|
|
87
|
+
------
|
|
88
|
+
ClientError
|
|
89
|
+
If the client is not properly configured or if the token
|
|
90
|
+
refresh fails.
|
|
73
91
|
"""
|
|
74
92
|
client: ClientDHCore = get_client(local=False)
|
|
75
93
|
client._configurator.check_config()
|
|
76
|
-
client._configurator.
|
|
94
|
+
client._configurator.refresh_credentials()
|
|
@@ -73,6 +73,23 @@ class ClientLocalApiBuilder(ClientApiBuilder):
|
|
|
73
73
|
def build_api_context(self, operation: str, **kwargs) -> str:
|
|
74
74
|
"""
|
|
75
75
|
Build the context API for the client.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
operation : str
|
|
80
|
+
The API operation.
|
|
81
|
+
**kwargs : dict
|
|
82
|
+
Additional parameters including project, entity_type, entity_id, etc.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
str
|
|
87
|
+
The formatted context API endpoint.
|
|
88
|
+
|
|
89
|
+
Raises
|
|
90
|
+
------
|
|
91
|
+
BackendError
|
|
92
|
+
If the operation is not supported for the entity type.
|
|
76
93
|
"""
|
|
77
94
|
try:
|
|
78
95
|
entity_type = kwargs["entity_type"] + "s"
|
|
@@ -537,18 +537,16 @@ class ClientLocal(Client):
|
|
|
537
537
|
Parameters
|
|
538
538
|
----------
|
|
539
539
|
error_code : int
|
|
540
|
-
Error code.
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
Entity
|
|
545
|
-
entity_id : str
|
|
546
|
-
Entity ID.
|
|
540
|
+
Error code identifying the type of error.
|
|
541
|
+
entity_type : str, optional
|
|
542
|
+
Entity type that caused the error.
|
|
543
|
+
entity_id : str, optional
|
|
544
|
+
Entity ID that caused the error.
|
|
547
545
|
|
|
548
546
|
Returns
|
|
549
547
|
-------
|
|
550
548
|
str
|
|
551
|
-
The formatted message.
|
|
549
|
+
The formatted error message.
|
|
552
550
|
"""
|
|
553
551
|
msg = {
|
|
554
552
|
1: f"Object '{entity_type}' to create is not valid",
|
|
@@ -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.credentials.handler import creds_handler
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def set_current_profile(environment: str) -> None:
|
|
11
|
+
"""
|
|
12
|
+
Set the current credentials profile.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
environment : str
|
|
17
|
+
Name of the credentials profile to set.
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
None
|
|
22
|
+
"""
|
|
23
|
+
creds_handler.set_current_profile(environment)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_current_profile() -> str:
|
|
27
|
+
"""
|
|
28
|
+
Get the name of the current credentials profile.
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
str
|
|
33
|
+
Name of the current credentials profile.
|
|
34
|
+
"""
|
|
35
|
+
return creds_handler.get_current_profile()
|
|
@@ -0,0 +1,210 @@
|
|
|
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 abc import abstractmethod
|
|
8
|
+
|
|
9
|
+
from digitalhub.stores.credentials.enums import CredsOrigin
|
|
10
|
+
from digitalhub.stores.credentials.handler import creds_handler
|
|
11
|
+
from digitalhub.utils.exceptions import ConfigError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Configurator:
|
|
15
|
+
"""
|
|
16
|
+
Base configurator for credentials management.
|
|
17
|
+
|
|
18
|
+
Attributes
|
|
19
|
+
----------
|
|
20
|
+
keys : list of str
|
|
21
|
+
List of credential keys to manage.
|
|
22
|
+
required_keys : list of str
|
|
23
|
+
List of required credential keys.
|
|
24
|
+
_env : str
|
|
25
|
+
Environment origin identifier.
|
|
26
|
+
_file : str
|
|
27
|
+
File origin identifier.
|
|
28
|
+
_creds_handler : object
|
|
29
|
+
Credentials handler instance.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
# Must be set in implementing class
|
|
33
|
+
keys: list[str] = []
|
|
34
|
+
required_keys: list[str] = []
|
|
35
|
+
|
|
36
|
+
# Origin of the credentials
|
|
37
|
+
_env = CredsOrigin.ENV.value
|
|
38
|
+
_file = CredsOrigin.FILE.value
|
|
39
|
+
|
|
40
|
+
# Credentials handler
|
|
41
|
+
_creds_handler = creds_handler
|
|
42
|
+
|
|
43
|
+
def __init__(self):
|
|
44
|
+
self._current_profile = self._creds_handler.get_current_profile()
|
|
45
|
+
self.load_configs()
|
|
46
|
+
self._changed_origin = False
|
|
47
|
+
self._origin = self.set_origin()
|
|
48
|
+
|
|
49
|
+
##############################
|
|
50
|
+
# Configuration
|
|
51
|
+
##############################
|
|
52
|
+
|
|
53
|
+
def load_configs(self) -> None:
|
|
54
|
+
"""
|
|
55
|
+
Load the configuration from both environment and file sources.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
None
|
|
60
|
+
"""
|
|
61
|
+
self.load_env_vars()
|
|
62
|
+
self.load_file_vars()
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def load_env_vars(self) -> None:
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
@abstractmethod
|
|
69
|
+
def load_file_vars(self) -> None:
|
|
70
|
+
...
|
|
71
|
+
|
|
72
|
+
def check_config(self) -> None:
|
|
73
|
+
"""
|
|
74
|
+
Check if the current profile has changed and reload
|
|
75
|
+
the file credentials if needed.
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
None
|
|
80
|
+
"""
|
|
81
|
+
if (current := self._creds_handler.get_current_profile()) != self._current_profile:
|
|
82
|
+
self.load_file_vars()
|
|
83
|
+
self._current_profile = current
|
|
84
|
+
|
|
85
|
+
def set_origin(self) -> str:
|
|
86
|
+
"""
|
|
87
|
+
Determine the default origin for credentials (env or file).
|
|
88
|
+
|
|
89
|
+
Returns
|
|
90
|
+
-------
|
|
91
|
+
str
|
|
92
|
+
The selected origin ('env' or 'file').
|
|
93
|
+
|
|
94
|
+
Raises
|
|
95
|
+
------
|
|
96
|
+
ConfigError
|
|
97
|
+
If required credentials are missing in both sources.
|
|
98
|
+
"""
|
|
99
|
+
origin = self._env
|
|
100
|
+
|
|
101
|
+
env_creds = self._creds_handler.get_credentials(self._env)
|
|
102
|
+
missing_env = self._check_credentials(env_creds)
|
|
103
|
+
|
|
104
|
+
file_creds = self._creds_handler.get_credentials(self._file)
|
|
105
|
+
missing_file = self._check_credentials(file_creds)
|
|
106
|
+
|
|
107
|
+
msg = ""
|
|
108
|
+
if missing_env:
|
|
109
|
+
msg = f"Missing required vars in env: {', '.join(missing_env)}"
|
|
110
|
+
origin = self._file
|
|
111
|
+
self._changed_origin = True
|
|
112
|
+
elif missing_file:
|
|
113
|
+
msg += f"Missing required vars in .dhcore.ini file: {', '.join(missing_file)}"
|
|
114
|
+
|
|
115
|
+
if missing_env and missing_file:
|
|
116
|
+
raise ConfigError(msg)
|
|
117
|
+
|
|
118
|
+
return origin
|
|
119
|
+
|
|
120
|
+
def eval_change_origin(self) -> None:
|
|
121
|
+
"""
|
|
122
|
+
Attempt to change the origin of credentials.
|
|
123
|
+
Raise error if already evaluated.
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
None
|
|
128
|
+
"""
|
|
129
|
+
try:
|
|
130
|
+
self.change_origin()
|
|
131
|
+
except ConfigError:
|
|
132
|
+
raise ConfigError("Credentials origin already evaluated. Please check your credentials.")
|
|
133
|
+
|
|
134
|
+
def change_origin(self) -> None:
|
|
135
|
+
"""
|
|
136
|
+
Change the origin of credentials from env to file or vice versa.
|
|
137
|
+
|
|
138
|
+
Returns
|
|
139
|
+
-------
|
|
140
|
+
None
|
|
141
|
+
"""
|
|
142
|
+
if self._changed_origin:
|
|
143
|
+
raise ConfigError("Origin has already been changed.")
|
|
144
|
+
if self._origin == self._env:
|
|
145
|
+
self.change_to_file()
|
|
146
|
+
self.change_to_env()
|
|
147
|
+
|
|
148
|
+
def change_to_file(self) -> None:
|
|
149
|
+
"""
|
|
150
|
+
Set the credentials origin to file.
|
|
151
|
+
|
|
152
|
+
Returns
|
|
153
|
+
-------
|
|
154
|
+
None
|
|
155
|
+
"""
|
|
156
|
+
if self._origin == self._env:
|
|
157
|
+
self._changed_origin = True
|
|
158
|
+
self._origin = CredsOrigin.FILE.value
|
|
159
|
+
|
|
160
|
+
def change_to_env(self) -> None:
|
|
161
|
+
"""
|
|
162
|
+
Set the credentials origin to environment.
|
|
163
|
+
|
|
164
|
+
Returns
|
|
165
|
+
-------
|
|
166
|
+
None
|
|
167
|
+
"""
|
|
168
|
+
if self._origin == self._file:
|
|
169
|
+
self._changed_origin = True
|
|
170
|
+
self._origin = CredsOrigin.ENV.value
|
|
171
|
+
|
|
172
|
+
##############################
|
|
173
|
+
# Credentials
|
|
174
|
+
##############################
|
|
175
|
+
|
|
176
|
+
def get_credentials(self, origin: str) -> dict:
|
|
177
|
+
"""
|
|
178
|
+
Retrieve credentials for the specified origin.
|
|
179
|
+
|
|
180
|
+
Parameters
|
|
181
|
+
----------
|
|
182
|
+
origin : str
|
|
183
|
+
The origin to retrieve credentials from ('env' or 'file').
|
|
184
|
+
|
|
185
|
+
Returns
|
|
186
|
+
-------
|
|
187
|
+
dict
|
|
188
|
+
Dictionary of credentials.
|
|
189
|
+
"""
|
|
190
|
+
return self._creds_handler.get_credentials(origin)
|
|
191
|
+
|
|
192
|
+
def _check_credentials(self, creds: dict) -> list[str]:
|
|
193
|
+
"""
|
|
194
|
+
Check for missing required credentials in a dictionary.
|
|
195
|
+
|
|
196
|
+
Parameters
|
|
197
|
+
----------
|
|
198
|
+
creds : dict
|
|
199
|
+
Dictionary of credentials to check.
|
|
200
|
+
|
|
201
|
+
Returns
|
|
202
|
+
-------
|
|
203
|
+
list of str
|
|
204
|
+
List of missing required credential keys.
|
|
205
|
+
"""
|
|
206
|
+
missing_keys = []
|
|
207
|
+
for k, v in creds.items():
|
|
208
|
+
if v is None and k in self.required_keys:
|
|
209
|
+
missing_keys.append(k)
|
|
210
|
+
return missing_keys
|