digitalhub 0.9.2__py3-none-any.whl → 0.10.0b1__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 (68) hide show
  1. digitalhub/__init__.py +2 -3
  2. digitalhub/client/_base/client.py +3 -2
  3. digitalhub/client/dhcore/api_builder.py +5 -0
  4. digitalhub/client/dhcore/client.py +27 -399
  5. digitalhub/client/dhcore/configurator.py +339 -0
  6. digitalhub/client/dhcore/error_parser.py +107 -0
  7. digitalhub/client/dhcore/models.py +13 -23
  8. digitalhub/client/dhcore/utils.py +4 -44
  9. digitalhub/client/local/api_builder.py +9 -17
  10. digitalhub/client/local/client.py +12 -2
  11. digitalhub/client/local/enums.py +11 -0
  12. digitalhub/configurator/api.py +31 -0
  13. digitalhub/configurator/configurator.py +194 -0
  14. digitalhub/configurator/credentials_store.py +65 -0
  15. digitalhub/configurator/ini_module.py +74 -0
  16. digitalhub/entities/_base/_base/entity.py +2 -2
  17. digitalhub/entities/_base/material/entity.py +19 -6
  18. digitalhub/entities/_base/material/utils.py +2 -2
  19. digitalhub/entities/_commons/enums.py +1 -0
  20. digitalhub/entities/_commons/models.py +9 -0
  21. digitalhub/entities/_commons/utils.py +25 -0
  22. digitalhub/entities/_operations/processor.py +103 -107
  23. digitalhub/entities/artifact/crud.py +3 -3
  24. digitalhub/entities/artifact/utils.py +1 -1
  25. digitalhub/entities/dataitem/_base/entity.py +2 -2
  26. digitalhub/entities/dataitem/crud.py +3 -3
  27. digitalhub/entities/dataitem/table/entity.py +2 -2
  28. digitalhub/{utils/data_utils.py → entities/dataitem/table/utils.py} +43 -51
  29. digitalhub/entities/dataitem/utils.py +6 -3
  30. digitalhub/entities/model/_base/entity.py +172 -0
  31. digitalhub/entities/model/_base/spec.py +0 -10
  32. digitalhub/entities/model/_base/status.py +10 -0
  33. digitalhub/entities/model/crud.py +3 -3
  34. digitalhub/entities/model/huggingface/spec.py +6 -3
  35. digitalhub/entities/model/mlflow/models.py +2 -2
  36. digitalhub/entities/model/mlflow/spec.py +1 -3
  37. digitalhub/entities/model/mlflow/utils.py +44 -5
  38. digitalhub/entities/run/_base/entity.py +149 -0
  39. digitalhub/entities/run/_base/status.py +12 -0
  40. digitalhub/entities/task/_base/spec.py +2 -0
  41. digitalhub/entities/task/crud.py +4 -0
  42. digitalhub/readers/{_commons → pandas}/enums.py +4 -0
  43. digitalhub/readers/pandas/reader.py +58 -10
  44. digitalhub/stores/_base/store.py +1 -49
  45. digitalhub/stores/api.py +8 -33
  46. digitalhub/stores/builder.py +44 -161
  47. digitalhub/stores/local/store.py +4 -18
  48. digitalhub/stores/remote/store.py +3 -10
  49. digitalhub/stores/s3/configurator.py +107 -0
  50. digitalhub/stores/s3/enums.py +17 -0
  51. digitalhub/stores/s3/models.py +21 -0
  52. digitalhub/stores/s3/store.py +8 -28
  53. digitalhub/{utils/s3_utils.py → stores/s3/utils.py} +7 -3
  54. digitalhub/stores/sql/configurator.py +88 -0
  55. digitalhub/stores/sql/enums.py +16 -0
  56. digitalhub/stores/sql/models.py +24 -0
  57. digitalhub/stores/sql/store.py +14 -57
  58. digitalhub/utils/exceptions.py +6 -0
  59. digitalhub/utils/generic_utils.py +9 -8
  60. digitalhub/utils/uri_utils.py +1 -1
  61. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b1.dist-info}/METADATA +5 -6
  62. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b1.dist-info}/RECORD +67 -54
  63. test/local/imports/test_imports.py +0 -1
  64. digitalhub/client/dhcore/env.py +0 -23
  65. /digitalhub/{readers/_commons → configurator}/__init__.py +0 -0
  66. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b1.dist-info}/LICENSE.txt +0 -0
  67. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b1.dist-info}/WHEEL +0 -0
  68. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b1.dist-info}/top_level.txt +0 -0
