Skip to content

Commit 6f32fb5

Browse files
committed
chore: base resource refactor.
1 parent a1574e3 commit 6f32fb5

12 files changed

Lines changed: 177 additions & 198 deletions

File tree

nitric/resources/apis.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,6 @@ def __init__(self, name: str, opts: ApiOptions = None):
124124
self.security_definitions = opts.security_definitions
125125
self.security = opts.security
126126

127-
self._channel = new_default_channel()
128-
self._resources_stub = ResourceServiceStub(channel=self._channel)
129-
130127
async def _register(self):
131128
try:
132129
await self._resources_stub.declare(

nitric/resources/base.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@
2222
from abc import ABC, abstractmethod
2323
from asyncio import Task
2424

25-
from typing import TypeVar, Type, Coroutine, Union
25+
from typing import TypeVar, Type, Coroutine, Union, List
26+
27+
from grpclib import GRPCError
28+
from nitricapi.nitric.resource.v1 import Action, PolicyResource, Resource, ResourceType, ResourceDeclareRequest, \
29+
ResourceServiceStub
30+
31+
from nitric.api.exception import exception_from_grpc_error
32+
from nitric.utils import new_default_channel
2633

2734
T = TypeVar("T", bound="BaseResource")
2835

@@ -35,6 +42,8 @@ class BaseResource(ABC):
3542
def __init__(self):
3643
"""Construct a new resource."""
3744
self._reg: Union[Task, None] = None
45+
self._channel = new_default_channel()
46+
self._resources_stub = ResourceServiceStub(channel=self._channel)
3847

3948
@abstractmethod
4049
async def _register(self):
@@ -57,3 +66,41 @@ def make(cls: Type[T], name: str) -> T:
5766
loop.run_until_complete(r._register())
5867

5968
return r
69+
70+
71+
class SecureResource(BaseResource):
72+
"""A secure base resource class"""
73+
74+
@abstractmethod
75+
def _to_resource(self) -> Resource:
76+
pass
77+
78+
@abstractmethod
79+
def _perms_to_actions(self, permissions: List[str]) -> List[Action]:
80+
pass
81+
82+
async def _register_policy_async(self, permissions: List[str]):
83+
# if self._reg is not None:
84+
# await asyncio.wait({self._reg})
85+
86+
policy = PolicyResource(
87+
principals=[Resource(type=ResourceType.Function)],
88+
actions=self._perms_to_actions(permissions),
89+
resources=[self._to_resource()],
90+
)
91+
try:
92+
await self._resources_stub.declare(
93+
resource_declare_request=ResourceDeclareRequest(resource=Resource(type=ResourceType.Policy),
94+
policy=policy))
95+
except GRPCError as grpc_err:
96+
raise exception_from_grpc_error(grpc_err)
97+
pass
98+
99+
def _register_policy(self, permissions: List[str]):
100+
try:
101+
loop = asyncio.get_running_loop()
102+
loop.create_task(self._register_policy_async(permissions))
103+
except RuntimeError:
104+
loop = asyncio.get_event_loop()
105+
loop.run_until_complete(self._register_policy_async(permissions))
106+

nitric/resources/buckets.py

Lines changed: 22 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
Action, ResourceDeclareRequest,
3737
)
3838

39-
from nitric.resources.base import BaseResource
39+
from nitric.resources.base import BaseResource, SecureResource
4040

4141

4242
class BucketPermission(Enum):
@@ -47,25 +47,7 @@ class BucketPermission(Enum):
4747
deleting = "deleting"
4848

4949

50-
def _perms_to_actions(permissions: List[Union[BucketPermission, str]]) -> List[Action]:
51-
permission_actions_map = {
52-
BucketPermission.reading: [Action.BucketFileGet, Action.BucketFileList],
53-
BucketPermission.writing: [Action.BucketFilePut],
54-
BucketPermission.deleting: [Action.BucketFileDelete],
55-
}
56-
# convert strings to the enum value where needed
57-
perms = [
58-
permission if isinstance(permission, BucketPermission) else BucketPermission[permission.lower()]
59-
for permission in permissions
60-
]
61-
return [action for perm in perms for action in permission_actions_map[perm]]
62-
63-
64-
def _to_resource(b: Bucket) -> Resource:
65-
return Resource(name=b.name, type=ResourceType.Bucket)
66-
67-
68-
class Bucket(BaseResource):
50+
class Bucket(SecureResource):
6951
"""A bucket resource, used for storage and retrieval of blob/binary data."""
7052

7153
name: str
@@ -75,30 +57,33 @@ def __init__(self, name: str):
7557
"""Create a bucket with the name provided or references it if it already exists."""
7658
super().__init__()
7759
self.name = name
78-
self._channel = new_default_channel()
79-
self._resources_stub = ResourceServiceStub(channel=self._channel)
8060

