Skip to content

Commit 11d8a1d

Browse files
authored
Merge pull request #101 from akx/session-refactor
Session refactor (composition over multi-inheritance!)
2 parents d04dbef + 4eb9de9 commit 11d8a1d

16 files changed

+215
-183
lines changed

test/test_storage.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ def test_create_storage_import(self, manager):
127127

128128
@responses.activate
129129
def test_upload_file_for_storage_import(self, manager):
130+
# TODO: this test probably doesn't correctly test for the actual format of the data being uploaded
130131
data = Mock.mock_post(
131132
"storage/01d4fcd4-e446-433b-8a9c-551a1284952e/import", ignore_data_field=True
132133
)

upcloud_api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
__license__ = 'MIT'
1111
__copyright__ = 'Copyright (c) 2015 UpCloud'
1212

13-
from upcloud_api.cloud_manager.cloud_manager import CloudManager
13+
from upcloud_api.cloud_manager import CloudManager
1414
from upcloud_api.errors import UpCloudAPIError, UpCloudClientError
1515
from upcloud_api.firewall import FirewallRule
1616
from upcloud_api.host import Host
Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@
55
from upcloud_api.errors import UpCloudAPIError
66

77

8-
class BaseAPI:
8+
class API:
99
"""
10-
CloudManager base that handles basic HTTP communication with API.
10+
Handles basic HTTP communication with API.
1111
"""
1212

13-
api = 'api.upcloud.com'
14-
api_v = '1.3'
13+
api_root = 'https://api.upcloud.com/1.3'
1514

1615
def __init__(self, token, timeout=None):
1716
"""
@@ -20,27 +19,21 @@ def __init__(self, token, timeout=None):
2019
self.token = token
2120
self.timeout = timeout
2221

23-
def request(self, method, endpoint, body=None, params=None, timeout=-1, request_to_api=True):
22+
def api_request(self, method, endpoint, body=None, params=None, timeout=-1):
2423
"""
25-
Perform a request with a given body to a given endpoint in UpCloud's API or UpCloud's uploader session.
24+
Perform a request with a given JSON body to a given endpoint in UpCloud's API.
2625
2726
Handles errors with __error_middleware.
2827
"""
2928
if method not in {'GET', 'POST', 'PUT', 'PATCH', 'DELETE'}:
3029
raise Exception('Invalid/Forbidden HTTP method')
3130

32-
# TODO: revise the semantics of `request_to_api` and where it is set to False
33-
url = 'https://api.upcloud.com/' + self.api_v + endpoint if request_to_api else endpoint
31+
url = f'{self.api_root}{endpoint}'
3432
headers = {'Authorization': self.token, 'User-Agent': self._get_user_agent()}
3533

36-
headers['Content-Type'] = (
37-
'application/json' if request_to_api else 'application/octet-stream'
38-
)
39-
40-
if body and request_to_api:
34+
if body:
4135
data = json.dumps(body)
42-
elif body and not request_to_api:
43-
data = body
36+
headers['Content-Type'] = 'application/json'
4437
else:
4538
data = None
4639

@@ -61,33 +54,31 @@ def get_request(self, endpoint, params=None, timeout=-1):
6154
"""
6255
Perform a GET request to a given endpoint in UpCloud's API.
6356
"""
64-
return self.request('GET', endpoint, params=params, timeout=timeout)
57+
return self.api_request('GET', endpoint, params=params, timeout=timeout)
6558

6659
def post_request(self, endpoint, body=None, timeout=-1):
6760
"""
6861
Perform a POST request to a given endpoint in UpCloud's API.
6962
"""
70-
return self.request('POST', endpoint, body=body, timeout=timeout)
63+
return self.api_request('POST', endpoint, body=body, timeout=timeout)
7164

72-
def put_request(self, endpoint, body=None, timeout=-1, request_to_api=True):
65+
def put_request(self, endpoint, body=None, timeout=-1):
7366
"""
74-
Perform a PUT request to a given endpoint in UpCloud's API or UpCloud's uploader session.
67+
Perform a PUT request to a given endpoint in UpCloud's API.
7568
"""
76-
return self.request(
77-
'PUT', endpoint, body=body, timeout=timeout, request_to_api=request_to_api
78-
)
69+
return self.api_request('PUT', endpoint, body=body, timeout=timeout)
7970

8071
def patch_request(self, endpoint, body=None, timeout=-1):
8172
"""
8273
Perform a PATCH request to a given endpoint in UpCloud's API.
8374
"""
84-
return self.request('PATCH', endpoint, body=body, timeout=timeout)
75+
return self.api_request('PATCH', endpoint, body=body, timeout=timeout)
8576

8677
def delete_request(self, endpoint, timeout=-1):
8778
"""
8879
Perform a DELETE request to a given endpoint in UpCloud's API.
8980
"""
90-
return self.request('DELETE', endpoint, timeout=timeout)
81+
return self.api_request('DELETE', endpoint, timeout=timeout)
9182

9283
def __error_middleware(self, res, res_json):
9384
"""
Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from upcloud_api.cloud_manager.base import BaseAPI
1+
import base64
2+
3+
from upcloud_api.api import API
24
from upcloud_api.cloud_manager.firewall_mixin import FirewallManager
35
from upcloud_api.cloud_manager.host_mixin import HostManager
46
from upcloud_api.cloud_manager.ip_address_mixin import IPManager
@@ -7,3 +9,76 @@
79
from upcloud_api.cloud_manager.server_mixin import ServerManager
810
from upcloud_api.cloud_manager.storage_mixin import StorageManager
911
from upcloud_api.cloud_manager.tag_mixin import TagManager
12+
13+
14+
class CloudManager(
15+
ServerManager,
16+
IPManager,
17+
StorageManager,
18+
FirewallManager,
19+
TagManager,
20+
NetworkManager,
21+
HostManager,
22+
ObjectStorageManager,
23+
):
24+
"""
25+
CloudManager contains the core functionality of the upcloud API library.
26+
27+
All other managers are mixed in so code can be organized in corresponding sub-manager classes.
28+
"""
29+
30+
api: API
31+
32+
def __init__(self, username: str, password: str, timeout: int = 60) -> None:
33+
"""
34+
Initiates CloudManager that handles all HTTP connections with UpCloud's API.
35+
36+
Optionally determine a timeout for API connections (in seconds). A timeout with the value
37+
`None` means that there is no timeout.
38+
"""
39+
if not username or not password:
40+
raise Exception('Invalid credentials, please provide a username and password')
41+
42+
credentials = f'{username}:{password}'.encode()
43+
encoded_credentials = base64.b64encode(credentials).decode()
44+
45+
self.api = API(
46+
token=f'Basic {encoded_credentials}',
47+
timeout=timeout,
48+
)
49+
50+
def authenticate(self):
51+
"""
52+
Authenticate.
53+
"""
54+
return self.get_account()
55+
56+
def get_account(self):
57+
"""
58+
Returns information on the user's account and resource limits.
59+
"""
60+
return self.api.get_request('/account')
61+
62+
def get_zones(self):
63+
"""
64+
Returns a list of available zones.
65+
"""
66+
return self.api.get_request('/zone')
67+
68+
def get_timezones(self):
69+
"""
70+
Returns a list of available timezones.
71+
"""
72+
return self.api.get_request('/timezone')
73+
74+
def get_prices(self):
75+
"""
76+
Returns a list of resource prices.
77+
"""
78+
return self.api.get_request('/price')
79+
80+
def get_server_sizes(self):
81+
"""
82+
Returns a list of available server configurations.
83+
"""
84+
return self.api.get_request('/server_size')

