Enable Middleware Support for Blueprint Groups (#1399)
* 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 #1386 Signed-off-by: Harsha Narayana <harsha2k4@gmail.com> * enable indexing of BlueprintGroup object Signed-off-by: Harsha Narayana <harsha2k4@gmail.com> * rename blueprint group file to fix spelling error Signed-off-by: Harsha Narayana <harsha2k4@gmail.com> * add documentation and additional unit tests Signed-off-by: Harsha Narayana <harsha2k4@gmail.com> * cleanup and optimize headers in unit test file Signed-off-by: Harsha Narayana <harsha2k4@gmail.com> * fix Bluprint Group iteratable method Signed-off-by: Harsha Narayana <harsha2k4@gmail.com> * add additional unit test to check StopIteration condition Signed-off-by: Harsha Narayana <harsha2k4@gmail.com> * cleanup iter protocol implemenation for blueprint group and add slots Signed-off-by: Harsha Narayana <harsha2k4@gmail.com> * fix blueprint group middleware invocation identification Signed-off-by: Harsha Narayana <harsha2k4@gmail.com> * feat: enable list behavior on blueprint group object and use append instead of properly to add blueprint to group Signed-off-by: Harsha Narayana <harsha2k4@gmail.com>
This commit is contained in:
		 Harsha Narayana
					Harsha Narayana
				
			
				
					committed by
					
						 Stephen Sadowski
						Stephen Sadowski
					
				
			
			
				
	
			
			
			 Stephen Sadowski
						Stephen Sadowski
					
				
			
						parent
						
							e5c7589fc0
						
					
				
				
					commit
					348964fe12
				
			
							
								
								
									
										180
									
								
								tests/test_blueprint_group.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								tests/test_blueprint_group.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,180 @@ | ||||
| from pytest import raises | ||||
|  | ||||
| from sanic.app import Sanic | ||||
| from sanic.blueprints import Blueprint | ||||
| from sanic.request import Request | ||||
| from sanic.response import text, HTTPResponse | ||||
|  | ||||
| MIDDLEWARE_INVOKE_COUNTER = {"request": 0, "response": 0} | ||||
|  | ||||
| AUTH = "dGVzdDp0ZXN0Cg==" | ||||
|  | ||||
|  | ||||
| def test_bp_group_indexing(app: Sanic): | ||||
|     blueprint_1 = Blueprint("blueprint_1", url_prefix="/bp1") | ||||
|     blueprint_2 = Blueprint("blueprint_2", url_prefix="/bp2") | ||||
|  | ||||
|     group = Blueprint.group(blueprint_1, blueprint_2) | ||||
|     assert group[0] == blueprint_1 | ||||
|  | ||||
|     with raises(expected_exception=IndexError) as e: | ||||
|         _ = group[3] | ||||
|  | ||||
|  | ||||
| def test_bp_group_with_additional_route_params(app: Sanic): | ||||
|     blueprint_1 = Blueprint("blueprint_1", url_prefix="/bp1") | ||||
|     blueprint_2 = Blueprint("blueprint_2", url_prefix="/bp2") | ||||
|  | ||||
|     @blueprint_1.route( | ||||
|         "/request_path", methods=frozenset({"PUT", "POST"}), version=2 | ||||
|     ) | ||||
|     def blueprint_1_v2_method_with_put_and_post(request: Request): | ||||
|         if request.method == "PUT": | ||||
|             return text("PUT_OK") | ||||
|         elif request.method == "POST": | ||||
|             return text("POST_OK") | ||||
|  | ||||
|     @blueprint_2.route( | ||||
|         "/route/<param>", methods=frozenset({"DELETE", "PATCH"}), name="test" | ||||
|     ) | ||||
|     def blueprint_2_named_method(request: Request, param): | ||||
|         if request.method == "DELETE": | ||||
|             return text("DELETE_{}".format(param)) | ||||
|         elif request.method == "PATCH": | ||||
|             return text("PATCH_{}".format(param)) | ||||
|  | ||||
|     blueprint_group = Blueprint.group( | ||||
|         blueprint_1, blueprint_2, url_prefix="/api" | ||||
|     ) | ||||
|  | ||||
|     @blueprint_group.middleware("request") | ||||
|     def authenticate_request(request: Request): | ||||
|         global AUTH | ||||
|         auth = request.headers.get("authorization") | ||||
|         if auth: | ||||
|             # Dummy auth check. We can have anything here and it's fine. | ||||
|             if AUTH not in auth: | ||||
|                 return text("Unauthorized", status=401) | ||||
|         else: | ||||
|             return text("Unauthorized", status=401) | ||||
|  | ||||
|     @blueprint_group.middleware("response") | ||||
|     def enhance_response_middleware(request: Request, response: HTTPResponse): | ||||
|         response.headers.add("x-test-middleware", "value") | ||||
|  | ||||
|     app.blueprint(blueprint_group) | ||||
|  | ||||
|     header = {"authorization": " ".join(["Basic", AUTH])} | ||||
|     _, response = app.test_client.put( | ||||
|         "/v2/api/bp1/request_path", headers=header | ||||
|     ) | ||||
|     assert response.text == "PUT_OK" | ||||
|     assert response.headers.get("x-test-middleware") == "value" | ||||
|  | ||||
|     _, response = app.test_client.post( | ||||
|         "/v2/api/bp1/request_path", headers=header | ||||
|     ) | ||||
|     assert response.text == "POST_OK" | ||||
|  | ||||
|     _, response = app.test_client.delete("/api/bp2/route/bp2", headers=header) | ||||
|     assert response.text == "DELETE_bp2" | ||||
|  | ||||
|     _, response = app.test_client.patch("/api/bp2/route/bp2", headers=header) | ||||
|     assert response.text == "PATCH_bp2" | ||||
|  | ||||
|     _, response = app.test_client.get("/v2/api/bp1/request_path") | ||||
|     assert response.status == 401 | ||||
|  | ||||
|  | ||||
| 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 | ||||
|  | ||||
|  | ||||
| def test_bp_group_list_operations(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_2", url_prefix="/bp3") | ||||
|  | ||||
|     @blueprint_3.route("/second") | ||||
|     def blueprint_3_second_route(request): | ||||
|         return text("BP3_OK") | ||||
|  | ||||
|     assert len(blueprint_group_1) == 2 | ||||
|  | ||||
|     blueprint_group_1.append(blueprint_3) | ||||
|     assert len(blueprint_group_1) == 3 | ||||
|  | ||||
|     del blueprint_group_1[2] | ||||
|     assert len(blueprint_group_1) == 2 | ||||
|  | ||||
|     blueprint_group_1[1] = blueprint_3 | ||||
|     assert len(blueprint_group_1) == 2 | ||||
|  | ||||
|     assert blueprint_group_1.url_prefix == "/bp" | ||||
		Reference in New Issue
	
	Block a user