8161
async def _register(self):
8262
try:
83-
await self._resources_stub.declare(resource_declare_request=ResourceDeclareRequest(resource=_to_resource(self)))
63+
await self._resources_stub.declare(
64+
resource_declare_request=ResourceDeclareRequest(resource=self._to_resource()))
8465
except GRPCError as grpc_err:
8566
raise exception_from_grpc_error(grpc_err)
8667

87-
async def allow(self, permissions: List[str]) -> BucketRef:
68+
def _perms_to_actions(self, permissions: List[Union[BucketPermission, str]]) -> List[Action]:
69+
permission_actions_map = {
70+
BucketPermission.reading: [Action.BucketFileGet, Action.BucketFileList],
71+
BucketPermission.writing: [Action.BucketFilePut],
72+
BucketPermission.deleting: [Action.BucketFileDelete],
73+
}
74+
# convert strings to the enum value where needed
75+
perms = [
76+
permission if isinstance(permission, BucketPermission) else BucketPermission[permission.lower()]
77+
for permission in permissions
78+
]
79+
return [action for perm in perms for action in permission_actions_map[perm]]
80+
81+
def _to_resource(self) -> Resource:
82+
return Resource(name=self.name, type=ResourceType.Bucket)
83+
84+
def allow(self, permissions: List[Union[BucketPermission, str]]) -> BucketRef:
8885
"""Request the required permissions for this resource."""
89-
# Ensure registration of the resource is complete before requesting permissions.
90-
if self._reg is not None:
91-
await self._reg
92-
93-
policy = PolicyResource(
94-
principals=[Resource(type=ResourceType.Function)],
95-
actions=_perms_to_actions(permissions),
96-
resources=[_to_resource(self)],
97-
)
98-
try:
99-
await self._resources_stub.declare(resource_declare_request=ResourceDeclareRequest(resource=Resource(type=ResourceType.Policy), policy=policy))
100-
except GRPCError as grpc_err:
101-
raise exception_from_grpc_error(grpc_err)
86+
self._register_policy(permissions)
10287

10388
return Storage().bucket(self.name)
10489

nitric/resources/collections.py

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
Action, ResourceDeclareRequest,
3737
)
3838

39-
from nitric.resources.base import BaseResource
39+
from nitric.resources.base import BaseResource, SecureResource
4040

4141

4242
class CollectionPermission(Enum):
@@ -47,56 +47,47 @@ class CollectionPermission(Enum):
4747
deleting = "deleting"
4848

4949

50-
def _perms_to_actions(permissions: List[Union[CollectionPermission, str]]) -> List[Action]:
51-
permission_actions_map = {
52-
CollectionPermission.reading: [Action.CollectionDocumentRead, Action.CollectionQuery, Action.CollectionList],
53-
CollectionPermission.writing: [Action.CollectionDocumentWrite, Action.CollectionList],
54-
CollectionPermission.deleting: [Action.CollectionDocumentDelete, Action.CollectionList],
55-
}
56-
# convert strings to the enum value where needed
57-
perms = [
58-
permission if isinstance(permission, CollectionPermission) else CollectionPermission[permission.lower()]
59-
for permission in permissions
60-
]
6150

62-
return [action for perm in perms for action in permission_actions_map[perm]]
6351

6452

65-
def _to_resource(collection: Collection) -> Resource:
66-
return Resource(name=collection.name, type=ResourceType.Collection)
6753

6854

69-
class Collection(BaseResource):
55+
class Collection(SecureResource):
7056
"""A document collection resource."""
7157

7258
def __init__(self, name: str):
7359
"""Construct a new document collection."""
7460
super().__init__()
7561
self.name = name
76-
self._channel = new_default_channel()
77-
self._resources_stub = ResourceServiceStub(channel=self._channel)
7862

7963
async def _register(self):
8064
try:
81-
await self._resources_stub.declare(resource_declare_request=ResourceDeclareRequest(resource=_to_resource(self)))
65+
await self._resources_stub.declare(resource_declare_request=ResourceDeclareRequest(resource=self._to_resource()))
8266
except GRPCError as grpc_err:
8367
raise exception_from_grpc_error(grpc_err)
8468