upcloud_api/cloud_manager/cloud_manager.py

Lines changed: 0 additions & 85 deletions
This file was deleted.

upcloud_api/cloud_manager/firewall_mixin.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from upcloud_api.api import API
12
from upcloud_api.firewall import FirewallRule
23
from upcloud_api.server import Server
34

@@ -17,13 +18,15 @@ class FirewallManager:
1718
directly.
1819
"""
1920

21+
api: API
22+
2023
# TODO: server_instance is unused?
2124
def get_firewall_rule(self, server_uuid, firewall_rule_position, server_instance=None):
2225
"""
2326
Return a FirewallRule object based on server uuid and rule position.
2427
"""
2528
url = f'/server/{server_uuid}/firewall_rule/{firewall_rule_position}'
26-
res = self.get_request(url)
29+
res = self.api.get_request(url)
2730
return FirewallRule(**res['firewall_rule'])
2831

2932
def get_firewall_rules(self, server):
@@ -33,7 +36,7 @@ def get_firewall_rules(self, server):
3336
server_uuid, server_instance = uuid_and_instance(server)
3437

3538
url = f'/server/{server_uuid}/firewall_rule'
36-
res = self.get_request(url)
39+
res = self.api.get_request(url)
3740

3841
return [
3942
FirewallRule(server=server_instance, **firewall_rule)
@@ -51,7 +54,7 @@ def create_firewall_rule(self, server, firewall_rule_body):
5154

5255
url = f'/server/{server_uuid}/firewall_rule'
5356
body = {'firewall_rule': firewall_rule_body}
54-
res = self.post_request(url, body)
57+
res = self.api.post_request(url, body)
5558

5659
return FirewallRule(server=server_instance, **res['firewall_rule'])
5760

@@ -60,7 +63,7 @@ def delete_firewall_rule(self, server_uuid, firewall_rule_position):
6063
Delete a firewall rule based on a server uuid and rule position.
6164
"""
6265
url = f'/server/{server_uuid}/firewall_rule/{firewall_rule_position}'
63-
return self.delete_request(url)
66+
return self.api.delete_request(url)
6467

6568
def configure_firewall(self, server, firewall_rule_bodies):
6669
"""
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from upcloud_api.api import API
12
from upcloud_api.host import Host
23

34

@@ -6,20 +7,22 @@ class HostManager:
67
Functions for managing hosts. Intended to be used as a mixin for CloudManager.
78
"""
89

10+
api: API
11+
912
def get_hosts(self):
1013
"""
1114
Returns a list of available hosts, along with basic statistics of them when available.
1215
"""
1316
url = '/host'
14-
res = self.get_request(url)
17+
res = self.api.get_request(url)
1518
return [Host(**host) for host in res['hosts']['host']]
1619

1720
def get_host(self, id: str) -> Host:
1821
"""
1922
Returns detailed information about a specific host.
2023
"""
2124
url = f'/host/{id}'
22-
res = self.get_request(url)
25+
res = self.api.get_request(url)
2326
return Host(**res['host'])
2427

2528
def modify_host(self, host: str, description: str) -> Host:
@@ -28,5 +31,5 @@ def modify_host(self, host: str, description: str) -> Host:
2831
"""
2932
url = f'/host/{host}'
3033
body = {'host': {'description': description}}
31-
res = self.patch_request(url, body)
34+
res = self.api.patch_request(url, body)
3235
return Host(**res['host'])

0 commit comments

Comments
 (0)