Skip to content

Commit ef89bac

Browse files
Add VPC Grant and Refactor UserGrants class (#455)
* Add VPC grant; refactor UserGrants and make it serializable * Fix warning by adding `r` prior to a regex * Add `.DS_Store` into .gitignore
1 parent e4214f4 commit ef89bac

File tree

4 files changed

+145
-49
lines changed

4 files changed

+145
-49
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ docs/_build/*
1212
venv
1313
baked_version
1414
.vscode
15+
.DS_Store

linode_api4/objects/account.py

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,20 @@
55
import requests
66

77
from linode_api4.errors import ApiError, UnexpectedResponseError
8-
from linode_api4.objects import (
9-
DATE_FORMAT,
10-
Base,
11-
DerivedBase,
12-
Domain,
13-
Image,
14-
Instance,
15-
Property,
16-
StackScript,
17-
Volume,
18-
)
8+
from linode_api4.objects import DATE_FORMAT, Volume
9+
from linode_api4.objects.base import Base, Property
10+
from linode_api4.objects.database import Database
11+
from linode_api4.objects.dbase import DerivedBase
12+
from linode_api4.objects.domain import Domain
13+
from linode_api4.objects.image import Image
14+
from linode_api4.objects.linode import Instance, StackScript
1915
from linode_api4.objects.longview import LongviewClient, LongviewSubscription
16+
from linode_api4.objects.networking import Firewall
2017
from linode_api4.objects.nodebalancer import NodeBalancer
2118
from linode_api4.objects.profile import PersonalAccessToken
2219
from linode_api4.objects.support import SupportTicket
20+
from linode_api4.objects.volume import Volume
21+
from linode_api4.objects.vpc import VPC
2322

2423

2524
class Account(Base):
@@ -554,10 +553,6 @@ def get_obj_grants():
554553
"""
555554
Returns Grant keys mapped to Object types.
556555
"""
557-
from linode_api4.objects import ( # pylint: disable=import-outside-toplevel
558-
Database,
559-
Firewall,
560-
)
561556

562557
return (
563558
("linode", Instance),
@@ -569,6 +564,7 @@ def get_obj_grants():
569564
("longview", LongviewClient),
570565
("database", Database),
571566
("firewall", Firewall),
567+
("vpc", VPC),
572568
)
573569

574570

@@ -641,10 +637,47 @@ def _populate(self, json):
641637
self.global_grants = type("global_grants", (object,), json["global"])
642638

643639
for key, cls in get_obj_grants():
644-
lst = []
645-
for gdct in json[key]:
646-
lst.append(Grant(self._client, cls, gdct))
647-
setattr(self, key, lst)
640+
if key in json:
641+
lst = []
642+
for gdct in json[key]:
643+
lst.append(Grant(self._client, cls, gdct))
644+
setattr(self, key, lst)
645+
646+
@property
647+
def _global_grants_dict(self):
648+
"""
649+
The global grants stored in this object.
650+
"""
651+
return {
652+
k: v
653+
for k, v in vars(self.global_grants).items()
654+
if not k.startswith("_")
655+
}
656+
657+
@property
658+
def _grants_dict(self):
659+
"""
660+
The grants stored in this object.
661+
"""
662+
grants = {}
663+
for key, _ in get_obj_grants():
664+
if hasattr(self, key):
665+
lst = []
666+
for cg in getattr(self, key):
667+
lst.append(cg._serialize())
668+
grants[key] = lst
669+
670+
return grants
671+
672+
def _serialize(self):
673+
"""
674+
Returns the user grants in as JSON the api will accept.
675+
This is only relevant in the context of UserGrants.save
676+
"""
677+
return {
678+
"global": self._global_grants_dict,
679+
**self._grants_dict,
680+
}
648681

649682
def save(self):
650683
"""
@@ -653,19 +686,7 @@ def save(self):
653686
API Documentation: https://techdocs.akamai.com/linode-api/reference/put-user-grants
654687
"""
655688

656-
req = {
657-
"global": {
658-
k: v
659-
for k, v in vars(self.global_grants).items()
660-
if not k.startswith("_")
661-
},
662-
}
663-
664-
for key, _ in get_obj_grants():
665-
lst = []
666-
for cg in getattr(self, key):
667-
lst.append(cg._serialize())
668-
req[key] = lst
689+
req = self._serialize()
669690

670691
result = self._client.put(
671692
UserGrants.api_endpoint.format(username=self.username), data=req

test/integration/linode_client/test_linode_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ def test_get_account_settings(test_linode_client):
222222

223223
assert account_settings._populated == True
224224
assert re.search(
225-
"'network_helper':\s*(True|False)", str(account_settings._raw_json)
225+
r"'network_helper':\s*(True|False)", str(account_settings._raw_json)
226226
)
227227

228228

test/unit/objects/account_test.py

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from collections.abc import Iterable
2+
from copy import deepcopy
13
from datetime import datetime
24
from test.unit.base import ClientBaseCase
35

@@ -21,10 +23,12 @@
2123
ServiceTransfer,
2224
StackScript,
2325
User,
26+
UserGrants,
2427
Volume,
2528
get_obj_grants,
2629
)
2730
from linode_api4.objects.account import ChildAccount
31+
from linode_api4.objects.vpc import VPC
2832

2933

3034
class InvoiceTest(ClientBaseCase):
@@ -204,22 +208,6 @@ def test_get_payment_method(self):
204208
self.assertTrue(paymentMethod.is_default)
205209
self.assertEqual(paymentMethod.type, "credit_card")
206210

207-
def test_get_user_grant(self):
208-
"""
209-
Tests that a user grant is loaded correctly
210-
"""
211-
grants = get_obj_grants()
212-
213-
self.assertTrue(grants.count(("linode", Instance)) > 0)
214-
self.assertTrue(grants.count(("domain", Domain)) > 0)
215-
self.assertTrue(grants.count(("stackscript", StackScript)) > 0)
216-
self.assertTrue(grants.count(("nodebalancer", NodeBalancer)) > 0)
217-
self.assertTrue(grants.count(("volume", Volume)) > 0)
218-
self.assertTrue(grants.count(("image", Image)) > 0)
219-
self.assertTrue(grants.count(("longview", LongviewClient)) > 0)
220-
self.assertTrue(grants.count(("database", Database)) > 0)
221-
self.assertTrue(grants.count(("firewall", Firewall)) > 0)
222-
223211
def test_payment_method_make_default(self):
224212
"""
225213
Tests that making a payment method default creates the correct api request.
@@ -309,3 +297,89 @@ def test_child_account_create_token(self):
309297
token = child_account.create_token()
310298
self.assertEqual(token.token, "abcdefghijklmnop")
311299
self.assertEqual(m.call_data, {})
300+
301+
302+
def test_get_user_grant():
303+
"""
304+
Tests that a user grant is loaded correctly
305+
"""
306+
grants = get_obj_grants()
307+
308+
assert grants.count(("linode", Instance)) > 0
309+
assert grants.count(("domain", Domain)) > 0
310+
assert grants.count(("stackscript", StackScript)) > 0
311+
assert grants.count(("nodebalancer", NodeBalancer)) > 0
312+
assert grants.count(("volume", Volume)) > 0
313+
assert grants.count(("image", Image)) > 0
314+
assert grants.count(("longview", LongviewClient)) > 0
315+
assert grants.count(("database", Database)) > 0
316+
assert grants.count(("firewall", Firewall)) > 0
317+
assert grants.count(("vpc", VPC)) > 0
318+
319+
320+
def test_user_grants_serialization():
321+
"""
322+
Tests that user grants from JSON is serialized correctly
323+
"""
324+
user_grants_json = {
325+
"database": [
326+
{"id": 123, "label": "example-entity", "permissions": "read_only"}
327+
],
328+
"domain": [
329+
{"id": 123, "label": "example-entity", "permissions": "read_only"}
330+
],
331+
"firewall": [
332+
{"id": 123, "label": "example-entity", "permissions": "read_only"}
333+
],
334+
"global": {
335+
"account_access": "read_only",
336+
"add_databases": True,
337+
"add_domains": True,
338+
"add_firewalls": True,
339+
"add_images": True,
340+
"add_linodes": True,
341+
"add_longview": True,
342+
"add_nodebalancers": True,
343+
"add_placement_groups": True,
344+
"add_stackscripts": True,
345+
"add_volumes": True,
346+
"add_vpcs": True,
347+
"cancel_account": False,
348+
"child_account_access": True,
349+
"longview_subscription": True,
350+
},
351+
"image": [
352+
{"id": 123, "label": "example-entity", "permissions": "read_only"}
353+
],
354+
"linode": [
355+
{"id": 123, "label": "example-entity", "permissions": "read_only"}
356+
],
357+
"longview": [
358+
{"id": 123, "label": "example-entity", "permissions": "read_only"}
359+
],
360+
"nodebalancer": [
361+
{"id": 123, "label": "example-entity", "permissions": "read_only"}
362+
],
363+
"stackscript": [
364+
{"id": 123, "label": "example-entity", "permissions": "read_only"}
365+
],
366+
"volume": [
367+
{"id": 123, "label": "example-entity", "permissions": "read_only"}
368+
],
369+
"vpc": [
370+
{"id": 123, "label": "example-entity", "permissions": "read_only"}
371+
],
372+
}
373+
374+
expected_serialized_grants = deepcopy(user_grants_json)
375+
376+
for grants in expected_serialized_grants.values():
377+
if isinstance(grants, Iterable):
378+
for grant in grants:
379+
if isinstance(grant, dict) and "label" in grant:
380+
del grant["label"]
381+
382+
assert (
383+
UserGrants(None, None, user_grants_json)._serialize()
384+
== expected_serialized_grants
385+
)

0 commit comments

Comments
 (0)