datacrunch 1.15.0__py3-none-any.whl → 1.17.1__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.
Files changed (72) hide show
  1. datacrunch/__init__.py +53 -1
  2. datacrunch/datacrunch.py +44 -81
  3. datacrunch-1.17.1.dist-info/METADATA +30 -0
  4. datacrunch-1.17.1.dist-info/RECORD +5 -0
  5. datacrunch-1.17.1.dist-info/WHEEL +4 -0
  6. datacrunch/InferenceClient/__init__.py +0 -3
  7. datacrunch/InferenceClient/inference_client.py +0 -379
  8. datacrunch/__version__.py +0 -1
  9. datacrunch/authentication/__init__.py +0 -0
  10. datacrunch/authentication/authentication.py +0 -112
  11. datacrunch/balance/__init__.py +0 -0
  12. datacrunch/balance/balance.py +0 -52
  13. datacrunch/constants.py +0 -107
  14. datacrunch/containers/__init__.py +0 -33
  15. datacrunch/containers/containers.py +0 -1081
  16. datacrunch/exceptions.py +0 -29
  17. datacrunch/helpers.py +0 -13
  18. datacrunch/http_client/__init__.py +0 -0
  19. datacrunch/http_client/http_client.py +0 -241
  20. datacrunch/images/__init__.py +0 -0
  21. datacrunch/images/images.py +0 -87
  22. datacrunch/instance_types/__init__.py +0 -0
  23. datacrunch/instance_types/instance_types.py +0 -188
  24. datacrunch/instances/__init__.py +0 -0
  25. datacrunch/instances/instances.py +0 -247
  26. datacrunch/locations/__init__.py +0 -0
  27. datacrunch/locations/locations.py +0 -16
  28. datacrunch/ssh_keys/__init__.py +0 -0
  29. datacrunch/ssh_keys/ssh_keys.py +0 -112
  30. datacrunch/startup_scripts/__init__.py +0 -0
  31. datacrunch/startup_scripts/startup_scripts.py +0 -113
  32. datacrunch/volume_types/__init__.py +0 -0
  33. datacrunch/volume_types/volume_types.py +0 -66
  34. datacrunch/volumes/__init__.py +0 -0
  35. datacrunch/volumes/volumes.py +0 -398
  36. datacrunch-1.15.0.dist-info/METADATA +0 -208
  37. datacrunch-1.15.0.dist-info/RECORD +0 -69
  38. datacrunch-1.15.0.dist-info/WHEEL +0 -5
  39. datacrunch-1.15.0.dist-info/licenses/LICENSE +0 -21
  40. datacrunch-1.15.0.dist-info/top_level.txt +0 -2
  41. tests/__init__.py +0 -0
  42. tests/integration_tests/__init__.py +0 -0
  43. tests/integration_tests/conftest.py +0 -20
  44. tests/integration_tests/test_instances.py +0 -36
  45. tests/integration_tests/test_locations.py +0 -65
  46. tests/integration_tests/test_volumes.py +0 -94
  47. tests/unit_tests/__init__.py +0 -0
  48. tests/unit_tests/authentication/__init__.py +0 -0
  49. tests/unit_tests/authentication/test_authentication.py +0 -202
  50. tests/unit_tests/balance/__init__.py +0 -0
  51. tests/unit_tests/balance/test_balance.py +0 -25
  52. tests/unit_tests/conftest.py +0 -21
  53. tests/unit_tests/containers/__init__.py +0 -1
  54. tests/unit_tests/containers/test_containers.py +0 -959
  55. tests/unit_tests/http_client/__init__.py +0 -0
  56. tests/unit_tests/http_client/test_http_client.py +0 -193
  57. tests/unit_tests/images/__init__.py +0 -0
  58. tests/unit_tests/images/test_images.py +0 -41
  59. tests/unit_tests/instance_types/__init__.py +0 -0
  60. tests/unit_tests/instance_types/test_instance_types.py +0 -87
  61. tests/unit_tests/instances/__init__.py +0 -0
  62. tests/unit_tests/instances/test_instances.py +0 -483
  63. tests/unit_tests/ssh_keys/__init__.py +0 -0
  64. tests/unit_tests/ssh_keys/test_ssh_keys.py +0 -198
  65. tests/unit_tests/startup_scripts/__init__.py +0 -0
  66. tests/unit_tests/startup_scripts/test_startup_scripts.py +0 -196
  67. tests/unit_tests/test_datacrunch.py +0 -65
  68. tests/unit_tests/test_exceptions.py +0 -33
  69. tests/unit_tests/volume_types/__init__.py +0 -0
  70. tests/unit_tests/volume_types/test_volume_types.py +0 -50
  71. tests/unit_tests/volumes/__init__.py +0 -0
  72. tests/unit_tests/volumes/test_volumes.py +0 -641
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2020 DataCrunch Oy
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,2 +0,0 @@
1
- datacrunch
2
- tests
tests/__init__.py DELETED
File without changes
File without changes
@@ -1,20 +0,0 @@
1
- import os
2
- import pytest
3
- from dotenv import load_dotenv
4
- from datacrunch.datacrunch import DataCrunchClient
5
-
6
- """
7
- Make sure to run the server and the account has enough balance before running the tests
8
- """
9
-
10
- BASE_URL = "http://localhost:3010/v1"
11
-
12
- # Load env variables, make sure there's an env file with valid client credentials
13
- load_dotenv()
14
- CLIENT_SECRET = os.getenv('DATACRUNCH_CLIENT_SECRET')
15
- CLIENT_ID = os.getenv('DATACRUNCH_CLIENT_ID')
16
-
17
-
18
- @pytest.fixture
19
- def datacrunch_client():
20
- return DataCrunchClient(CLIENT_ID, CLIENT_SECRET, BASE_URL)
@@ -1,36 +0,0 @@
1
- import os
2
- import pytest
3
- from datacrunch.datacrunch import DataCrunchClient
4
- from datacrunch.constants import Locations
5
-
6
- IN_GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true"
7
-
8
-
9
- @pytest.mark.skipif(IN_GITHUB_ACTIONS, reason="Test doesn't work in Github Actions.")
10
- @pytest.mark.withoutresponses
11
- class TestInstances():
12
-
13
- def test_create_instance(self, datacrunch_client: DataCrunchClient):
14
- # get ssh key
15
- ssh_key = datacrunch_client.ssh_keys.get()[0]
16
-
17
- # create instance
18
- instance = datacrunch_client.instances.create(
19
- hostname="test-instance",
20
- location=Locations.FIN_01,
21
- instance_type='CPU.4V',
22
- description="test instance",
23
- image="ubuntu-18.04",
24
- ssh_key_ids=[ssh_key.id])
25
-
26
- # assert instance is created
27
- assert instance.id is not None
28
- assert instance.status == datacrunch_client.constants.instance_status.PROVISIONING
29
-
30
- # delete instance
31
- datacrunch_client.instances.action(instance.id, "delete")
32
-
33
- # permanently delete all volumes in trash
34
- trash = datacrunch_client.volumes.get_in_trash()
35
- for volume in trash:
36
- datacrunch_client.volumes.delete(volume.id, is_permanent=True)
@@ -1,65 +0,0 @@
1
- import os
2
- import pytest
3
- from datacrunch.datacrunch import DataCrunchClient
4
- from datacrunch.constants import Locations
5
-
6
- IN_GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true"
7
-
8
- location_codes = [Locations.FIN_01, Locations.ICE_01]
9
-
10
-
11
- @pytest.mark.skipif(IN_GITHUB_ACTIONS, reason="Test doesn't work in Github Actions.")
12
- @pytest.mark.withoutresponses
13
- class TestLocations():
14
-
15
- def test_specific_instance_availability_in_specific_location(self, datacrunch_client: DataCrunchClient):
16
- # call the instance availability endpoint, for a specific location
17
- availability = datacrunch_client.instances.is_available(
18
- 'CPU.4V', location_code=Locations.FIN_01)
19
-
20
- assert availability is not None
21
- assert isinstance(availability, bool)
22
-
23
- def test_all_availabilies_in_specific_location(self, datacrunch_client: DataCrunchClient):
24
-
25
- # call the instance availability endpoint, for a specific location
26
- availabilities = datacrunch_client.instances.get_availabilities(
27
- location_code=Locations.FIN_01)
28
-
29
- assert availabilities is not None
30
- assert isinstance(availabilities, list)
31
- assert len(availabilities) == 1
32
- assert availabilities[0]['location_code'] in location_codes
33
- assert isinstance(availabilities[0]['availabilities'], list)
34
- assert len(availabilities[0]['availabilities']) > 0
35
-
36
- def test_all_availabilites(self, datacrunch_client: DataCrunchClient):
37
- # call the instance availability endpoint, for all locations
38
- all_availabilities = datacrunch_client.instances.get_availabilities()
39
-
40
- assert all_availabilities is not None
41
- assert isinstance(all_availabilities, list)
42
- assert len(all_availabilities) > 1
43
- assert all_availabilities[0]['location_code'] in location_codes
44
- assert all_availabilities[1]['location_code'] in location_codes
45
- assert isinstance(all_availabilities[0]['availabilities'], list)
46
- assert len(all_availabilities[0]['availabilities']) > 0
47
-
48
- def test_get_all_locations(self, datacrunch_client: DataCrunchClient):
49
- # call the locations endpoint
50
- locations = datacrunch_client.locations.get()
51
-
52
- assert locations is not None
53
- assert isinstance(locations, list)
54
-
55
- assert locations[0]['code'] in location_codes
56
- assert locations[1]['code'] in location_codes
57
- assert locations[0]['code'] != locations[1]['code']
58
-
59
- assert locations[0]['name'] is not None
60
- assert locations[1]['name'] is not None
61
- assert locations[0]['name'] != locations[1]['name']
62
-
63
- assert locations[0]['country_code'] is not None
64
- assert locations[1]['country_code'] is not None
65
- assert locations[0]['country_code'] != locations[1]['country_code']
@@ -1,94 +0,0 @@
1
- import os
2
- import time
3
- import pytest
4
- from datacrunch.datacrunch import DataCrunchClient
5
- from datacrunch.constants import Locations, VolumeTypes, VolumeStatus
6
-
7
- IN_GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true"
8
-
9
-
10
- NVMe = VolumeTypes.NVMe
11
-
12
-
13
- @pytest.mark.skipif(IN_GITHUB_ACTIONS, reason="Test doesn't work in Github Actions.")
14
- @pytest.mark.withoutresponses
15
- class TestVolumes():
16
-
17
- def test_get_volumes_from_trash(self, datacrunch_client: DataCrunchClient):
18
- # create new volume
19
- volume = datacrunch_client.volumes.create(
20
- type=NVMe, name="test_volume", size=100)
21
-
22
- # delete volume
23
- datacrunch_client.volumes.delete(volume.id)
24
-
25
- # get volumes from trash
26
- volumes = datacrunch_client.volumes.get_in_trash()
27
-
28
- # assert volume is in trash
29
- assert volume.id in [v.id for v in volumes]
30
-
31
- # cleaning: permanently delete the volume
32
- datacrunch_client.volumes.delete(volume.id, is_permanent=True)
33
-
34
- def test_permanently_delete_detached_volumes(seld, datacrunch_client):
35
- # create new volume
36
- volume = datacrunch_client.volumes.create(
37
- type=NVMe, name="test_volume", size=100)
38
-
39
- # permanently delete the detached volume
40
- datacrunch_client.volumes.delete(volume.id, is_permanent=True)
41
-
42
- # sleep for 2 seconds
43
- time.sleep(2)
44
-
45
- # make sure the volume is not in trash
46
- volumes = datacrunch_client.volumes.get_in_trash()
47
-
48
- # assert volume is not in trash
49
- assert volume.id not in [v.id for v in volumes]
50
-
51
- # get the volume
52
- volume = datacrunch_client.volumes.get_by_id(volume.id)
53
-
54
- # assert volume status is deleted
55
- assert volume.status == datacrunch_client.constants.volume_status.DELETED
56
-
57
- def test_permanently_delete_a_deleted_volume_from_trash(self, datacrunch_client):
58
- # create new volume
59
- volume = datacrunch_client.volumes.create(
60
- type=NVMe, name="test_volume", size=100)
61
-
62
- # delete volume
63
- datacrunch_client.volumes.delete(volume.id)
64
-
65
- # sleep for 2 seconds
66
- time.sleep(2)
67
-
68
- # permanently delete the volume
69
- datacrunch_client.volumes.delete(volume.id, is_permanent=True)
70
-
71
- # get the volume
72
- volume = datacrunch_client.volumes.get_by_id(volume.id)
73
-
74
- # assert volume status is deleted
75
- assert volume.status == datacrunch_client.constants.volume_status.DELETED
76
-
77
- # make sure the volume is not in trash
78
- volumes = datacrunch_client.volumes.get_in_trash()
79
-
80
- # assert volume is not in trash
81
- assert volume.id not in [v.id for v in volumes]
82
-
83
- def test_create_volume(self, datacrunch_client):
84
- # create new volume
85
- volume = datacrunch_client.volumes.create(
86
- type=NVMe, name="test_volume", size=100, location=Locations.FIN_01)
87
-
88
- # assert volume is created
89
- assert volume.id is not None
90
- assert volume.location == Locations.FIN_01
91
- assert volume.status == VolumeStatus.ORDERED or volume.status == VolumeStatus.DETACHED
92
-
93
- # cleaning: delete volume
94
- datacrunch_client.volumes.delete(volume.id, is_permanent=True)
File without changes
File without changes
@@ -1,202 +0,0 @@
1
- import pytest
2
- import responses # https://github.com/getsentry/responses
3
- from responses import matchers
4
- import time
5
-
6
- from datacrunch.exceptions import APIException
7
- from datacrunch.authentication.authentication import AuthenticationService
8
-
9
- INVALID_REQUEST = 'invalid_request'
10
- INVALID_REQUEST_MESSAGE = 'Your existence is invalid'
11
-
12
- BASE_URL = "https://api-testing.datacrunch.io/v1"
13
- CLIENT_ID = "0123456789xyz"
14
- CLIENT_SECRET = 'zyx987654321'
15
-
16
- ACCESS_TOKEN = 'access'
17
- REFRESH_TOKEN = 'refresh'
18
- SCOPE = 'fullAccess'
19
- TOKEN_TYPE = 'Bearer'
20
- EXPIRES_IN = 3600
21
-
22
- ACCESS_TOKEN2 = 'access2'
23
- REFRESH_TOKEN2 = 'refresh2'
24
-
25
-
26
- class TestAuthenticationService:
27
-
28
- @pytest.fixture
29
- def authentication_service(self):
30
- return AuthenticationService(CLIENT_ID, CLIENT_SECRET, BASE_URL)
31
-
32
- @pytest.fixture
33
- def endpoint(self, http_client):
34
- return http_client._base_url + "/oauth2/token"
35
-
36
- def test_authenticate_successful(self, authentication_service, endpoint):
37
- # arrange - add response mock
38
- responses.add(
39
- responses.POST,
40
- endpoint,
41
- json={
42
- 'access_token': ACCESS_TOKEN,
43
- 'refresh_token': REFRESH_TOKEN,
44
- 'scope': SCOPE,
45
- 'token_type': TOKEN_TYPE,
46
- 'expires_in': EXPIRES_IN
47
- },
48
- status=200
49
- )
50
-
51
- # act
52
- auth_data = authentication_service.authenticate()
53
-
54
- # assert
55
- assert type(auth_data) == dict
56
- assert authentication_service._access_token == ACCESS_TOKEN
57
- assert authentication_service._refresh_token == REFRESH_TOKEN
58
- assert authentication_service._scope == SCOPE
59
- assert authentication_service._token_type == TOKEN_TYPE
60
- assert authentication_service._expires_at != None
61
- assert responses.assert_call_count(endpoint, 1) is True
62
-
63
- def test_authenticate_failed(self, authentication_service, endpoint):
64
- # arrange - add response mock
65
- responses.add(
66
- responses.POST,
67
- endpoint,
68
- json={"code": INVALID_REQUEST, "message": INVALID_REQUEST_MESSAGE},
69
- status=400
70
- )
71
-
72
- # act
73
- with pytest.raises(APIException) as excinfo:
74
- authentication_service.authenticate()
75
-
76
- x = responses.calls[0].request
77
-
78
- # assert
79
- assert excinfo.value.code == INVALID_REQUEST
80
- assert excinfo.value.message == INVALID_REQUEST_MESSAGE
81
- assert responses.assert_call_count(endpoint, 1) is True
82
- assert responses.calls[0].request.body == f'{{"grant_type": "client_credentials", "client_id": "{CLIENT_ID}", "client_secret": "{CLIENT_SECRET}"}}'.encode(
83
- )
84
-
85
- def test_refresh_successful(self, authentication_service, endpoint):
86
- # add a response for the client credentials grant
87
- responses.add(
88
- responses.POST,
89
- endpoint,
90
- json={
91
- 'access_token': ACCESS_TOKEN,
92
- 'refresh_token': REFRESH_TOKEN,
93
- 'scope': SCOPE,
94
- 'token_type': TOKEN_TYPE,
95
- 'expires_in': EXPIRES_IN
96
- },
97
- match=[matchers.json_params_matcher(
98
- {"grant_type": "client_credentials", "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET})],
99
- status=200
100
- )
101
-
102
- # add another response for the refresh token grant
103
- responses.add(
104
- responses.POST,
105
- endpoint,
106
- json={
107
- 'access_token': ACCESS_TOKEN2,
108
- 'refresh_token': REFRESH_TOKEN2,
109
- 'scope': SCOPE,
110
- 'token_type': TOKEN_TYPE,
111
- 'expires_in': EXPIRES_IN
112
- },
113
- match=[matchers.json_params_matcher(
114
- {"grant_type": "refresh_token", "refresh_token": REFRESH_TOKEN})],
115
- status=200
116
- )
117
-
118
- # act
119
- auth_data = authentication_service.authenticate() # authenticate first
120
-
121
- # assert
122
- assert type(auth_data) == dict
123
- assert authentication_service._access_token == ACCESS_TOKEN
124
- assert authentication_service._refresh_token == REFRESH_TOKEN
125
- assert authentication_service._scope == SCOPE
126
- assert authentication_service._token_type == TOKEN_TYPE
127
- assert authentication_service._expires_at != None
128
- assert responses.calls[0].request.body == f'{{"grant_type": "client_credentials", "client_id": "{CLIENT_ID}", "client_secret": "{CLIENT_SECRET}"}}'.encode(
129
- )
130
-
131
- auth_data2 = authentication_service.refresh() # refresh
132
-
133
- assert type(auth_data2) == dict
134
- assert authentication_service._access_token == ACCESS_TOKEN2
135
- assert authentication_service._refresh_token == REFRESH_TOKEN2
136
- assert authentication_service._scope == SCOPE
137
- assert authentication_service._token_type == TOKEN_TYPE
138
- assert authentication_service._expires_at != None
139
- assert responses.calls[1].request.body == f'{{"grant_type": "refresh_token", "refresh_token": "{REFRESH_TOKEN}"}}'.encode(
140
- )
141
- assert responses.assert_call_count(endpoint, 2) is True
142
-
143
- def test_refresh_failed(self, authentication_service, endpoint):
144
- # arrange - add responses mock
145
- # first response for authentication - ok
146
- responses.add(
147
- responses.POST,
148
- endpoint,
149
- json={
150
- 'access_token': ACCESS_TOKEN,
151
- 'refresh_token': REFRESH_TOKEN,
152
- 'scope': SCOPE,
153
- 'token_type': TOKEN_TYPE,
154
- 'expires_in': EXPIRES_IN
155
- },
156
- match=[matchers.json_params_matcher(
157
- {"grant_type": "client_credentials", "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET})],
158
- status=200
159
- )
160
-
161
- # second response for the refresh - failed
162
- responses.add(
163
- responses.POST,
164
- endpoint,
165
- json={"code": INVALID_REQUEST, "message": INVALID_REQUEST_MESSAGE},
166
- match=[matchers.json_params_matcher(
167
- {"grant_type": "refresh_token", "refresh_token": REFRESH_TOKEN})],
168
- status=500
169
- )
170
-
171
- # act
172
- authentication_service.authenticate() # authenticate first
173
-
174
- with pytest.raises(APIException) as excinfo:
175
- authentication_service.refresh()
176
-
177
- # assert
178
- assert excinfo.value.code == INVALID_REQUEST
179
- assert excinfo.value.message == INVALID_REQUEST_MESSAGE
180
- assert responses.assert_call_count(endpoint, 2) is True
181
- assert responses.calls[0].request.body == f'{{"grant_type": "client_credentials", "client_id": "{CLIENT_ID}", "client_secret": "{CLIENT_SECRET}"}}'.encode(
182
- )
183
- assert responses.calls[1].request.body == f'{{"grant_type": "refresh_token", "refresh_token": "{REFRESH_TOKEN}"}}'.encode(
184
- )
185
-
186
- def test_is_expired(self, authentication_service, endpoint):
187
- # arrange
188
- current_time = time.time()
189
- future_time = current_time + 3600
190
-
191
- # act
192
- # set the expired_at as current time
193
- authentication_service._expires_at = current_time
194
- is_expired_current = authentication_service.is_expired()
195
-
196
- # set the expired_at as future time
197
- authentication_service._expires_at = future_time
198
- is_expired_future = authentication_service.is_expired()
199
-
200
- # assert
201
- assert is_expired_current == True
202
- assert is_expired_future == False
File without changes
@@ -1,25 +0,0 @@
1
- import responses # https://github.com/getsentry/responses
2
-
3
- from datacrunch.balance.balance import BalanceService, Balance
4
-
5
-
6
- def test_balance(http_client):
7
- # arrange - add response mock
8
- responses.add(
9
- responses.GET,
10
- http_client._base_url + "/balance",
11
- json={"amount": 50.5, "currency": "usd"},
12
- status=200
13
- )
14
-
15
- balance_service = BalanceService(http_client)
16
-
17
- # act
18
- balance = balance_service.get()
19
-
20
- # assert
21
- assert type(balance) == Balance
22
- assert type(balance.amount) == float
23
- assert type(balance.currency) == str
24
- assert balance.amount == 50.5
25
- assert balance.currency == "usd"
@@ -1,21 +0,0 @@
1
- import pytest
2
- from unittest.mock import Mock
3
- from datacrunch.http_client.http_client import HTTPClient
4
-
5
-
6
- BASE_URL = "https://api-testing.datacrunch.io/v1"
7
- ACCESS_TOKEN = "test-token"
8
- CLIENT_ID = "0123456789xyz"
9
- CLIENT_SECRET = "0123456789xyz"
10
-
11
-
12
- @pytest.fixture
13
- def http_client():
14
- auth_service = Mock()
15
- auth_service._access_token = ACCESS_TOKEN
16
- auth_service.is_expired = Mock(return_value=True)
17
- auth_service.refresh = Mock(return_value=None)
18
- auth_service._client_id = CLIENT_ID
19
- auth_service._client_secret = CLIENT_SECRET
20
-
21
- return HTTPClient(auth_service, BASE_URL)
@@ -1 +0,0 @@
1
-