forked from sanic-org/sanic
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
enable blueprint group middleware support
This commit will enable the users to implement a middleware at the blueprint group level whereby enforcing the middleware automatically to each of the available Blueprints that are part of the group. This will eanble a simple way in which a certain set of common features and criteria can be enforced on a Blueprint group. i.e. authentication and authorization This commit will address the feature request raised as part of Issue sanic-org#1386 Signed-off-by: Harsha Narayana <[email protected]>
- Loading branch information
1 parent
52deeba
commit e0912ef
Showing
4 changed files
with
158 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
class BlueprintGroup(object): | ||
""" | ||
This class provides a mechanism to implement a Blueprint Group | ||
using the `Blueprint.group` method. To avoid having to re-write | ||
some of the existing implementation, this class provides a custom | ||
iterator implementation that will let you use the object of this | ||
class as a list/tuple inside the existing implementation. | ||
""" | ||
|
||
def __init__(self, url_prefix=None): | ||
""" | ||
Create a new Blueprint Group | ||
:param url_prefix: URL: to be prefixed before all the Blueprint Prefix | ||
""" | ||
self._blueprints = list() | ||
self._iter_position = 0 | ||
self._url_prefix = url_prefix | ||
|
||
@property | ||
def url_prefix(self): | ||
""" | ||
Retrieve the URL prefix being used for the Current Blueprint Group | ||
:return: string with url prefix | ||
""" | ||
return self._url_prefix | ||
|
||
@property | ||
def blueprints(self): | ||
""" | ||
Retrieve a list of all the available blueprints under this group. | ||
:return: List of Blueprint instance | ||
""" | ||
return self._blueprints | ||
|
||
@blueprints.setter | ||
def blueprints(self, blueprint): | ||
""" | ||
Add a new Blueprint to the Group under consideration. | ||
:param blueprint: Instance of Blueprint | ||
:return: None | ||
""" | ||
self._blueprints.append(blueprint) | ||
|
||
def __iter__(self): | ||
"""Tun the class Blueprint Group into an Iterable item""" | ||
return self | ||
|
||
def __next__(self): | ||
""" | ||
A Custom method to iterate over the Blueprint Objects in the | ||
group under consideration | ||
""" | ||
if not len(self._blueprints) or self._iter_position >= len( | ||
self._blueprints | ||
): | ||
raise StopIteration | ||
else: | ||
self._iter_position += 1 | ||
return self._blueprints[self._iter_position - 1] | ||
|
||
def middleware(self, *args, **kwargs): | ||
""" | ||
A decorator that can be used to implement a Middleware plugin to | ||
all of the Blueprints that belongs to this specific Blueprint Group. | ||
In case of nested Blueprint Groups, the same middleware is applied | ||
across each of the Blueprints recursively. | ||
:param args: Optional positional Parameters to be use middleware | ||
:param kwargs: Optional Keyword arg to use with Middleware | ||
:return: Partial function to apply the middleware | ||
""" | ||
|
||
def register_middleware_for_blueprints(fn): | ||
for blueprint in self.blueprints: | ||
blueprint.middleware(fn, *args, **kwargs) | ||
|
||
return register_middleware_for_blueprints |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from sanic.app import Sanic | ||
from sanic.blueprints import Blueprint | ||
from sanic.response import text | ||
|
||
MIDDLEWARE_INVOKE_COUNTER = { | ||
'request': 0, | ||
'response': 0 | ||
} | ||
|
||
|
||
def test_bp_group(app: Sanic): | ||
blueprint_1 = Blueprint('blueprint_1', url_prefix="/bp1") | ||
blueprint_2 = Blueprint('blueprint_2', url_prefix='/bp2') | ||
|
||
@blueprint_1.route('/') | ||
def blueprint_1_default_route(request): | ||
return text("BP1_OK") | ||
|
||
@blueprint_2.route("/") | ||
def blueprint_2_default_route(request): | ||
return text("BP2_OK") | ||
|
||
blueprint_group_1 = Blueprint.group( | ||
blueprint_1, blueprint_2, url_prefix="/bp") | ||
|
||
blueprint_3 = Blueprint('blueprint_3', url_prefix="/bp3") | ||
|
||
@blueprint_group_1.middleware('request') | ||
def blueprint_group_1_middleware(request): | ||
global MIDDLEWARE_INVOKE_COUNTER | ||
MIDDLEWARE_INVOKE_COUNTER['request'] += 1 | ||
|
||
@blueprint_3.route("/") | ||
def blueprint_3_default_route(request): | ||
return text("BP3_OK") | ||
|
||
blueprint_group_2 = Blueprint.group(blueprint_group_1, blueprint_3, url_prefix="/api") | ||
|
||
@blueprint_group_2.middleware('response') | ||
def blueprint_group_2_middleware(request, response): | ||
global MIDDLEWARE_INVOKE_COUNTER | ||
MIDDLEWARE_INVOKE_COUNTER['response'] += 1 | ||
|
||
app.blueprint(blueprint_group_2) | ||
|
||
@app.route("/") | ||
def app_default_route(request): | ||
return text("APP_OK") | ||
|
||
_, response = app.test_client.get("/") | ||
assert response.text == 'APP_OK' | ||
|
||
_, response = app.test_client.get("/api/bp/bp1") | ||
assert response.text == 'BP1_OK' | ||
|
||
_, response = app.test_client.get("/api/bp/bp2") | ||
assert response.text == 'BP2_OK' | ||
|
||
_, response = app.test_client.get('/api/bp3') | ||
assert response.text == 'BP3_OK' | ||
|
||
assert MIDDLEWARE_INVOKE_COUNTER['response'] == 4 | ||
assert MIDDLEWARE_INVOKE_COUNTER['request'] == 4 |