Skip to content

Commit 5b9e283

Browse files
Merge pull request #329 from linode/dev
Release v5.8.0
2 parents 4301728 + e2719eb commit 5b9e283

18 files changed

+322
-6
lines changed

Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ lint: build
6262
testint:
6363
python3 -m pytest test/integration/${INTEGRATION_TEST_PATH}${MODEL_COMMAND} ${TEST_CASE_COMMAND}
6464

65+
@PHONEY: testunit
66+
testunit:
67+
python3 -m python test/unit
68+
6569
@PHONEY: smoketest
6670
smoketest:
6771
pytest -m smoke test/integration --disable-warnings

linode_api4/groups/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from .group import * # isort: skip
33

44
from .account import *
5+
from .beta import *
56
from .database import *
67
from .domain import *
78
from .image import *

linode_api4/groups/account.py

+35
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
from typing import Union
2+
13
from linode_api4.errors import UnexpectedResponseError
24
from linode_api4.groups import Group
35
from linode_api4.objects import (
46
Account,
7+
AccountBetaProgram,
58
AccountSettings,
9+
BetaProgram,
610
Event,
711
Invoice,
812
Login,
@@ -448,3 +452,34 @@ def user_create(self, email, username, restricted=True):
448452

449453
u = User(self.client, result["username"], result)
450454
return u
455+
456+
def enrolled_betas(self, *filters):
457+
"""
458+
Returns a list of all Beta Programs an account is enrolled in.
459+
460+
API doc: TBD
461+
462+
:returns: a list of Beta Programs.
463+
:rtype: PaginatedList of AccountBetaProgram
464+
"""
465+
return self.client._get_and_filter(AccountBetaProgram, *filters)
466+
467+
def join_beta_program(self, beta: Union[str, BetaProgram]):
468+
"""
469+
Enrolls an account into a beta program.
470+
471+
API doc: TBD
472+
473+
:param beta: The object or id of a beta program to join.
474+
:type beta: BetaProgram or str
475+
476+
:returns: A boolean indicating whether the account joined a beta program successfully.
477+
:rtype: bool
478+
"""
479+
480+
self.client.post(
481+
"/account/betas",
482+
data={"id": beta.id if isinstance(beta, BetaProgram) else beta},
483+
)
484+
485+
return True

linode_api4/groups/beta.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from linode_api4.groups import Group
2+
from linode_api4.objects import BetaProgram
3+
4+
5+
class BetaProgramGroup(Group):
6+
"""
7+
This group encapsulates all endpoints under /betas, including viewing
8+
available active beta programs.
9+
"""
10+
11+
def betas(self, *filters):
12+
"""
13+
Returns a list of available active Beta Programs.
14+
15+
API Documentation: TBD
16+
17+
:param filters: Any number of filters to apply to this query.
18+
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
19+
for more details on filtering.
20+
21+
:returns: A list of Beta Programs that matched the query.
22+
:rtype: PaginatedList of BetaProgram
23+
"""
24+
return self.client._get_and_filter(BetaProgram, *filters)

linode_api4/linode_client.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from linode_api4.errors import ApiError, UnexpectedResponseError
1313
from linode_api4.groups import (
1414
AccountGroup,
15+
BetaProgramGroup,
1516
DatabaseGroup,
1617
DomainGroup,
1718
ImageGroup,
@@ -61,6 +62,7 @@ def __init__(
6162
retry_rate_limit_interval=1.0,
6263
retry_max=5,
6364
retry_statuses=None,
65+
ca_path=None,
6466
):
6567
"""
6668
The main interface to the Linode API.
@@ -95,11 +97,14 @@ def __init__(
9597
:param retry_statuses: Additional HTTP response statuses to retry on.
9698
By default, the client will retry on 408, 429, and 502
9799
responses.
100+
:param ca_path: The path to a CA file to use for API requests in this client.
101+
:type ca_path: str
98102
"""
99103
self.base_url = base_url
100104
self._add_user_agent = user_agent
101105
self.token = token
102106
self.page_size = page_size
107+
self.ca_path = ca_path
103108

104109
retry_forcelist = [408, 429, 502]
105110

@@ -188,6 +193,9 @@ def __init__(
188193
#: Access methods related to Event polling - See :any:`PollingGroup` for more information.
189194
self.polling = PollingGroup(self)
190195

196+
#: Access methods related to Beta Program - See :any:`BetaProgramGroup` for more information.
197+
self.beta = BetaProgramGroup(self)
198+
191199
@property
192200
def _user_agent(self):
193201
return "{}python-linode_api4/{} {}".format(
@@ -263,7 +271,9 @@ def _api_call(
263271
if data is not None:
264272
body = json.dumps(data)
265273

266-
response = method(url, headers=headers, data=body)
274+
response = method(
275+
url, headers=headers, data=body, verify=self.ca_path or True
276+
)
267277

268278
warning = response.headers.get("Warning", None)
269279
if warning:

linode_api4/login_client.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,11 @@ def serialize(scopes):
324324

325325
class LinodeLoginClient:
326326
def __init__(
327-
self, client_id, client_secret, base_url="https://login.linode.com"
327+
self,
328+
client_id,
329+
client_secret,
330+
base_url="https://login.linode.com",
331+
ca_path=None,
328332
):
329333
"""
330334
Create a new LinodeLoginClient. These clients do not make any requests
@@ -339,10 +343,13 @@ def __init__(
339343
:param base_url: The URL for Linode's OAuth server. This should not be
340344
changed.
341345
:type base_url: str
346+
:param ca_path: The path to the CA file to use for requests run by this client.
347+
:type ca_path: str
342348
"""
343349
self.base_url = base_url
344350
self.client_id = client_id
345351
self.client_secret = client_secret
352+
self.ca_path = ca_path
346353

347354
def _login_uri(self, path):
348355
return "{}{}".format(self.base_url, path)
@@ -423,6 +430,7 @@ def oauth_redirect():
423430
"client_id": self.client_id,
424431
"client_secret": self.client_secret,
425432
},
433+
verify=self.ca_path or True,
426434
)
427435

428436
if r.status_code != 200:
@@ -467,6 +475,7 @@ def refresh_oauth_token(self, refresh_token):
467475
"client_secret": self.client_secret,
468476
"refresh_token": refresh_token,
469477
},
478+
verify=self.ca_path or True,
470479
)
471480

472481
if r.status_code != 200:
@@ -501,6 +510,7 @@ def expire_token(self, token):
501510
"client_secret": self.client_secret,
502511
"token": token,
503512
},
513+
verify=self.ca_path or True,
504514
)
505515

