Skip to content

Commit d5ae5f5

Browse files
authored
Merge pull request #361 from linode/dev
Release v5.11.0
2 parents 0bcfa70 + ddb0b98 commit d5ae5f5

21 files changed

+971
-107
lines changed

.github/workflows/e2e-test-pr.yml

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ jobs:
1919
runs-on: ubuntu-latest
2020
if:
2121
github.event_name == 'workflow_dispatch' && inputs.sha != ''
22+
env:
23+
EXIT_STATUS: 0
2224

2325
steps:
2426
- uses: actions-ecosystem/action-regex-match@v2
@@ -83,20 +85,17 @@ jobs:
8385
timestamp=$(date +'%Y%m%d%H%M')
8486
report_filename="${timestamp}_sdk_test_report.xml"
8587
status=0
86-
if ! python3 -m pytest test/integration/${INTEGRATION_TEST_PATH} --junitxml="${report_filename}"; then
87-
echo "Tests failed, but attempting to upload results anyway"
88+
if ! python3 -m pytest test/integration/${INTEGRATION_TEST_PATH} --disable-warnings --junitxml="${report_filename}"; then
89+
echo "EXIT_STATUS=1" >> $GITHUB_ENV
8890
fi
8991
env:
9092
LINODE_TOKEN: ${{ secrets.LINODE_TOKEN }}
9193

92-
- name: Set release version env
93-
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
94-
9594
- name: Add additional information to XML report
9695
run: |
9796
filename=$(ls | grep -E '^[0-9]{12}_sdk_test_report\.xml$')
9897
python test/script/add_to_xml_test_report.py \
99-
--branch_name "${{ env.RELEASE_VERSION }}" \
98+
--branch_name "${GITHUB_REF#refs/*/}" \
10099
--gha_run_id "$GITHUB_RUN_ID" \
101100
--gha_run_number "$GITHUB_RUN_NUMBER" \
102101
--xmlfile "${filename}"
@@ -135,4 +134,13 @@ jobs:
135134
status: 'completed',
136135
conclusion: process.env.conclusion
137136
});
138-
return result;
137+
return result;
138+
139+
- name: Test Execution Status Handler
140+
run: |
141+
if [[ "$EXIT_STATUS" != 0 ]]; then
142+
echo "Test execution contains failure(s)"
143+
exit $EXIT_STATUS
144+
else
145+
echo "Tests passed!"
146+
fi

linode_api4/groups/account.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ def enrolled_betas(self, *filters):
457457
"""
458458
Returns a list of all Beta Programs an account is enrolled in.
459459
460-
API doc: TBD
460+
API doc: https://www.linode.com/docs/api/beta-programs/#enrolled-beta-programs-list
461461
462462
:returns: a list of Beta Programs.
463463
:rtype: PaginatedList of AccountBetaProgram
@@ -468,7 +468,7 @@ def join_beta_program(self, beta: Union[str, BetaProgram]):
468468
"""
469469
Enrolls an account into a beta program.
470470
471-
API doc: TBD
471+
API doc: https://www.linode.com/docs/api/beta-programs/#beta-program-enroll
472472
473473
:param beta: The object or id of a beta program to join.
474474
:type beta: BetaProgram or str

linode_api4/groups/beta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def betas(self, *filters):
1212
"""
1313
Returns a list of available active Beta Programs.
1414
15-
API Documentation: TBD
15+
API Documentation: https://www.linode.com/docs/api/beta-programs/#beta-programs-list
1616
1717
:param filters: Any number of filters to apply to this query.
1818
See :doc:`Filtering Collections</linode_api4/objects/filtering>`

linode_api4/groups/region.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from linode_api4.groups import Group
22
from linode_api4.objects import Region
3+
from linode_api4.objects.region import RegionAvailabilityEntry
34

45

56
class RegionGroup(Group):
@@ -23,3 +24,22 @@ def __call__(self, *filters):
2324
"""
2425

2526
return self.client._get_and_filter(Region, *filters)
27+
28+
def availability(self, *filters):
29+
"""
30+
Returns the availability of Linode plans within a Region.
31+
32+
33+
API Documentation: https://www.linode.com/docs/api/regions/#regions-availability-list
34+
35+
:param filters: Any number of filters to apply to this query.
36+
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
37+
for more details on filtering.
38+
39+
:returns: A list of entries describing the availability of a plan in a region.
40+
:rtype: PaginatedList of RegionAvailabilityEntry
41+
"""
42+
43+
return self.client._get_and_filter(
44+
RegionAvailabilityEntry, *filters, endpoint="/regions/availability"
45+
)

