digitalhub 0.8.1__py3-none-any.whl → 0.9.0b0__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 (128) hide show
  1. digitalhub/__init__.py +19 -2
  2. digitalhub/client/_base/api_builder.py +16 -0
  3. digitalhub/client/_base/client.py +31 -0
  4. digitalhub/client/api.py +2 -38
  5. digitalhub/client/dhcore/api_builder.py +100 -0
  6. digitalhub/client/dhcore/client.py +77 -24
  7. digitalhub/client/dhcore/enums.py +26 -0
  8. digitalhub/client/dhcore/env.py +2 -2
  9. digitalhub/client/dhcore/utils.py +17 -17
  10. digitalhub/client/local/api_builder.py +100 -0
  11. digitalhub/client/local/client.py +20 -0
  12. digitalhub/context/api.py +3 -38
  13. digitalhub/context/builder.py +10 -23
  14. digitalhub/context/context.py +20 -92
  15. digitalhub/entities/_base/context/entity.py +30 -22
  16. digitalhub/entities/_base/entity/_constructors/metadata.py +12 -1
  17. digitalhub/entities/_base/entity/_constructors/name.py +1 -1
  18. digitalhub/entities/_base/entity/_constructors/spec.py +1 -1
  19. digitalhub/entities/_base/entity/_constructors/status.py +3 -2
  20. digitalhub/entities/_base/entity/builder.py +6 -1
  21. digitalhub/entities/_base/entity/entity.py +30 -10
  22. digitalhub/entities/_base/entity/metadata.py +22 -0
  23. digitalhub/entities/_base/entity/spec.py +7 -2
  24. digitalhub/entities/_base/executable/entity.py +8 -8
  25. digitalhub/entities/_base/material/entity.py +48 -16
  26. digitalhub/entities/_base/material/status.py +0 -31
  27. digitalhub/entities/_base/material/utils.py +106 -0
  28. digitalhub/entities/_base/project/entity.py +341 -0
  29. digitalhub/entities/_base/unversioned/entity.py +1 -23
  30. digitalhub/entities/_base/versioned/entity.py +0 -25
  31. digitalhub/entities/_commons/enums.py +103 -0
  32. digitalhub/entities/_commons/utils.py +83 -0
  33. digitalhub/entities/_operations/processor.py +1747 -0
  34. digitalhub/entities/artifact/_base/builder.py +1 -1
  35. digitalhub/entities/artifact/_base/entity.py +1 -1
  36. digitalhub/entities/artifact/artifact/builder.py +2 -1
  37. digitalhub/entities/artifact/crud.py +46 -29
  38. digitalhub/entities/artifact/utils.py +62 -0
  39. digitalhub/entities/dataitem/_base/builder.py +1 -1
  40. digitalhub/entities/dataitem/_base/entity.py +6 -6
  41. digitalhub/entities/dataitem/crud.py +50 -66
  42. digitalhub/entities/dataitem/dataitem/builder.py +2 -1
  43. digitalhub/entities/dataitem/iceberg/builder.py +2 -1
  44. digitalhub/entities/dataitem/table/builder.py +2 -1
  45. digitalhub/entities/dataitem/table/entity.py +5 -10
  46. digitalhub/entities/dataitem/table/models.py +4 -5
  47. digitalhub/entities/dataitem/utils.py +137 -0
  48. digitalhub/entities/function/_base/builder.py +1 -1
  49. digitalhub/entities/function/_base/entity.py +5 -1
  50. digitalhub/entities/function/crud.py +36 -17
  51. digitalhub/entities/model/_base/builder.py +1 -1
  52. digitalhub/entities/model/_base/entity.py +1 -1
  53. digitalhub/entities/model/crud.py +46 -29
  54. digitalhub/entities/model/huggingface/builder.py +2 -1
  55. digitalhub/entities/model/huggingface/spec.py +4 -2
  56. digitalhub/entities/model/mlflow/builder.py +2 -1
  57. digitalhub/entities/model/mlflow/models.py +17 -9
  58. digitalhub/entities/model/mlflow/spec.py +6 -1
  59. digitalhub/entities/model/mlflow/utils.py +4 -2
  60. digitalhub/entities/model/model/builder.py +2 -1
  61. digitalhub/entities/model/sklearn/builder.py +2 -1
  62. digitalhub/entities/model/utils.py +62 -0
  63. digitalhub/entities/project/_base/builder.py +2 -2
  64. digitalhub/entities/project/_base/entity.py +82 -272
  65. digitalhub/entities/project/crud.py +110 -91
  66. digitalhub/entities/project/utils.py +35 -0
  67. digitalhub/entities/run/_base/builder.py +3 -1
  68. digitalhub/entities/run/_base/entity.py +52 -54
  69. digitalhub/entities/run/_base/spec.py +11 -7
  70. digitalhub/entities/run/crud.py +35 -17
  71. digitalhub/entities/secret/_base/builder.py +2 -2
  72. digitalhub/entities/secret/_base/entity.py +4 -10
  73. digitalhub/entities/secret/crud.py +36 -21
  74. digitalhub/entities/task/_base/builder.py +14 -14
  75. digitalhub/entities/task/_base/entity.py +6 -6
  76. digitalhub/entities/task/_base/models.py +29 -6
  77. digitalhub/entities/task/_base/spec.py +44 -13
  78. digitalhub/entities/task/_base/utils.py +18 -0
  79. digitalhub/entities/task/crud.py +35 -15
  80. digitalhub/entities/workflow/_base/builder.py +1 -1
  81. digitalhub/entities/workflow/_base/entity.py +14 -6
  82. digitalhub/entities/workflow/crud.py +36 -17
  83. digitalhub/factory/utils.py +1 -1
  84. digitalhub/readers/_base/reader.py +2 -2
  85. digitalhub/readers/_commons/enums.py +13 -0
  86. digitalhub/readers/api.py +3 -2
  87. digitalhub/readers/factory.py +12 -6
  88. digitalhub/readers/pandas/reader.py +20 -8
  89. digitalhub/runtimes/_base.py +0 -7
  90. digitalhub/stores/_base/store.py +53 -9
  91. digitalhub/stores/builder.py +5 -5
  92. digitalhub/stores/local/store.py +37 -2
  93. digitalhub/stores/remote/store.py +25 -3
  94. digitalhub/stores/s3/store.py +34 -7
  95. digitalhub/stores/sql/store.py +112 -45
  96. digitalhub/utils/exceptions.py +6 -0
  97. digitalhub/utils/file_utils.py +60 -2
  98. digitalhub/utils/generic_utils.py +45 -4
  99. digitalhub/utils/io_utils.py +18 -0
  100. digitalhub/utils/uri_utils.py +153 -15
  101. {digitalhub-0.8.1.dist-info → digitalhub-0.9.0b0.dist-info}/METADATA +2 -2
  102. {digitalhub-0.8.1.dist-info → digitalhub-0.9.0b0.dist-info}/RECORD +110 -113
  103. test/testkfp.py +4 -1
  104. digitalhub/datastores/_base/datastore.py +0 -85
  105. digitalhub/datastores/api.py +0 -37
  106. digitalhub/datastores/builder.py +0 -110
  107. digitalhub/datastores/local/datastore.py +0 -50
  108. digitalhub/datastores/remote/__init__.py +0 -0
  109. digitalhub/datastores/remote/datastore.py +0 -31
  110. digitalhub/datastores/s3/__init__.py +0 -0
  111. digitalhub/datastores/s3/datastore.py +0 -46
  112. digitalhub/datastores/sql/__init__.py +0 -0
  113. digitalhub/datastores/sql/datastore.py +0 -68
  114. digitalhub/entities/_base/api_utils.py +0 -620
  115. digitalhub/entities/_base/crud.py +0 -468
  116. digitalhub/entities/function/_base/models.py +0 -118
  117. digitalhub/entities/utils/__init__.py +0 -0
  118. digitalhub/entities/utils/api.py +0 -346
  119. digitalhub/entities/utils/entity_types.py +0 -19
  120. digitalhub/entities/utils/state.py +0 -31
  121. digitalhub/entities/utils/utils.py +0 -202
  122. /digitalhub/{context → entities/_base/project}/__init__.py +0 -0
  123. /digitalhub/{datastores → entities/_commons}/__init__.py +0 -0
  124. /digitalhub/{datastores/_base → entities/_operations}/__init__.py +0 -0
  125. /digitalhub/{datastores/local → readers/_commons}/__init__.py +0 -0
  126. {digitalhub-0.8.1.dist-info → digitalhub-0.9.0b0.dist-info}/LICENSE.txt +0 -0
  127. {digitalhub-0.8.1.dist-info → digitalhub-0.9.0b0.dist-info}/WHEEL +0 -0
  128. {digitalhub-0.8.1.dist-info → digitalhub-0.9.0b0.dist-info}/top_level.txt +0 -0