506516
if r.status_code != 200:

linode_api4/objects/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717
from .object_storage import *
1818
from .lke import *
1919
from .database import *
20+
from .beta import *

linode_api4/objects/account.py

+17
Original file line numberDiff line numberDiff line change
@@ -637,3 +637,20 @@ def save(self):
637637
self._populate(result)
638638

639639
return True
640+
641+
642+
class AccountBetaProgram(Base):
643+
"""
644+
The details and enrollment information of a Beta program that an account is enrolled in.
645+
"""
646+
647+
api_endpoint = "/account/betas/{id}"
648+
649+
properties = {
650+
"id": Property(identifier=True),
651+
"label": Property(),
652+
"description": Property(),
653+
"started": Property(is_datetime=True),
654+
"ended": Property(is_datetime=True),
655+
"enrolled": Property(is_datetime=True),
656+
}

linode_api4/objects/beta.py

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from linode_api4.objects import Base, Property
2+
3+
4+
class BetaProgram(Base):
5+
"""
6+
Beta program is a new product or service that's not generally available to all customers.
7+
User with permissions can enroll into a beta program and access the functionalities.
8+
9+
API Documentation: TBD
10+
"""
11+
12+
api_endpoint = "/betas/{id}"
13+
14+
properties = {
15+
"id": Property(identifier=True),
16+
"label": Property(),
17+
"description": Property(),
18+
"started": Property(is_datetime=True),
19+
"ended": Property(is_datetime=True),
20+
"greenlight_only": Property(),
21+
"more_info": Property(),
22+
}

test/fixtures/account_betas.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"data": [
3+
{
4+
"id": "cool",
5+
"label": "\r\n\r\nRepellat consequatur sunt qui.",
6+
"enrolled": "2018-01-02T03:04:05",
7+
"description": "Repellat consequatur sunt qui. Fugit eligendi ipsa et assumenda ea aspernatur esse. A itaque iste distinctio qui voluptas eum enim ipsa.",
8+
"started": "2018-01-02T03:04:05",
9+
"ended": "2018-01-02T03:04:05"
10+
}
11+
],
12+
"page": 1,
13+
"pages": 1,
14+
"results": 1
15+
}

test/fixtures/account_betas_cool.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": "cool",
3+
"label": "\r\n\r\nRepellat consequatur sunt qui.",
4+
"enrolled": "2018-01-02T03:04:05",
5+
"description": "Repellat consequatur sunt qui. Fugit eligendi ipsa et assumenda ea aspernatur esse. A itaque iste distinctio qui voluptas eum enim ipsa.",
6+
"started": "2018-01-02T03:04:05",
7+
"ended": "2018-01-02T03:04:05"
8+
}

test/fixtures/betas.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"data": [
3+
{
4+
"id": "active_closed",
5+
"label": "active closed beta",
6+
"description": "An active closed beta",
7+
"started": "2023-07-19T15:23:43",
8+
"ended": null,
9+
"greenlight_only": true,
10+
"more_info": "a link with even more info"
11+
},
12+
{
13+
"id": "limited",
14+
"label": "limited beta",
15+
"description": "An active limited beta",
16+
"started": "2023-07-19T15:23:43",
17+
"ended": null, "greenlight_only": false,
18+
"more_info": "a link with even more info"
19+
}
20+
],
21+
"page": 1,
22+
"pages": 1,
23+
"results": 2
24+
}

test/fixtures/betas_active.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"id": "active",
3+
"label": "active closed beta",
4+
"description": "An active closed beta",
5+
"started": "2018-01-02T03:04:05",
6+
"ended": null,
7+
"greenlight_only": true,
8+
"more_info": "a link with even more info"
9+
}

test/unit/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def load_json(url):
3636
return FIXTURES.get_fixture(formatted_url)
3737

3838

39-
def mock_get(url, headers=None, data=None):
39+
def mock_get(url, headers=None, data=None, **kwargs):
4040
"""
4141
Loads the response from a JSON file
4242
"""

0 commit comments

Comments
 (0)