digitalhub/__init__.py CHANGED
@@ -1,4 +1,3 @@
1
- __version__ = "0.9.0b0"
2
1
  from digitalhub.entities.artifact.crud import (
3
2
  delete_artifact,
4
3
  get_artifact,
@@ -85,15 +84,15 @@ from digitalhub.entities.workflow.crud import (
85
84
  )
86
85
 
87
86
  try:
88
- from digitalhub.entities.model.mlflow.utils import from_mlflow_run
87
+ from digitalhub.entities.model.mlflow.utils import from_mlflow_run, get_mlflow_model_metrics
89
88
  except ImportError:
90
89
  ...
91
90
 
92
91
  from digitalhub.client.dhcore.utils import refresh_token, set_dhcore_env
92
+ from digitalhub.configurator.api import get_current_env, set_current_env
93
93
 
94
94
  # Register entities into registry
95
95
  from digitalhub.factory.utils import register_entities, register_runtimes_entities
96
- from digitalhub.stores.api import set_store
97
96
 
98
97
  register_entities()
99
98
  register_runtimes_entities()
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import typing
4
4
  from abc import abstractmethod
5
+ from typing import Any
5
6
 
6
7
  if typing.TYPE_CHECKING:
7
8
  from digitalhub.client._base.api_builder import ClientApiBuilder
@@ -26,7 +27,7 @@ class Client:
26
27
  ##############################
27
28
 
28
29
  @abstractmethod
29
- def create_object(self, api: str, obj: dict, **kwargs) -> dict:
30
+ def create_object(self, api: str, obj: Any, **kwargs) -> dict:
30
31
  """
31
32
  Create object method.
32
33
  """
@@ -38,7 +39,7 @@ class Client:
38
39
  """
39
40
 
40
41
  @abstractmethod
41
- def update_object(self, api: str, obj: dict, **kwargs) -> dict:
42
+ def update_object(self, api: str, obj: Any, **kwargs) -> dict:
42
43
  """
43
44
  Update object method.
44
45
  """
@@ -96,5 +96,10 @@ class ClientDHCoreApiBuilder(ClientApiBuilder):
96
96
  return f"{API_CONTEXT}/{project}/{entity_type}/{kwargs['entity_id']}/files/info"
97
97
  elif operation == BackendOperations.SEARCH.value:
98
98
  return f"{API_CONTEXT}/{project}/solr/search/item"
99
+ elif operation == BackendOperations.METRICS.value:
100
+ if kwargs["metric_name"] is not None:
101
+ return f"{API_CONTEXT}/{project}/{entity_type}/{kwargs['entity_id']}/metrics/{kwargs['metric_name']}"
102
+ else:
103
+ return f"{API_CONTEXT}/{project}/{entity_type}/{kwargs['entity_id']}/metrics"
99
104
 
100
105
  raise BackendError(f"Invalid operation '{operation}' for entity type '{entity_type}' in DHCore.")
@@ -1,31 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
- import datetime
4
- import json
5
- import os
6
3
  import typing
7
- from warnings import warn
4
+ from typing import Any
8
5
 
9
- from dotenv import get_key, set_key
10
6
  from requests import request
11
- from requests.exceptions import HTTPError, JSONDecodeError, RequestException
7
+ from requests.exceptions import JSONDecodeError
12
8
 
13
9
  from digitalhub.client._base.client import Client
14
10
  from digitalhub.client.dhcore.api_builder import ClientDHCoreApiBuilder
15
- from digitalhub.client.dhcore.enums import AuthType, DhcoreEnvVar
16
- from digitalhub.client.dhcore.env import ENV_FILE, FALLBACK_USER, LIB_VERSION, MAX_API_LEVEL, MIN_API_LEVEL
11
+ from digitalhub.client.dhcore.configurator import ClientDHCoreConfigurator
12
+ from digitalhub.client.dhcore.error_parser import ErrorParser
17
13
  from digitalhub.client.dhcore.key_builder import ClientDHCoreKeyBuilder
18
- from digitalhub.client.dhcore.models import BasicAuth, OAuth2TokenAuth
19
- from digitalhub.utils.exceptions import (
20
- BackendError,
21
- BadRequestError,
22
- EntityAlreadyExistsError,
23
- EntityNotExistsError,
24
- ForbiddenError,
25
- MissingSpecError,
26
- UnauthorizedError,
27
- )
28
- from digitalhub.utils.uri_utils import has_remote_scheme
14
+ from digitalhub.utils.exceptions import BackendError
15
+ from digitalhub.utils.generic_utils import dump_json
29
16
 
30
17
  if typing.TYPE_CHECKING:
31
18
  from requests import Response
@@ -56,28 +43,18 @@ class ClientDHCore(Client):
56
43
  # Key builder
57
44
  self._key_builder = ClientDHCoreKeyBuilder()
58
45
 
59
- # Endpoints
60
- self._endpoint_core: str | None = None
61
- self._endpoint_issuer: str | None = None
46
+ # Error parser
47
+ self._error_parser = ErrorParser()
62
48
 
63
- # Authentication
64
- self._auth_type: str | None = None
65
-
66
- # Basic
67
- self._user: str | None = None
68
- self._password: str | None = None
69
-
70
- # OAuth2
71
- self._access_token: str | None = None
72
- self._refresh_token: str | None = None
73
-
74
- self._configure(config)
49
+ # Client Configurator
50
+ self._configurator = ClientDHCoreConfigurator()
51
+ self._configurator.configure(config)
75
52
 
76
53
  ##############################
77
54
  # CRUD methods
78
55
  ##############################
79
56
 
80
- def create_object(self, api: str, obj: dict, **kwargs) -> dict:
57
+ def create_object(self, api: str, obj: Any, **kwargs) -> dict:
81
58
  """
82
59
  Create an object in DHCore.
83
60
 
@@ -85,7 +62,7 @@ class ClientDHCore(Client):
85
62
  ----------
86
63
  api : str
87
64
  Create API.
88
- obj : dict
65
+ obj : Any
89
66
  Object to create.
90
67
  **kwargs : dict
91
68
  Keyword arguments to pass to the request.
@@ -98,7 +75,7 @@ class ClientDHCore(Client):
98
75
  if "headers" not in kwargs:
99
76
  kwargs["headers"] = {}
100
77
  kwargs["headers"]["Content-Type"] = "application/json"
101
- kwargs["data"] = json.dumps(obj, default=ClientDHCore._json_serialize)
78
+ kwargs["data"] = dump_json(obj)
102
79
  return self._prepare_call("POST", api, **kwargs)
103
80
 
104
81
  def read_object(self, api: str, **kwargs) -> dict:
@@ -119,7 +96,7 @@ class ClientDHCore(Client):
119
96
  """
120
97
  return self._prepare_call("GET", api, **kwargs)
121
98
 
122
- def update_object(self, api: str, obj: dict, **kwargs) -> dict:
99
+ def update_object(self, api: str, obj: Any, **kwargs) -> dict:
123
100
  """
124
101
  Update an object in DHCore.
125
102
 
@@ -140,7 +117,7 @@ class ClientDHCore(Client):
140
117
  if "headers" not in kwargs:
141
118
  kwargs["headers"] = {}
142
119
  kwargs["headers"]["Content-Type"] = "application/json"
143
- kwargs["data"] = json.dumps(obj, default=ClientDHCore._json_serialize)
120
+ kwargs["data"] = dump_json(obj)
144
121
  return self._prepare_call("PUT", api, **kwargs)
145
122
 
146
123
  def delete_object(self, api: str, **kwargs) -> dict:
@@ -256,7 +233,7 @@ class ClientDHCore(Client):
256
233
  if "sort" not in kwargs["params"]:
257
234
  kwargs["params"]["sort"] = "metadata.updated,DESC"
258
235
 
259
- objects_with_highlights = []
236
+ objects_with_highlights: list[dict] = []
260
237
  while True:
261
238
  resp = self._prepare_call("GET", api, **kwargs)
262
239
  contents = resp["content"]
@@ -277,25 +254,6 @@ class ClientDHCore(Client):
277
254
  # Call methods
278
255
  ##############################
279
256
 
280
- @staticmethod
281
- def _json_serialize(obj: dict) -> dict:
282
- """
283
- JSON datetime to ISO format serializer.
284
-
285
- Parameters
286
- ----------
287
- obj : dict
288
- The object to serialize.
289
-
290
- Returns
291
- -------
292
- dict
293
- The serialized object.
294
- """
295
- if isinstance(obj, (datetime.datetime, datetime.date)):
296
- return obj.isoformat()
297
- raise TypeError("Type %s not serializable" % type(obj))
298
-
299
257
  def _prepare_call(self, call_type: str, api: str, **kwargs) -> dict:
300
258
  """
301
259
  Prepare a call to the DHCore API.
@@ -316,32 +274,10 @@ class ClientDHCore(Client):
316
274
  """
317
275
  if kwargs is None:
318
276
  kwargs = {}
319
- url = self._endpoint_core + api
320
- kwargs = self._set_auth(kwargs)
277
+ url = self._configurator.build_url(api)
278
+ kwargs = self._configurator.set_request_auth(kwargs)
321
279
  return self._make_call(call_type, url, **kwargs)
322
280
 
323
- def _set_auth(self, kwargs: dict) -> dict:
324
- """
325
- Set the authentication type.
326
-
327
- Parameters
328
- ----------
329
- kwargs : dict
330
- Keyword arguments to pass to the request.
331
-
332
- Returns
333
- -------
334
- dict
335
- Keyword arguments with the authentication parameters.
336
- """
337
- if self._auth_type == AuthType.BASIC.value:
338
- kwargs["auth"] = self._user, self._password
339
- elif self._auth_type == AuthType.OAUTH2.value:
340
- if "headers" not in kwargs:
341
- kwargs["headers"] = {}
342
- kwargs["headers"]["Authorization"] = f"Bearer {self._access_token}"
343
- return kwargs
344
-
345
281
  def _make_call(self, call_type: str, url: str, refresh_token: bool = True, **kwargs) -> dict:
346
282
  """
347
283
  Make a call to the DHCore API.
@@ -364,126 +300,20 @@ class ClientDHCore(Client):
364
300
  response = request(call_type, url, timeout=60, **kwargs)
365
301
 
366
302
  # Evaluate DHCore API version
367
- self._check_core_version(response)
303
+ self._configurator.check_core_version(response)
368
304
 
369
305
  # Handle token refresh
370
- if response.status_code in [401] and refresh_token:
371
- self._get_new_access_token()
372
- kwargs = self._set_auth(kwargs)
306
+ if response.status_code in [401] and refresh_token and self._configurator.oauth2_auth():
307
+ self._configurator.get_new_access_token()
308
+ kwargs = self._configurator.set_request_auth(kwargs)
373
309
  return self._make_call(call_type, url, refresh_token=False, **kwargs)
374
310
 
375
- self._raise_for_error(response)
376
- return self._parse_response(response)
377
-
378
- def _check_core_version(self, response: Response) -> None:
379
- """
380
- Raise an exception if DHCore API version is not supported.
381
-
382
- Parameters
383
- ----------
384
- response : Response
385
- The response object.
311
+ self._error_parser.parse(response)
312
+ return self._dictify_response(response)
386
313
 
387
- Returns
388
- -------
389
- None
390
- """
391
- if "X-Api-Level" in response.headers:
392
- core_api_level = int(response.headers["X-Api-Level"])
393
- if not (MIN_API_LEVEL <= core_api_level <= MAX_API_LEVEL):
394
- raise BackendError("Backend API level not supported.")
395
- if LIB_VERSION < core_api_level:
396
- warn("Backend API level is higher than library version. You should consider updating the library.")
397
-
398
- def _raise_for_error(self, response: Response) -> None:
399
- """
400
- Handle DHCore API errors.
401
-
402
- Parameters
403
- ----------
404
- response : Response
405
- The response object.
406
-
407
- Returns
408
- -------
409
- None
410
- """
411
- try:
412
- response.raise_for_status()
413
-
414
- # Backend errors
415
- except RequestException as e:
416
- # Handle timeout
417
- if isinstance(e, TimeoutError):
418
- msg = "Request to DHCore backend timed out."
419
- raise TimeoutError(msg)
420
-
421
- # Handle connection error
422
- elif isinstance(e, ConnectionError):
423
- msg = "Unable to connect to DHCore backend."
424
- raise ConnectionError(msg)
425
-
426
- # Handle HTTP errors
427
- elif isinstance(e, HTTPError):
428
- txt_resp = f"Response: {response.text}."
429
-
430
- # Bad request
431
- if response.status_code == 400:
432
- # Missing spec in backend
433
- if "missing spec" in response.text:
434
- msg = f"Missing spec in backend. {txt_resp}"
435
- raise MissingSpecError(msg)
436
-
437
- # Duplicated entity
438
- elif "Duplicated entity" in response.text:
439
- msg = f"Entity already exists. {txt_resp}"
440
- raise EntityAlreadyExistsError(msg)
441
-
442
- # Other errors
443
- else:
444
- msg = f"Bad request. {txt_resp}"
445
- raise BadRequestError(msg)
446
-
447
- # Unauthorized errors
448
- elif response.status_code == 401:
449
- msg = f"Unauthorized. {txt_resp}"
450
- raise UnauthorizedError(msg)
451
-
452
- # Forbidden errors
453
- elif response.status_code == 403:
454
- msg = f"Forbidden. {txt_resp}"
455
- raise ForbiddenError(msg)
456
-
457
- # Entity not found
458
- elif response.status_code == 404:
459
- # Put with entity not found
460
- if "No such EntityName" in response.text:
461
- msg = f"Entity does not exists. {txt_resp}"
462
- raise EntityNotExistsError(msg)
463
-
464
- # Other cases
465
- else:
466
- msg = f"Not found. {txt_resp}"
467
- raise BackendError(msg)
468
-
469
- # Other errors
470
- else:
471
- msg = f"Backend error. {txt_resp}"
472
- raise BackendError(msg) from e
473
-
474
- # Other requests errors
475
- else:
476
- msg = f"Some error occurred. {e}"
477
- raise BackendError(msg) from e
478
-
479
- # Other generic errors
480
- except Exception as e:
481
- msg = f"Some error occurred: {e}"
482
- raise RuntimeError(msg) from e
483
-
484
- def _parse_response(self, response: Response) -> dict:
314
+ def _dictify_response(self, response: Response) -> dict:
485
315
  """
486
- Parse the response object.
316
+ Return dict from response.
487
317
 
488
318
  Parameters
489
319
  ----------
@@ -502,208 +332,6 @@ class ClientDHCore(Client):
502
332
  return {}
503
333
  raise BackendError("Backend response could not be parsed.")
504
334
 
505
- ##############################
506
- # Configuration methods
507
- ##############################
508
-
509
- def _configure(self, config: dict | None = None) -> None:
510
- """
511
- Configure the client attributes with config (given or from
512
- environment).
513
- Regarding authentication parameters, the config parameter
514
- takes precedence over the env variables, and the token
515
- over the basic auth. Furthermore, the config parameter is
516
- validated against the proper pydantic model.
517
-
518
- Parameters
519
- ----------
520
- config : dict
521
- Configuration dictionary.
522
-
523
- Returns
524
- -------
525
- None
526
- """
527
- self._get_endpoints_from_env()
528
-
529
- if config is not None:
530
- if config.get("access_token") is not None:
531
- config = OAuth2TokenAuth(**config)
532
- self._user = config.user
533
- self._access_token = config.access_token
534
- self._refresh_token = config.refresh_token
535
- self._client_id = config.client_id
536
- self._auth_type = AuthType.OAUTH2.value
537
-
538
- elif config.get("user") is not None and config.get("password") is not None:
539
- config = BasicAuth(**config)
540
- self._user = config.user
541
- self._password = config.password
542
- self._auth_type = AuthType.BASIC.value
543
-
544
- return
545
-
546
- self._get_auth_from_env()
547
-
548
- # Propagate access and refresh token to env file
549
- self._write_env()
550
-
551
- def _get_endpoints_from_env(self) -> None:
552
- """
553
- Get the DHCore endpoint and token issuer endpoint from env.
554
-
555
- Returns
556
- -------
557
- None
558
-
559
- Raises
560
- ------
561
- Exception
562
- If the endpoint of DHCore is not set in the env variables.
563
- """
564
- core_endpt = os.getenv(DhcoreEnvVar.ENDPOINT.value)
565
- if core_endpt is None:
566
- raise BackendError("Endpoint not set as environment variables.")
567
- self._endpoint_core = self._sanitize_endpoint(core_endpt)
568
-
569
- issr_endpt = os.getenv(DhcoreEnvVar.ISSUER.value)
570
- if issr_endpt is not None:
571
- self._endpoint_issuer = self._sanitize_endpoint(issr_endpt)
572
-
573
- def _sanitize_endpoint(self, endpoint: str) -> str:
574
- """
575
- Sanitize the endpoint.
576
-
577
- Returns
578
- -------
579
- None
580
- """
581
- if not has_remote_scheme(endpoint):
582
- raise BackendError("Invalid endpoint scheme. Must start with http:// or https://.")
583
-
584
- endpoint = endpoint.strip()
585
- return endpoint.removesuffix("/")
586
-
587
- def _get_auth_from_env(self) -> None:
588
- """
589
- Get authentication parameters from the env.
590
-
591
- Returns
592
- -------
593
- None
594
- """
595
- self._user = os.getenv(DhcoreEnvVar.USER.value, FALLBACK_USER)
596
- self._refresh_token = os.getenv(DhcoreEnvVar.REFRESH_TOKEN.value)
597
- self._client_id = os.getenv(DhcoreEnvVar.CLIENT_ID.value)
598
-
599
- token = os.getenv(DhcoreEnvVar.ACCESS_TOKEN.value)
600
- if token is not None and token != "":
601
- self._auth_type = AuthType.OAUTH2.value
602
- self._access_token = token
603
- return
604
-
605
- password = os.getenv(DhcoreEnvVar.PASSWORD.value)
606
- if self._user is not None and password is not None:
607
- self._auth_type = AuthType.BASIC.value
608
- self._password = password
609
- return
610
-
611
- def _get_new_access_token(self) -> None:
612
- """
613
- Get a new access token.
614
-
615
- Returns
616
- -------
617
- None
618
- """
619
- # Call issuer and get endpoint for
620
- # refreshing access token
621
- url = self._get_refresh_endpoint()
622
-
623
- # Call refresh token endpoint
624
- # Try token from env
625
- refresh_token = os.getenv(DhcoreEnvVar.REFRESH_TOKEN.value)
626
- response = self._call_refresh_token_endpoint(url, refresh_token)
627
-
628
- # Otherwise try token from file
629
- if response.status_code in (400, 401, 403):
630
- refresh_token = get_key(ENV_FILE, DhcoreEnvVar.REFRESH_TOKEN.value)
631
- response = self._call_refresh_token_endpoint(url, refresh_token)
632
-
633
- response.raise_for_status()
634
- dict_response = response.json()
635
-
636
- # Read new access token and refresh token
637
- self._access_token = dict_response["access_token"]
638
- self._refresh_token = dict_response["refresh_token"]
639
-
640
- # Propagate new access token to env
641
- self._write_env()
642
-
643
- def _get_refresh_endpoint(self) -> str:
644
- """
645
- Get the refresh endpoint.
646
-
647
- Returns
648
- -------
649
- str
650
- Refresh endpoint.
651
- """
652
- # Get issuer endpoint
653
- if self._endpoint_issuer is None:
654
- raise BackendError("Issuer endpoint not set.")
655
-
656
- # Standard issuer endpoint path
657
- url = self._endpoint_issuer + "/.well-known/openid-configuration"
658
-
659
- # Call
660
- r = request("GET", url, timeout=60)
661
- self._raise_for_error(r)
662
- return r.json().get("token_endpoint")
663
-
664
- def _call_refresh_token_endpoint(self, url: str, refresh_token: str) -> Response:
665
- """
666
- Call the refresh token endpoint.
667
-
668
- Parameters
669
- ----------
670
- url : str
671
- Refresh token endpoint.
672
- refresh_token : str
673
- Refresh token.
674
-
675
- Returns
676
- -------
677
- Response
678
- Response object.
679
- """
680
- # Send request to get new access token
681
- payload = {
682
- "grant_type": "refresh_token",
683
- "client_id": self._client_id,
684
- "refresh_token": refresh_token,
685
- }
686
- headers = {"Content-Type": "application/x-www-form-urlencoded"}
687
- return request("POST", url, data=payload, headers=headers, timeout=60)
688
-
689
- def _write_env(self) -> None:
690
- """
691
- Write the env variables to the .dhcore file.
692
- It will overwrite any existing env variables.
693
-
694
- Returns
695
- -------
696
- None
697
- """
698
- keys = {}
699
- if self._access_token is not None:
700
- keys[DhcoreEnvVar.ACCESS_TOKEN.value] = self._access_token
701
- if self._refresh_token is not None:
702
- keys[DhcoreEnvVar.REFRESH_TOKEN.value] = self._refresh_token
703
-
704
- for k, v in keys.items():
705
- set_key(dotenv_path=ENV_FILE, key_to_set=k, value_to_set=v)
706
-
707
335
  ##############################
708
336
  # Interface methods
709
337
  ##############################