digitalhub/__init__.py CHANGED
@@ -1,9 +1,11 @@
1
+ __version__ = "0.9.0b0"
1
2
  from digitalhub.entities.artifact.crud import (
2
3
  delete_artifact,
3
4
  get_artifact,
4
5
  get_artifact_versions,
5
6
  import_artifact,
6
7
  list_artifacts,
8
+ load_artifact,
7
9
  log_artifact,
8
10
  new_artifact,
9
11
  update_artifact,
@@ -14,6 +16,7 @@ from digitalhub.entities.dataitem.crud import (
14
16
  get_dataitem_versions,
15
17
  import_dataitem,
16
18
  list_dataitems,
19
+ load_dataitem,
17
20
  log_dataitem,
18
21
  new_dataitem,
19
22
  update_dataitem,
@@ -24,6 +27,7 @@ from digitalhub.entities.function.crud import (
24
27
  get_function_versions,
25
28
  import_function,
26
29
  list_functions,
30
+ load_function,
27
31
  new_function,
28
32
  update_function,
29
33
  )
@@ -33,6 +37,7 @@ from digitalhub.entities.model.crud import (
33
37
  get_model_versions,
34
38
  import_model,
35
39
  list_models,
40
+ load_model,
36
41
  log_model,
37
42
  new_model,
38
43
  update_model,
@@ -42,27 +47,39 @@ from digitalhub.entities.project.crud import (
42
47
  get_or_create_project,
43
48
  get_project,
44
49
  import_project,
50
+ list_projects,
45
51
  load_project,
46
52
  new_project,
53
+ search_entity,
47
54
  update_project,
48
55
  )
49
- from digitalhub.entities.run.crud import delete_run, get_run, import_run, list_runs, new_run, update_run
56
+ from digitalhub.entities.run.crud import delete_run, get_run, import_run, list_runs, load_run, new_run, update_run
50
57
  from digitalhub.entities.secret.crud import (
51
58
  delete_secret,
52
59
  get_secret,
53
60
  get_secret_versions,
54
61
  import_secret,
55
62
  list_secrets,
63
+ load_secret,
56
64
  new_secret,
57
65
  update_secret,
58
66
  )
59
- from digitalhub.entities.task.crud import delete_task, get_task, import_task, list_tasks, new_task, update_task
67
+ from digitalhub.entities.task.crud import (
68
+ delete_task,
69
+ get_task,
70
+ import_task,
71
+ list_tasks,
72
+ load_task,
73
+ new_task,
74
+ update_task,
75
+ )
60
76
  from digitalhub.entities.workflow.crud import (
61
77
  delete_workflow,
62
78
  get_workflow,
63
79
  get_workflow_versions,
64
80
  import_workflow,
65
81
  list_workflows,
82
+ load_workflow,
66
83
  new_workflow,
67
84
  update_workflow,
68
85
  )
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import abstractmethod
4
+
5
+
6
+ class ClientApiBuilder:
7
+ """
8
+ This class is used to build the API for the client.
9
+ Depending on the client, the API is built differently.
10
+ """
11
+
12
+ @abstractmethod
13
+ def build_api(self) -> str:
14
+ """
15
+ Build the API for the client.
16
+ """
@@ -2,6 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  from abc import abstractmethod
4
4
 
5
+ from digitalhub.client._base.api_builder import ClientApiBuilder
6
+
5
7
 
6
8
  class Client:
7
9
  """
@@ -12,6 +14,29 @@ class Client:
12
14
  listing of objects and comes into two subclasses: Local and DHCore.
13
15
  """
14
16
 
17
+ def __init__(self) -> None:
18
+ self._api_builder: ClientApiBuilder = None
19
+
20
+ def build_api(self, category: str, operation: str, **kwargs) -> str:
21
+ """
22
+ Build the API for the client.
23
+
24
+ Parameters
25
+ ----------
26
+ category : str
27
+ API category.
28
+ operation : str
29
+ API operation.
30
+ **kwargs : dict
31
+ Additional parameters.
32
+
33
+ Returns
34
+ -------
35
+ str
36
+ API formatted.
37
+ """
38
+ return self._api_builder.build_api(category, operation, **kwargs)
39
+
15
40
  @abstractmethod
16
41
  def create_object(self, api: str, obj: dict, **kwargs) -> dict:
17
42
  """
@@ -48,6 +73,12 @@ class Client:
48
73
  Read first object method.
49
74
  """
50
75
 
76
+ @abstractmethod
77
+ def search_objects(self, api: str, **kwargs) -> dict:
78
+ """
79
+ Search objects method.
80
+ """
81
+
51
82
  @staticmethod
52
83
  @abstractmethod
53
84
  def is_local() -> bool:
digitalhub/client/api.py CHANGED
@@ -8,26 +8,7 @@ if typing.TYPE_CHECKING:
8
8
  from digitalhub.client._base.client import Client
9
9
 
10
10
 
11
- def check_client_exists(local: bool = False) -> bool:
12
- """
13
- Check if client exists.
14
-
15
- Parameters
16
- ----------
17
- local : bool
18
- Check client existence by local.
19
-
20
- Returns
21
- -------
22
- bool
23
- True if client exists, False otherwise.
24
- """
25
- if local:
26
- return client_builder._local is not None
27
- return client_builder._dhcore is not None
28
-
29
-
30
- def build_client(local: bool = False, config: dict | None = None) -> None:
11
+ def get_client(local: bool = False, config: dict | None = None) -> Client:
31
12
  """
32
13
  Wrapper around ClientBuilder.build.
33
14
 
@@ -43,21 +24,4 @@ def build_client(local: bool = False, config: dict | None = None) -> None:
43
24
  Client
44
25
  The client instance.
45
26
  """
46
- client_builder.build(local, config)
47
-
48
-
49
- def get_client(local: bool = False) -> Client:
50
- """
51
- Wrapper around ClientBuilder.build.
52
-
53
- Parameters
54
- ----------
55
- local : bool
56
- Whether to create a local client or not.
57
-
58
- Returns
59
- -------
60
- Client
61
- The client instance.
62
- """
63
- return client_builder.build(local)
27
+ return client_builder.build(local, config)
@@ -0,0 +1,100 @@
1
+ from __future__ import annotations
2
+
3
+ from digitalhub.client._base.api_builder import ClientApiBuilder
4
+ from digitalhub.entities._commons.enums import ApiCategories, BackendOperations
5
+ from digitalhub.utils.exceptions import BackendError
6
+
7
+ API_BASE = "/api/v1"
8
+ API_CONTEXT = f"{API_BASE}/-"
9
+
10
+
11
+ class ClientDHCoreApiBuilder(ClientApiBuilder):
12
+ """
13
+ This class is used to build the API for the DHCore client.
14
+ """
15
+
16
+ def build_api(self, category: str, operation: str, **kwargs) -> str:
17
+ """
18
+ Build the API for the client.
19
+
20
+ Parameters
21
+ ----------
22
+ category : str
23
+ API category.
24
+ operation : str
25
+ API operation.
26
+ **kwargs : dict
27
+ Additional parameters.
28
+
29
+ Returns
30
+ -------
31
+ str
32
+ API formatted.
33
+ """
34
+ if category == ApiCategories.BASE.value:
35
+ return self.build_api_base(operation, **kwargs)
36
+ return self.build_api_context(operation, **kwargs)
37
+
38
+ def build_api_base(self, operation: str, **kwargs) -> str:
39
+ """
40
+ Build the base API for the client.
41
+
42
+ Parameters
43
+ ----------
44
+ operation : str
45
+ API operation.
46
+ **kwargs : dict
47
+ Additional parameters.
48
+
49
+ Returns
50
+ -------
51
+ str
52
+ API formatted.
53
+ """
54
+ entity_type = kwargs["entity_type"] + "s"
55
+ if operation in (
56
+ BackendOperations.CREATE.value,
57
+ BackendOperations.LIST.value,
58
+ ):
59
+ return f"{API_BASE}/{entity_type}"
60
+ elif operation in (
61
+ BackendOperations.READ.value,
62
+ BackendOperations.UPDATE.value,
63
+ BackendOperations.DELETE.value,
64
+ ):
65
+ return f"{API_BASE}/{entity_type}/{kwargs['entity_name']}"
66
+ elif operation == BackendOperations.SHARE.value:
67
+ return f"{API_BASE}/{entity_type}/{kwargs['entity_name']}/share"
68
+ raise BackendError(f"Invalid operation '{operation}' for entity type '{entity_type}' in DHCore.")
69
+
70
+ def build_api_context(self, operation: str, **kwargs) -> str:
71
+ """
72
+ Build the context API for the client.
73
+ """
74
+ entity_type = kwargs["entity_type"] + "s"
75
+ project = kwargs["project"]
76
+ if operation in (
77
+ BackendOperations.CREATE.value,
78
+ BackendOperations.LIST.value,
79
+ ):
80
+ return f"{API_CONTEXT}/{project}/{entity_type}"
81
+ elif operation in (
82
+ BackendOperations.READ.value,
83
+ BackendOperations.UPDATE.value,
84
+ BackendOperations.DELETE.value,
85
+ ):
86
+ return f"{API_CONTEXT}/{project}/{entity_type}/{kwargs['entity_id']}"
87
+ elif operation == BackendOperations.LOGS.value:
88
+ return f"{API_CONTEXT}/{project}/{entity_type}/{kwargs['entity_id']}/logs"
89
+ elif operation == BackendOperations.STOP.value:
90
+ return f"{API_CONTEXT}/{project}/{entity_type}/{kwargs['entity_id']}/stop"
91
+ elif operation == BackendOperations.RESUME.value:
92
+ return f"{API_CONTEXT}/{project}/{entity_type}/{kwargs['entity_id']}/resume"
93
+ elif operation == BackendOperations.DATA.value:
94
+ return f"{API_CONTEXT}/{project}/{entity_type}/{kwargs['entity_id']}/data"
95
+ elif operation == BackendOperations.FILES.value:
96
+ return f"{API_CONTEXT}/{project}/{entity_type}/{kwargs['entity_id']}/files/info"
97
+ elif operation == BackendOperations.SEARCH.value:
98
+ return f"{API_CONTEXT}/{project}/solr/search/item"
99
+
100
+ raise BackendError(f"Invalid operation '{operation}' for entity type '{entity_type}' in DHCore.")
@@ -4,7 +4,6 @@ import datetime
4
4
  import json
5
5
  import os
6
6
  import typing
7
- from urllib.parse import urlparse
8
7
  from warnings import warn
9
8
 
10
9
  from dotenv import get_key, set_key
@@ -12,7 +11,9 @@ from requests import request
12
11
  from requests.exceptions import HTTPError, JSONDecodeError, RequestException
13
12
 
14
13
  from digitalhub.client._base.client import Client
15
- from digitalhub.client.dhcore.env import ENV_FILE, FALLBACK_USER, MAX_API_LEVEL, MIN_API_LEVEL, LIB_VERSION
14
+ from digitalhub.client.dhcore.api_builder import ClientDHCoreApiBuilder
15
+ from digitalhub.client.dhcore.enums import AuthType, EnvVar
16
+ from digitalhub.client.dhcore.env import ENV_FILE, FALLBACK_USER, LIB_VERSION, MAX_API_LEVEL, MIN_API_LEVEL
16
17
  from digitalhub.client.dhcore.models import BasicAuth, OAuth2TokenAuth
17
18
  from digitalhub.utils.exceptions import (
18
19
  BackendError,
@@ -23,6 +24,7 @@ from digitalhub.utils.exceptions import (
23
24
  MissingSpecError,
24
25
  UnauthorizedError,
25
26
  )
27
+ from digitalhub.utils.uri_utils import has_remote_scheme
26
28
 
27
29
  if typing.TYPE_CHECKING:
28
30
  from requests import Response
@@ -47,6 +49,9 @@ class ClientDHCore(Client):
47
49
  def __init__(self, config: dict | None = None) -> None:
48
50
  super().__init__()
49
51
 
52
+ # API builder
53
+ self._api_builder = ClientDHCoreApiBuilder()
54
+
50
55
  # Endpoints
51
56
  self._endpoint_core: str | None = None
52
57
  self._endpoint_issuer: str | None = None
@@ -214,6 +219,56 @@ class ClientDHCore(Client):
214
219
  except IndexError:
215
220
  raise BackendError("No object found.")
216
221
 
222
+ def search_objects(self, api: str, **kwargs) -> list[dict]:
223
+ """
224
+ Search objects from DHCore.
225
+
226
+ Parameters
227
+ ----------
228
+ api : str
229
+ Search API.
230
+ **kwargs : dict
231
+ Keyword arguments to pass to the request.
232
+
233
+ Returns
234
+ -------
235
+ list[dict]
236
+ Response objects.
237
+ """
238
+ if kwargs is None:
239
+ kwargs = {}
240
+
241
+ if "params" not in kwargs:
242
+ kwargs["params"] = {}
243
+
244
+ start_page = 0
245
+ if "page" not in kwargs["params"]:
246
+ kwargs["params"]["page"] = start_page
247
+
248
+ if "size" not in kwargs["params"]:
249
+ kwargs["params"]["size"] = 10
250
+
251
+ # Add sorting
252
+ if "sort" not in kwargs["params"]:
253
+ kwargs["params"]["sort"] = "metadata.updated,DESC"
254
+
255
+ objects_with_highlights = []
256
+ while True:
257
+ resp = self._prepare_call("GET", api, **kwargs)
258
+ contents = resp["content"]
259
+ total_pages = resp["totalPages"]
260
+ if not contents or kwargs["params"]["page"] >= total_pages:
261
+ break
262
+ objects_with_highlights.extend(contents)
263
+ kwargs["params"]["page"] += 1
264
+
265
+ objects = []
266
+ for obj in objects_with_highlights:
267
+ obj.pop("highlights", None)
268
+ objects.append(obj)
269
+
270
+ return objects
271
+
217
272
  ##############################
218
273
  # Call methods
219
274
  ##############################
@@ -275,9 +330,9 @@ class ClientDHCore(Client):
275
330
  dict
276
331
  Keyword arguments with the authentication parameters.
277
332
  """
278
- if self._auth_type == "basic":
333
+ if self._auth_type == AuthType.BASIC.value:
279
334
  kwargs["auth"] = self._user, self._password
280
- elif self._auth_type == "oauth2":
335
+ elif self._auth_type == AuthType.OAUTH2.value:
281
336
  if "headers" not in kwargs:
282
337
  kwargs["headers"] = {}
283
338
  kwargs["headers"]["Authorization"] = f"Bearer {self._access_token}"
@@ -475,21 +530,20 @@ class ClientDHCore(Client):
475
530
  self._access_token = config.access_token
476
531
  self._refresh_token = config.refresh_token
477
532
  self._client_id = config.client_id
478
- self._auth_type = "oauth2"
533
+ self._auth_type = AuthType.OAUTH2.value
479
534
 
480
535
  elif config.get("user") is not None and config.get("password") is not None:
481
536
  config = BasicAuth(**config)
482
537
  self._user = config.user
483
538
  self._password = config.password
484
- self._auth_type = "basic"
539
+ self._auth_type = AuthType.BASIC.value
485
540
 
486
541
  return
487
542
 
488
543
  self._get_auth_from_env()
489
544
 
490
545
  # Propagate access and refresh token to env file
491
- if not ENV_FILE.exists():
492
- self._write_env()
546
+ self._write_env()
493
547
 
494
548
  def _get_endpoints_from_env(self) -> None:
495
549
  """
@@ -504,12 +558,12 @@ class ClientDHCore(Client):
504
558
  Exception
505
559
  If the endpoint of DHCore is not set in the env variables.
506
560
  """
507
- core_endpt = os.getenv("DHCORE_ENDPOINT")
561
+ core_endpt = os.getenv(EnvVar.ENDPOINT.value)
508
562
  if core_endpt is None:
509
563
  raise BackendError("Endpoint not set as environment variables.")
510
564
  self._endpoint_core = self._sanitize_endpoint(core_endpt)
511
565
 
512
- issr_endpt = os.getenv("DHCORE_ISSUER")
566
+ issr_endpt = os.getenv(EnvVar.ISSUER.value)
513
567
  if issr_endpt is not None:
514
568
  self._endpoint_issuer = self._sanitize_endpoint(issr_endpt)
515
569
 
@@ -521,9 +575,8 @@ class ClientDHCore(Client):
521
575
  -------
522
576
  None
523
577
  """
524
- parsed = urlparse(endpoint)
525
- if parsed.scheme not in ["http", "https"]:
526
- raise BackendError("Invalid endpoint scheme.")
578
+ if not has_remote_scheme(endpoint):
579
+ raise BackendError("Invalid endpoint scheme. Must start with http:// or https://.")
527
580
 
528
581
  endpoint = endpoint.strip()
529
582
  return endpoint.removesuffix("/")
@@ -536,19 +589,19 @@ class ClientDHCore(Client):
536
589
  -------
537
590
  None
538
591
  """
539
- self._user = os.getenv("DHCORE_USER", FALLBACK_USER)
540
- self._refresh_token = os.getenv("DHCORE_REFRESH_TOKEN")
541
- self._client_id = os.getenv("DHCORE_CLIENT_ID")
592
+ self._user = os.getenv(EnvVar.USER.value, FALLBACK_USER)
593
+ self._refresh_token = os.getenv(EnvVar.REFRESH_TOKEN.value)
594
+ self._client_id = os.getenv(EnvVar.CLIENT_ID.value)
542
595
 
543
- token = os.getenv("DHCORE_ACCESS_TOKEN")
596
+ token = os.getenv(EnvVar.ACCESS_TOKEN.value)
544
597
  if token is not None and token != "":
545
- self._auth_type = "oauth2"
598
+ self._auth_type = AuthType.OAUTH2.value
546
599
  self._access_token = token
547
600
  return
548
601
 
549
- password = os.getenv("DHCORE_PASSWORD")
602
+ password = os.getenv(EnvVar.PASSWORD.value)
550
603
  if self._user is not None and password is not None:
551
- self._auth_type = "basic"
604
+ self._auth_type = AuthType.BASIC.value
552
605
  self._password = password
553
606
  return
554
607
 
@@ -566,12 +619,12 @@ class ClientDHCore(Client):
566
619
 
567
620
  # Call refresh token endpoint
568
621
  # Try token from env
569
- refresh_token = os.getenv("DHCORE_REFRESH_TOKEN")
622
+ refresh_token = os.getenv(EnvVar.REFRESH_TOKEN.value)
570
623
  response = self._call_refresh_token_endpoint(url, refresh_token)
571
624
 
572
625
  # Otherwise try token from file
573
626
  if response.status_code in (400, 401, 403):
574
- refresh_token = get_key(ENV_FILE, "DHCORE_REFRESH_TOKEN")
627
+ refresh_token = get_key(ENV_FILE, EnvVar.REFRESH_TOKEN.value)
575
628
  response = self._call_refresh_token_endpoint(url, refresh_token)
576
629
 
577
630
  response.raise_for_status()
@@ -641,9 +694,9 @@ class ClientDHCore(Client):
641
694
  """
642
695
  keys = {}
643
696
  if self._access_token is not None:
644
- keys["DHCORE_ACCESS_TOKEN"] = self._access_token
697
+ keys[EnvVar.ACCESS_TOKEN.value] = self._access_token
645
698
  if self._refresh_token is not None:
646
- keys["DHCORE_REFRESH_TOKEN"] = self._refresh_token
699
+ keys[EnvVar.REFRESH_TOKEN.value] = self._refresh_token
647
700
 
648
701
  for k, v in keys.items():
649
702
  set_key(dotenv_path=ENV_FILE, key_to_set=k, value_to_set=v)
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class EnvVar(Enum):
7
+ """
8
+ Environment variables.
9
+ """
10
+
11
+ ENDPOINT = "DHCORE_ENDPOINT"
12
+ ISSUER = "DHCORE_ISSUER"
13
+ USER = "DHCORE_USER"
14
+ PASSWORD = "DHCORE_PASSWORD"
15
+ CLIENT_ID = "DHCORE_CLIENT_ID"
16
+ ACCESS_TOKEN = "DHCORE_ACCESS_TOKEN"
17
+ REFRESH_TOKEN = "DHCORE_REFRESH_TOKEN"
18
+
19
+
20
+ class AuthType(Enum):
21
+ """
22
+ Authentication types.
23
+ """
24
+
25
+ BASIC = "basic"
26
+ OAUTH2 = "oauth2"
@@ -19,5 +19,5 @@ ENV_FILE = Path.home() / ".dhcore"
19
19
 
20
20
  # API levels that are supported
21
21
  MAX_API_LEVEL = 20
22
- MIN_API_LEVEL = 8
23
- LIB_VERSION = 8
22
+ MIN_API_LEVEL = 9
23
+ LIB_VERSION = 9
@@ -3,7 +3,8 @@ from __future__ import annotations
3
3
  import os
4
4
  import typing
5
5
 
6
- from digitalhub.client.api import check_client_exists, get_client
6
+ from digitalhub.client.api import get_client
7
+ from digitalhub.client.dhcore.enums import AuthType, EnvVar
7
8
 
8
9
  if typing.TYPE_CHECKING:
9
10
  from digitalhub.client.dhcore.client import ClientDHCore
@@ -43,20 +44,19 @@ def set_dhcore_env(
43
44
  None
44
45
  """
45
46
  if endpoint is not None:
46
- os.environ["DHCORE_ENDPOINT"] = endpoint
47
+ os.environ[EnvVar.ENDPOINT.value] = endpoint
47
48
  if user is not None:
48
- os.environ["DHCORE_USER"] = user
49
+ os.environ[EnvVar.USER.value] = user
49
50
  if password is not None:
50
- os.environ["DHCORE_PASSWORD"] = password
51
+ os.environ[EnvVar.PASSWORD.value] = password
51
52
  if access_token is not None:
52
- os.environ["DHCORE_ACCESS_TOKEN"] = access_token
53
+ os.environ[EnvVar.ACCESS_TOKEN.value] = access_token
53
54
  if refresh_token is not None:
54
- os.environ["DHCORE_REFRESH_TOKEN"] = refresh_token
55
+ os.environ[EnvVar.REFRESH_TOKEN.value] = refresh_token
55
56
  if client_id is not None:
56
- os.environ["DHCORE_CLIENT_ID"] = client_id
57
+ os.environ[EnvVar.CLIENT_ID.value] = client_id
57
58
 
58
- if check_client_exists(local=False):
59
- update_client_from_env()
59
+ update_client_from_env()
60
60
 
61
61
 
62
62
  def update_client_from_env() -> None:
@@ -70,16 +70,16 @@ def update_client_from_env() -> None:
70
70
  client: ClientDHCore = get_client(local=False)
71
71
 
72
72
  # Update endpoint
73
- endpoint = os.getenv("DHCORE_ENDPOINT")
73
+ endpoint = os.getenv(EnvVar.ENDPOINT.value)
74
74
  if endpoint is not None:
75
75
  client._endpoint_core = endpoint
76
76
 
77
77
  # Update auth
78
78
 
79
79
  # If token is set, it will override the other auth options
80
- access_token = os.getenv("DHCORE_ACCESS_TOKEN")
81
- refresh_token = os.getenv("DHCORE_REFRESH_TOKEN")
82
- client_id = os.getenv("DHCORE_CLIENT_ID")
80
+ access_token = os.getenv(EnvVar.ACCESS_TOKEN.value)
81
+ refresh_token = os.getenv(EnvVar.REFRESH_TOKEN.value)
82
+ client_id = os.getenv(EnvVar.CLIENT_ID.value)
83
83
 
84
84
  if access_token is not None:
85
85
  if refresh_token is not None:
@@ -87,16 +87,16 @@ def update_client_from_env() -> None:
87
87
  if client_id is not None:
88
88
  client._client_id = client_id
89
89
  client._access_token = access_token
90
- client._auth_type = "oauth2"
90
+ client._auth_type = AuthType.OAUTH2.value
91
91
  return
92
92
 
93
93
  # Otherwise, if user and password are set, basic auth will be used
94
- username = os.getenv("DHCORE_USER")
95
- password = os.getenv("DHCORE_PASSWORD")
94
+ username = os.getenv(EnvVar.USER.value)
95
+ password = os.getenv(EnvVar.PASSWORD.value)
96
96
  if username is not None and password is not None:
97
97
  client._user = username
98
98
  client._password = password
99
- client._auth_type = "basic"
99
+ client._auth_type = AuthType.BASIC.value
100
100
 
101
101
 
102
102
  def refresh_token() -> None: