Skip to content

Commit

Permalink
Release (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
jyecusch authored Apr 5, 2023
2 parents bfe6457 + f6ffdf5 commit ee428b5
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 12 deletions.
10 changes: 10 additions & 0 deletions nitric/api/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,19 @@ class UnknownException(NitricServiceException):

pass


class NitricResourceException(Exception):
"""Illegal nitric resource creation."""

pass


class NitricUnavailableException(Exception):
"""Unable to connect to a nitric server."""

pass


def exception_from_grpc_error(error: GRPCError):
"""Translate a gRPC error to a nitric api exception."""
return exception_from_grpc_code(error.status.value, error.message)
Expand Down
18 changes: 14 additions & 4 deletions nitric/application.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
from nitric.faas import FunctionServer
from nitric.api.exception import NitricUnavailableException

# from nitric.resources.base import BaseResource
from typing import Dict, List, Type, Any, TypeVar
Expand Down Expand Up @@ -28,11 +29,16 @@ def _register_worker(cls, srv: FunctionServer):

@classmethod
def _create_resource(cls, resource: Type[BT], name: str, *args, **kwargs) -> BT:
resource_type = resource.__name__.lower()
if cls._cache.get(resource_type).get(name) is None:
cls._cache[resource_type][name] = resource.make(name, *args, **kwargs)
try:
resource_type = resource.__name__.lower()
if cls._cache.get(resource_type).get(name) is None:
cls._cache[resource_type][name] = resource.make(name, *args, **kwargs)

return cls._cache[resource_type][name]
return cls._cache[resource_type][name]
except ConnectionRefusedError:
raise NitricUnavailableException(
'Unable to connect to a nitric server! If you\'re running locally make sure to run "nitric start"'
)

@classmethod
def run(cls):
Expand All @@ -50,3 +56,7 @@ def run(cls):
loop.run_until_complete(asyncio.gather(*[wkr.start() for wkr in cls._workers]))
except KeyboardInterrupt:
print("\nexiting")
except ConnectionRefusedError:
raise NitricUnavailableException(
'Unable to connect to a nitric server! If you\'re running locally make sure to run "nitric start"'
)
6 changes: 4 additions & 2 deletions nitric/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#
"""Nitric Python SDK API Documentation. See: https://nitric.io/docs?lang=python for full framework documentation."""

from nitric.resources.apis import Api, api, MethodOptions, ApiOptions
from nitric.resources.apis import Api, api, MethodOptions, ApiOptions, ApiDetails, JwtSecurityDefinition
from nitric.resources.buckets import Bucket, bucket
from nitric.resources.collections import Collection, collection
from nitric.resources.queues import Queue, queue
Expand All @@ -30,6 +30,8 @@
"api",
"Api",
"ApiOptions",
"ApiDetails",
"JwtSecurityDefinition",
"MethodOptions",
"bucket",
"Bucket",
Expand All @@ -42,5 +44,5 @@
"secret",
"Secret",
"topic",
"Topic"
"Topic",
]
46 changes: 40 additions & 6 deletions nitric/resources/apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#
from __future__ import annotations
from typing import List, Union
from dataclasses import dataclass
from nitric.faas import ApiWorkerOptions, FunctionServer, HttpMiddleware, Middleware, MethodOptions, HttpMethod
from nitric.application import Nitric
from nitric.resources.base import BaseResource
Expand All @@ -29,11 +30,27 @@
ApiSecurityDefinition,
ApiSecurityDefinitionJwt,
ResourceDeclareRequest,
ResourceDetailsRequest,
)
from grpclib import GRPCError
from nitric.api.exception import exception_from_grpc_error


@dataclass
class ApiDetails:
"""Represents the APIs deployment details."""

# the identifier of the resource
id: str
# The provider this resource is deployed with (e.g. aws)
provider: str
# The service this resource is deployed on (e.g. ApiGateway)
service: str
# The url of the API
url: str


@dataclass
class JwtSecurityDefinition:
"""Represents the JWT security definition for an API."""

Expand All @@ -58,7 +75,7 @@ class ApiOptions:
def __init__(
self,
path: str = "",
middleware: List[Middleware] = None,
middleware: List[Middleware] = [],
security_definitions: dict[str, SecurityDefinition] = None,
security: dict[str, List[str]] = None,
):
Expand All @@ -74,7 +91,7 @@ class RouteOptions:

middleware: Union[None, List[Middleware]]

def __init__(self, middleware: List[Middleware] = None):
def __init__(self, middleware: List[Middleware] = []):
"""Construct a new route options object."""
self.middleware = middleware

Expand Down Expand Up @@ -118,7 +135,7 @@ def __init__(self, name: str, opts: ApiOptions = None):
opts = ApiOptions()

self.name = name
self.middleware = opts.middleware
self.middleware = opts.middleware if opts.middleware is not None else []
self.path = opts.path
self.routes = []
self.security_definitions = opts.security_definitions
Expand Down Expand Up @@ -246,6 +263,23 @@ def decorator(function: HttpMiddleware):

return decorator

async def _details(self) -> ApiDetails:
"""Get the API deployment details."""
try:
res = await self._resources_stub.details(
resource_details_request=ResourceDetailsRequest(
resource=_to_resource(self),
)
)
return ApiDetails(res.id, res.provider, res.service, res.api.url)
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)

async def URL(self) -> str:
"""Get the APIs live URL."""
details = await self._details()
return details.url


class Route:
"""An HTTP route."""
Expand All @@ -257,8 +291,8 @@ class Route:
def __init__(self, api: Api, path: str, opts: RouteOptions):
"""Define a route to be handled by the provided API."""
self.api = api
self.path = path
self.middleware = opts.middleware
self.path = api.path.join(path)
self.middleware = opts.middleware if opts.middleware is not None else []

def method(self, methods: List[HttpMethod], *middleware: HttpMiddleware, opts: MethodOptions = None):
"""Register middleware for multiple HTTP Methods."""
Expand Down Expand Up @@ -304,7 +338,7 @@ def __init__(
self.route = route
self.methods = methods
self.server = FunctionServer(ApiWorkerOptions(route.api.name, route.path, methods, opts))
self.server.http(*middleware)
self.server.http(*route.api.middleware, *route.middleware, *middleware)

def start(self):
"""Start the server which will respond to incoming requests."""
Expand Down

0 comments on commit ee428b5

Please sign in to comment.