linode_api4/linode_client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,10 @@ def _api_call(
276276
body = json.dumps(data)
277277

278278
response = method(
279-
url, headers=headers, data=body, verify=self.ca_path or True
279+
url,
280+
headers=headers,
281+
data=body,
282+
verify=self.ca_path or self.session.verify,
280283
)
281284

282285
warning = response.headers.get("Warning", None)

linode_api4/login_client.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -490,9 +490,10 @@ def refresh_oauth_token(self, refresh_token):
490490

491491
def expire_token(self, token):
492492
"""
493-
Given a token, makes a request to the authentication server to expire
494-
it immediately. This is considered a responsible way to log out a
495-
user. If you simply remove the session your application has for the
493+
Given a token, makes a request to the authentication server to expire both
494+
access token and refresh token.
495+
This is considered a responsible way to log out a user.
496+
If you remove only the session your application has for the
496497
user without expiring their token, the user is not _really_ logged out.
497498
498499
:param token: The OAuth token you wish to expire
@@ -504,8 +505,9 @@ def expire_token(self, token):
504505
:raises ApiError: If the expiration attempt failed.
505506
"""
506507
r = requests.post(
507-
self._login_uri("/oauth/token/expire"),
508+
self._login_uri("/oauth/revoke"),
508509
data={
510+
"token_type_hint": "access_token",
509511
"client_id": self.client_id,
510512
"client_secret": self.client_secret,
511513
"token": token,

linode_api4/objects/account.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,8 @@ def save(self):
642642
class AccountBetaProgram(Base):
643643
"""
644644
The details and enrollment information of a Beta program that an account is enrolled in.
645+
646+
API Documentation: https://www.linode.com/docs/api/beta-programs/#enrolled-beta-program-view
645647
"""
646648

647649
api_endpoint = "/account/betas/{id}"

linode_api4/objects/beta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class BetaProgram(Base):
66
Beta program is a new product or service that's not generally available to all customers.
77
User with permissions can enroll into a beta program and access the functionalities.
88
9-
API Documentation: TBD
9+
API Documentation: https://www.linode.com/docs/api/beta-programs/#beta-program-view
1010
"""
1111

1212
api_endpoint = "/betas/{id}"

linode_api4/objects/region.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
from linode_api4.objects import Base, Property
1+
from dataclasses import dataclass
2+
from typing import List
3+
4+
from linode_api4.errors import UnexpectedResponseError
5+
from linode_api4.objects.base import Base, JSONObject, Property
6+
from linode_api4.objects.filtering import FilterableAttribute
7+
from linode_api4.objects.serializable import JSONFilterableMetaclass
28

39

410
class Region(Base):
@@ -17,3 +23,29 @@ class Region(Base):
1723
"resolvers": Property(),
1824
"label": Property(),
1925
}
26+
27+
@property
28+
def availability(self) -> List["RegionAvailabilityEntry"]:
29+
result = self._client.get(
30+
f"{self.api_endpoint}/availability", model=self
31+
)
32+
33+
if result is None:
34+
raise UnexpectedResponseError(
35+
"Expected availability data, got None."
36+
)
37+
38+
return [RegionAvailabilityEntry.from_json(v) for v in result]
39+
40+
41+
@dataclass
42+
class RegionAvailabilityEntry(JSONObject):
43+
"""
44+
Represents the availability of a Linode type within a region.
45+
46+
API Documentation: https://www.linode.com/docs/api/regions/#region-availability-view
47+
"""
48+
49+
region: str = None
50+
plan: str = None
51+
available: bool = False

linode_api4/objects/serializable.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,41 @@
11
import inspect
22
from dataclasses import asdict, dataclass
3-
from typing import Any, Dict, Optional, get_args, get_origin, get_type_hints
3+
from types import SimpleNamespace
4+
from typing import (
5+
Any,
6+
ClassVar,
7+
Dict,
8+
Optional,
9+
get_args,
10+
get_origin,
11+
get_type_hints,
12+
)
13+
14+
from linode_api4.objects.filtering import FilterableAttribute
15+
16+
# Wraps the SimpleNamespace class and allows for
17+
# SQLAlchemy-style filter generation on JSONObjects.
18+
JSONFilterGroup = SimpleNamespace
19+
20+
21+
class JSONFilterableMetaclass(type):
22+
def __init__(cls, name, bases, dct):
23+
setattr(
24+
cls,
25+
"filters",
26+
JSONFilterGroup(
27+
**{
28+
k: FilterableAttribute(k)
29+
for k in cls.__annotations__.keys()
30+
}
31+
),
32+
)
33+
34+
super().__init__(name, bases, dct)
435

536

637
@dataclass
7-
class JSONObject:
38+
class JSONObject(metaclass=JSONFilterableMetaclass):
839
"""
940
A simple helper class for serializable API objects.
1041
This is typically used for nested object values.
@@ -13,6 +44,16 @@ class JSONObject:
1344
fields and static typing.
1445
"""
1546

47+
filters: ClassVar[JSONFilterGroup] = None
48+
"""
49+
A group containing FilterableAttributes used to create SQLAlchemy-style filters.
50+
51+
Example usage::
52+
self.client.regions.availability(
53+
RegionAvailabilityEntry.filters.plan == "premium4096.7"
54+
)
55+
"""
56+
1657
def __init__(self):
1758
raise NotImplementedError(
1859
"JSONObject is not intended to be constructed directly"

linode_api4/paginated_list.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import math
22

3+
from linode_api4.objects.serializable import JSONObject
4+
35

46
class PaginatedList(object):
57
"""
@@ -205,6 +207,10 @@ def make_list(json_arr, client, cls, parent_id=None):
205207

206208
for obj in json_arr:
207209
id_val = None
210+
# Special handling for JSON objects
211+
if issubclass(cls, JSONObject):
212+
result.append(cls.from_json(obj))
213+
continue
208214

209215
if "id" in obj:
210216
id_val = obj["id"]

0 commit comments

Comments
 (0)