Skip to content

Commit 1d5c181

Browse files
Merge pull request #411 from linode/proj/parent-child
new: Add support for Parent/Child account switching
2 parents 55aadbb + ddffa75 commit 1d5c181

File tree

7 files changed

+157
-1
lines changed

7 files changed

+157
-1
lines changed

linode_api4/groups/account.py

+13
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
AccountBetaProgram,
99
AccountSettings,
1010
BetaProgram,
11+
ChildAccount,
1112
Event,
1213
Invoice,
1314
Login,
@@ -18,6 +19,7 @@
1819
ServiceTransfer,
1920
User,
2021
)
22+
from linode_api4.objects.profile import PersonalAccessToken
2123

2224

2325
class AccountGroup(Group):
@@ -496,3 +498,14 @@ def availabilities(self, *filters):
496498
:rtype: PaginatedList of AccountAvailability
497499
"""
498500
return self.client._get_and_filter(AccountAvailability, *filters)
501+
502+
def child_accounts(self, *filters):
503+
"""
504+
Returns a list of all child accounts under the this parent account.
505+
506+
API doc: TBD
507+
508+
:returns: a list of all child accounts.
509+
:rtype: PaginatedList of ChildAccount
510+
"""
511+
return self.client._get_and_filter(ChildAccount, *filters)

linode_api4/objects/account.py

+35
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
from datetime import datetime
24

35
import requests
@@ -16,6 +18,7 @@
1618
)
1719
from linode_api4.objects.longview import LongviewClient, LongviewSubscription
1820
from linode_api4.objects.nodebalancer import NodeBalancer
21+
from linode_api4.objects.profile import PersonalAccessToken
1922
from linode_api4.objects.support import SupportTicket
2023

2124

@@ -53,6 +56,37 @@ class Account(Base):
5356
}
5457

5558

59+
class ChildAccount(Account):
60+
"""
61+
A child account under a parent account.
62+
63+
API Documentation: TBD
64+
"""
65+
66+
api_endpoint = "/account/child-accounts/{euuid}"
67+
id_attribute = "euuid"
68+
69+
def create_token(self, **kwargs):
70+
"""
71+
Create a ephemeral token for accessing the child account.
72+
73+
API Documentation: TBD
74+
"""
75+
resp = self._client.post(
76+
"{}/token".format(self.api_endpoint),
77+
model=self,
78+
data=kwargs,
79+
)
80+
81+
if "errors" in resp:
82+
raise UnexpectedResponseError(
83+
"Unexpected response when creating a token for the child account!",
84+
json=resp,
85+
)
86+
87+
return PersonalAccessToken(self._client, resp["id"], resp)
88+
89+
5690
class ServiceTransfer(Base):
5791
"""
5892
A transfer request for transferring a service between Linode accounts.
@@ -476,6 +510,7 @@ class User(Base):
476510
properties = {
477511
"email": Property(),
478512
"username": Property(identifier=True, mutable=True),
513+
"user_type": Property(),
479514
"restricted": Property(mutable=True),
480515
"ssh_keys": Property(),
481516
"tfa_enabled": Property(),
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"data": [
3+
{
4+
"active_since": "2018-01-01T00:01:01",
5+
"address_1": "123 Main Street",
6+
"address_2": "Suite A",
7+
"balance": 200,
8+
"balance_uninvoiced": 145,
9+
"billing_source": "external",
10+
"capabilities": [
11+
"Linodes",
12+
"NodeBalancers",
13+
"Block Storage",
14+
"Object Storage"
15+
],
16+
"city": "Philadelphia",
17+
"company": "Linode LLC",
18+
"country": "US",
19+
"credit_card": {
20+
"expiry": "11/2022",
21+
"last_four": 1111
22+
},
23+
"email": "[email protected]",
24+
"euuid": "E1AF5EEC-526F-487D-B317EBEB34C87D71",
25+
"first_name": "John",
26+
"last_name": "Smith",
27+
"phone": "215-555-1212",
28+
"state": "PA",
29+
"tax_id": "ATU99999999",
30+
"zip": "19102-1234"
31+
}
32+
],
33+
"page": 1,
34+
"pages": 1,
35+
"results": 1
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"active_since": "2018-01-01T00:01:01",
3+
"address_1": "123 Main Street",
4+
"address_2": "Suite A",
5+
"balance": 200,
6+
"balance_uninvoiced": 145,
7+
"billing_source": "external",
8+
"capabilities": [
9+
"Linodes",
10+
"NodeBalancers",
11+
"Block Storage",
12+
"Object Storage"
13+
],
14+
"city": "Philadelphia",
15+
"company": "Linode LLC",
16+
"country": "US",
17+
"credit_card": {
18+
"expiry": "11/2022",
19+
"last_four": 1111
20+
},
21+
"email": "[email protected]",
22+
"euuid": "E1AF5EEC-526F-487D-B317EBEB34C87D71",
23+
"first_name": "John",
24+
"last_name": "Smith",
25+
"phone": "215-555-1212",
26+
"state": "PA",
27+
"tax_id": "ATU99999999",
28+
"zip": "19102-1234"
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"created": "2024-01-01T00:01:01",
3+
"expiry": "2024-01-01T13:46:32",
4+
"id": 123,
5+
"label": "cool_customer_proxy",
6+
"scopes": "*",
7+
"token": "abcdefghijklmnop"
8+
}

test/integration/models/account/test_account.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@
33

44
import pytest
55

6-
from linode_api4.objects import Account, AccountSettings, Event, Login, User
6+
from linode_api4.objects import (
7+
Account,
8+
AccountSettings,
9+
ChildAccount,
10+
Event,
11+
Login,
12+
User,
13+
)
714

815

916
@pytest.mark.smoke
@@ -85,3 +92,12 @@ def test_get_user(test_linode_client):
8592
assert username == user.username
8693
assert "email" in user._raw_json
8794
assert "email" in user._raw_json
95+
96+
97+
def test_list_child_accounts(test_linode_client):
98+
client = test_linode_client
99+
child_accounts = client.account.child_accounts()
100+
if len(child_accounts) > 0:
101+
child_account = ChildAccount(client, child_accounts[0].euuid)
102+
child_account._api_get()
103+
child_account.create_token()

test/unit/objects/account_test.py

+19
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
Volume,
2525
get_obj_grants,
2626
)
27+
from linode_api4.objects.account import ChildAccount
2728

2829

2930
class InvoiceTest(ClientBaseCase):
@@ -290,3 +291,21 @@ def test_account_availability_api_get(self):
290291
self.assertEqual(availability.available, ["Linodes", "Kubernetes"])
291292

292293
self.assertEqual(m.call_url, account_availability_url)
294+
295+
296+
class ChildAccountTest(ClientBaseCase):
297+
"""
298+
Test methods of the ChildAccount
299+
"""
300+
301+
def test_child_account_api_list(self):
302+
result = self.client.account.child_accounts()
303+
self.assertEqual(len(result), 1)
304+
self.assertEqual(result[0].euuid, "E1AF5EEC-526F-487D-B317EBEB34C87D71")
305+
306+
def test_child_account_create_token(self):
307+
child_account = self.client.load(ChildAccount, 123456)
308+
with self.mock_post("/account/child-accounts/123456/token") as m:
309+
token = child_account.create_token()
310+
self.assertEqual(token.token, "abcdefghijklmnop")
311+
self.assertEqual(m.call_data, {})

0 commit comments

Comments
 (0)