85-
async def allow(self, permissions: List[Union[CollectionPermission, str]]) -> CollectionRef:
69+
def _to_resource(self) -> Resource:
70+
return Resource(name=self.name, type=ResourceType.Collection)
71+
72+
def _perms_to_actions(self, permissions: List[Union[CollectionPermission, str]]) -> List[Action]:
73+
permission_actions_map = {
74+
CollectionPermission.reading: [Action.CollectionDocumentRead, Action.CollectionQuery,
75+
Action.CollectionList],
76+
CollectionPermission.writing: [Action.CollectionDocumentWrite, Action.CollectionList],
77+
CollectionPermission.deleting: [Action.CollectionDocumentDelete, Action.CollectionList],
78+
}
79+
# convert strings to the enum value where needed
80+
perms = [
81+
permission if isinstance(permission, CollectionPermission) else CollectionPermission[permission.lower()]
82+
for permission in permissions
83+
]
84+
85+
return [action for perm in perms for action in permission_actions_map[perm]]
86+
87+
def allow(self, permissions: List[Union[CollectionPermission, str]]) -> CollectionRef:
8688
"""Request the required permissions for this collection."""
8789
# Ensure registration of the resource is complete before requesting permissions.
88-
if self._reg is not None:
89-
await self._reg
90-
91-
policy = PolicyResource(
92-
principals=[Resource(type=ResourceType.Function)],
93-
actions=_perms_to_actions(permissions),
94-
resources=[_to_resource(self)],
95-
)
96-
try:
97-
await self._resources_stub.declare(resource_declare_request=ResourceDeclareRequest(resource=Resource(type=ResourceType.Policy), policy=policy))
98-
except GRPCError as grpc_err:
99-
raise exception_from_grpc_error(grpc_err)
90+
self._register_policy(permissions)
10091

10192
return Documents().collection(self.name)
10293

nitric/resources/queues.py

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
Action, ResourceDeclareRequest,
3636
)
3737

38-
from nitric.resources.base import BaseResource
38+
from nitric.resources.base import BaseResource, SecureResource
3939

4040

4141
class QueuePermission(Enum):
@@ -45,25 +45,7 @@ class QueuePermission(Enum):
4545
receiving = "receiving"
4646

4747

48-
def _perms_to_actions(permissions: List[Union[QueuePermission, str]]) -> List[Action]:
49-
permission_actions_map = {
50-
QueuePermission.sending: [Action.QueueSend, Action.QueueList, Action.QueueDetail],
51-
QueuePermission.receiving: [Action.QueueReceive, Action.QueueList, Action.QueueDetail],
52-
}
53-
# convert strings to the enum value where needed
54-
perms = [
55-
permission if isinstance(permission, QueuePermission) else QueuePermission[permission.lower()]
56-
for permission in permissions
57-
]
58-
59-
return [action for perm in perms for action in permission_actions_map[perm]]
60-
61-
62-
def _to_resource(queue: Queue) -> Resource:
63-
return Resource(name=queue.name, type=ResourceType.Queue)
64-
65-
66-
class Queue(BaseResource):
48+
class Queue(SecureResource):
6749
"""A queue resource."""
6850

6951
name: str
@@ -73,30 +55,34 @@ def __init__(self, name: str):
7355
"""Construct a new queue resource."""
7456
super().__init__()
7557
self.name = name
76-
self._channel = new_default_channel()
77-
self._resources_stub = ResourceServiceStub(channel=self._channel)
58+
59+
def _to_resource(self) -> Resource:
60+
return Resource(name=self.name, type=ResourceType.Queue)
61+
62+
def _perms_to_actions(self, permissions: List[Union[QueuePermission, str]]) -> List[Action]:
63+
permission_actions_map = {
64+
QueuePermission.sending: [Action.QueueSend, Action.QueueList, Action.QueueDetail],
65+
QueuePermission.receiving: [Action.QueueReceive, Action.QueueList, Action.QueueDetail],
66+
}
67+
# convert strings to the enum value where needed
68+
perms = [
69+
permission if isinstance(permission, QueuePermission) else QueuePermission[permission.lower()]
70+
for permission in permissions
71+
]
72+
73+
return [action for perm in perms for action in permission_actions_map[perm]]
7874

7975
async def _register(self):
8076
try:
81-
await self._resources_stub.declare(resource_declare_request=ResourceDeclareRequest(resource=_to_resource(self)))
77+
await self._resources_stub.declare(
78+
resource_declare_request=ResourceDeclareRequest(resource=self._to_resource()))
8279
except GRPCError as grpc_err:
8380
raise exception_from_grpc_error(grpc_err)
8481

85-
async def allow(self, permissions: List[Union[QueuePermission, str]]) -> QueueRef:
82+
def allow(self, permissions: List[Union[QueuePermission, str]]) -> QueueRef:
8683
"""Request the required permissions for this queue."""
8784
# Ensure registration of the resource is complete before requesting permissions.
88-
if self._reg is not None:
89-
await self._reg
90-
91-
policy = PolicyResource(
92-
principals=[Resource(type=ResourceType.Function)],
93-
actions=_perms_to_actions(permissions),
94-
resources=[_to_resource(self)],
95-
)
96-
try:
97-
await self._resources_stub.declare(resource_declare_request=ResourceDeclareRequest(resource=Resource(type=ResourceType.Policy), policy=policy))
98-
except GRPCError as grpc_err:
99-
raise exception_from_grpc_error(grpc_err)
85+
self._register_policy(permissions)
10086

10187
return Queues().queue(self.name)
10288

0 commit comments

Comments
 (0)