Skip to content

Commit

Permalink
GIT-2045: enable versioning and strict slash on BlueprintGroup
Browse files Browse the repository at this point in the history
  • Loading branch information
harshanarayana committed Mar 7, 2021
1 parent be905e0 commit 4855586
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 10 deletions.
36 changes: 33 additions & 3 deletions sanic/blueprint_group.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from collections.abc import MutableSequence
from typing import List

import sanic
from sanic.exceptions import APIVersionMismatchException


class BlueprintGroup(MutableSequence):
"""
Expand All @@ -16,6 +19,11 @@ class as a list/tuple inside the existing implementation.
bp1 = Blueprint('bp1', url_prefix='/bp1')
bp2 = Blueprint('bp2', url_prefix='/bp2')
bp3 = Blueprint('bp3', url_prefix='/bp4')
bp3 = Blueprint('bp3', url_prefix='/bp4')
bpg = BlueprintGroup(bp3, bp4, url_prefix="/api", version="v1")
@bp1.middleware('request')
async def bp1_only_middleware(request):
print('applied on Blueprint : bp1 Only')
Expand All @@ -28,6 +36,14 @@ async def bp1_route(request):
async def bp2_route(request, param):
return text(param)
@bp3.route('/')
async def bp1_route(request):
return text('bp1')
@bp4.route('/<param>')
async def bp2_route(request, param):
return text(param)
group = Blueprint.group(bp1, bp2)
@group.middleware('request')
Expand All @@ -36,18 +52,23 @@ async def group_middleware(request):
# Register Blueprint group under the app
app.blueprint(group)
app.blueprint(bpg)
"""

__slots__ = ("_blueprints", "_url_prefix")
__slots__ = ("_blueprints", "_url_prefix", "_version", "_strict_slashes")

def __init__(self, url_prefix=None):
def __init__(self, url_prefix=None, version=None, strict_slashes=None):
"""
Create a new Blueprint Group
:param url_prefix: URL: to be prefixed before all the Blueprint Prefix
:param version: API Version for the blueprint group. This will be inherited by each of the Blueprint
:param strict_slashes: URL Strict slash behavior indicator
"""
self._blueprints = []
self._url_prefix = url_prefix
self._version = version
self._strict_slashes = strict_slashes

@property
def url_prefix(self) -> str:
Expand Down Expand Up @@ -121,7 +142,7 @@ def __len__(self) -> int:
"""
return len(self._blueprints)

def insert(self, index: int, item: object) -> None:
def insert(self, index: int, item: "sanic.blueprints.Blueprint") -> None:
"""
The Abstract class `MutableSequence` leverages this insert method to
perform the `BlueprintGroup.append` operation.
Expand All @@ -130,6 +151,15 @@ def insert(self, index: int, item: object) -> None:
:param item: New `Blueprint` object.
:return: None
"""
if self._version and item.version and self._version != item.version:
raise APIVersionMismatchException(
f"API Version Mismatch. Blueprint {item.name} has version {item.version} "
f"while Blueprint Group has {self._version}"
)
if self._version and not item.version:
item.version = self._version
if self._strict_slashes is not None:
item.strict_slashes = self._strict_slashes
self._blueprints.insert(index, item)

def middleware(self, *args, **kwargs):
Expand Down
24 changes: 20 additions & 4 deletions sanic/blueprints.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from collections import defaultdict
from typing import Dict, List, Optional
from typing import Dict, List, Optional, Iterable

from sanic_routing.route import Route # type: ignore

from sanic.base import BaseSanic
from sanic.blueprint_group import BlueprintGroup
from sanic.exceptions import APIVersionMismatchException
from sanic.handlers import ListenerType, MiddlewareType, RouteHandler
from sanic.models.futures import FutureRoute, FutureStatic

Expand Down Expand Up @@ -87,16 +88,18 @@ def exception(self, *args, **kwargs):
return super().exception(*args, **kwargs)

@staticmethod
def group(*blueprints, url_prefix=""):
def group(*blueprints, url_prefix="", version=None, strict_slashes=None):
"""
Create a list of blueprints, optionally grouping them under a
general URL prefix.
:param blueprints: blueprints to be registered as a group
:param url_prefix: URL route to be prepended to all sub-prefixes
:param version: API Version to be used for Blueprint group
:param strict_slashes: Indicate strict slash termination behavior for URL
"""

def chain(nested):
def chain(nested) -> Iterable[Blueprint]:
"""itertools.chain() but leaves strings untouched"""
for i in nested:
if isinstance(i, (list, tuple)):
Expand All @@ -106,10 +109,23 @@ def chain(nested):
else:
yield i

bps = BlueprintGroup(url_prefix=url_prefix)
bps = BlueprintGroup(
url_prefix=url_prefix,
version=version,
strict_slashes=strict_slashes,
)
for bp in chain(blueprints):
if bp.version and version and bp.version != version:
raise APIVersionMismatchException(
f"API Version Mismatch. Blueprint {bp.name} has version {bp.version} "
f"while Blueprint Group has {version}"
)
if bp.url_prefix is None:
bp.url_prefix = ""
if not bp.version and version:
bp.version = version
if strict_slashes is not None:
bp.strict_slashes = strict_slashes
bp.url_prefix = url_prefix + bp.url_prefix
bps.append(bp)
return bps
Expand Down
9 changes: 9 additions & 0 deletions sanic/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ def __init__(self, message, status_code=None, quiet=None):
self.quiet = True


class APIVersionMismatchException(SanicException):
"""
Exception generated for route setup configuration workflow when blueprint and blueprint-groups
are being configured to use two different API Versions
"""

pass


@add_status_code(404)
class NotFound(SanicException):
"""
Expand Down
4 changes: 1 addition & 3 deletions sanic/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,7 @@ async def accept(self) -> None:
await self._send(
{
"type": "websocket.accept",
"subprotocol": ",".join(
list(self.subprotocols)
),
"subprotocol": ",".join(list(self.subprotocols)),
}
)

Expand Down

0 comments on commit 4855586

Please sign in to comment.