Skip to content

Commit d2f45b1

Browse files
committed
Enable documented global decorators
- tests for global and resource level decorators
1 parent 620a96f commit d2f45b1

File tree

2 files changed

+48
-17
lines changed

2 files changed

+48
-17
lines changed

flask_combo_jsonapi/api.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ def route(self, resource, view, *urls, **kwargs):
9191
resource.view = view
9292
url_rule_options = kwargs.get('url_rule_options') or dict()
9393

94+
if hasattr(resource, 'decorators'):
95+
resource.decorators += self.decorators
96+
else:
97+
resource.decorators = self.decorators
98+
9499
view_func = resource.as_view(view)
95100

96101
if 'blueprint' in kwargs:

tests/test_sqlalchemy_data_layer.py

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
from sqlalchemy import create_engine, Column, Integer, DateTime, String, ForeignKey
55
from sqlalchemy.orm import sessionmaker, relationship
66
from sqlalchemy.ext.declarative import declarative_base
7-
from flask import Blueprint, make_response, json
7+
from flask import Blueprint, make_response, json, request
88
from marshmallow_jsonapi.flask import Schema, Relationship
99
from marshmallow import Schema as MarshmallowSchema
1010
from marshmallow_jsonapi import fields
1111
from marshmallow import ValidationError
12+
from werkzeug.exceptions import Unauthorized
1213

1314
from flask_combo_jsonapi import Api, ResourceList, ResourceDetail, ResourceRelationship, JsonApiException
1415
from flask_combo_jsonapi.pagination import add_pagination_links
@@ -231,9 +232,26 @@ def address(session, address_model):
231232

232233

233234
@pytest.fixture(scope="module")
234-
def dummy_decorator():
235+
def custom_auth_decorator():
235236
def deco(f):
236237
def wrapper_f(*args, **kwargs):
238+
auth = request.headers.get("auth", None)
239+
if auth == '123':
240+
raise Unauthorized()
241+
return f(*args, **kwargs)
242+
243+
return wrapper_f
244+
245+
yield deco
246+
247+
248+
@pytest.fixture(scope="module")
249+
def custom_auth_decorator_2():
250+
def deco(f):
251+
def wrapper_f(*args, **kwargs):
252+
auth = request.headers.get("auth", None)
253+
if auth == '1234':
254+
raise Unauthorized()
237255
return f(*args, **kwargs)
238256

239257
return wrapper_f
@@ -405,16 +423,14 @@ def before_delete_object_(self, obj, view_kwargs):
405423

406424

407425
@pytest.fixture(scope="module")
408-
def person_list(session, person_model, dummy_decorator, person_schema, before_create_object):
426+
def person_list(session, person_model, person_schema, before_create_object):
409427
class PersonList(ResourceList):
410428
schema = person_schema
411429
data_layer = {
412430
"model": person_model,
413431
"session": session,
414432
"methods": {"before_create_object": before_create_object},
415433
}
416-
get_decorators = [dummy_decorator]
417-
post_decorators = [dummy_decorator]
418434
get_schema_kwargs = dict()
419435
post_schema_kwargs = dict()
420436

@@ -459,7 +475,8 @@ class PersonList(ResourceList):
459475

460476

461477
@pytest.fixture(scope="module")
462-
def person_detail(session, person_model, dummy_decorator, person_schema, before_update_object, before_delete_object):
478+
def person_detail(session, person_model, person_schema, before_update_object, before_delete_object,
479+
custom_auth_decorator_2):
463480
class PersonDetail(ResourceDetail):
464481
schema = person_schema
465482
data_layer = {
@@ -468,25 +485,19 @@ class PersonDetail(ResourceDetail):
468485
"url_field": "person_id",
469486
"methods": {"before_update_object": before_update_object, "before_delete_object": before_delete_object},
470487
}
471-
get_decorators = [dummy_decorator]
472-
patch_decorators = [dummy_decorator]
473-
delete_decorators = [dummy_decorator]
474488
get_schema_kwargs = dict()
475489
patch_schema_kwargs = dict()
476490
delete_schema_kwargs = dict()
491+
decorators = (custom_auth_decorator_2,)
477492

478493
yield PersonDetail
479494

480495

481496
@pytest.fixture(scope="module")
482-
def person_computers(session, person_model, dummy_decorator, person_schema):
497+
def person_computers(session, person_model, person_schema):
483498
class PersonComputersRelationship(ResourceRelationship):
484499
schema = person_schema
485500
data_layer = {"session": session, "model": person_model, "url_field": "person_id"}
486-
get_decorators = [dummy_decorator]
487-
post_decorators = [dummy_decorator]
488-
patch_decorators = [dummy_decorator]
489-
delete_decorators = [dummy_decorator]
490501

491502
yield PersonComputersRelationship
492503

@@ -566,7 +577,7 @@ class ComputerList(ResourceList):
566577

567578

568579
@pytest.fixture(scope="module")
569-
def computer_detail(session, computer_model, dummy_decorator, computer_schema):
580+
def computer_detail(session, computer_model, computer_schema):
570581
class ComputerDetail(ResourceDetail):
571582
schema = computer_schema
572583
data_layer = {"model": computer_model, "session": session}
@@ -576,7 +587,7 @@ class ComputerDetail(ResourceDetail):
576587

577588

578589
@pytest.fixture(scope="module")
579-
def computer_owner(session, computer_model, dummy_decorator, computer_schema):
590+
def computer_owner(session, computer_model, computer_schema):
580591
class ComputerOwnerRelationship(ResourceRelationship):
581592
schema = computer_schema
582593
data_layer = {"session": session, "model": computer_model}
@@ -628,6 +639,7 @@ def register_routes(
628639
client,
629640
app,
630641
api_blueprint,
642+
custom_auth_decorator,
631643
person_list,
632644
person_detail,
633645
person_computers,
@@ -643,7 +655,7 @@ def register_routes(
643655
string_json_attribute_person_detail,
644656
string_json_attribute_person_list,
645657
):
646-
api = Api(blueprint=api_blueprint)
658+
api = Api(blueprint=api_blueprint, decorators=(custom_auth_decorator,))
647659
api.route(person_list, "person_list", "/persons")
648660
api.route(person_list_custom_qs_manager, "person_list_custom_qs_manager", "/persons_qs")
649661
api.route(person_detail, "person_detail", "/persons/<int:person_id>")
@@ -941,6 +953,7 @@ def test_post_list_nested(client, register_routes, computer):
941953
assert response.status_code == 201
942954
assert json.loads(response.get_data())["data"]["attributes"]["tags"][0]["key"] == "k1"
943955

956+
944957
def test_post_list_nested_field(client, register_routes):
945958
"""
946959
Test a schema contains a nested field is correctly serialized and deserialized
@@ -991,6 +1004,19 @@ def test_get_detail(client, register_routes, person):
9911004
assert response.status_code == 200
9921005

9931006

1007+
def test_get_detail_custom_auth_decorator_global(client, register_routes, person):
1008+
with client:
1009+
response = client.get("/persons/" + str(person.person_id), content_type="application/vnd.api+json",
1010+
headers={'auth': '123'})
1011+
assert response.status_code == 401
1012+
1013+
def test_get_detail_custom_auth_decorator_resource_level(client, register_routes, person):
1014+
with client:
1015+
response = client.get("/persons/" + str(person.person_id), content_type="application/vnd.api+json",
1016+
headers={'auth': '1234'})
1017+
assert response.status_code == 401
1018+
1019+
9941020
def test_patch_detail(client, register_routes, computer, person):
9951021
payload = {
9961022
"data": {

0 commit comments

Comments
 (0)