def test_blueprint_response_schema(self, app, openapi_version, schemas): """Check response schema is correctly documented. More specifically, check that: - plural response is documented as array in the spec - schema is document in the right place w.r.t. OpenAPI version """ app.config['OPENAPI_VERSION'] = openapi_version api = Api(app) blp = Blueprint('test', 'test', url_prefix='/test') @blp.route('/schema_many_false') @blp.response(schemas.DocSchema(many=False)) def many_false(): pass @blp.route('/schema_many_true') @blp.response(schemas.DocSchema(many=True)) def many_true(): pass api.register_blueprint(blp) paths = api.spec.to_dict()['paths'] schema_ref = build_ref(api.spec, 'schema', 'Doc') response = paths['/test/schema_many_false']['get']['responses']['200'] if openapi_version == '2.0': assert response['schema'] == schema_ref else: assert (response['content']['application/json']['schema'] == schema_ref) response = paths['/test/schema_many_true']['get']['responses']['200'] if openapi_version == '2.0': assert response['schema']['items'] == schema_ref else: assert (response['content']['application/json']['schema']['items'] == schema_ref)
def test_blueprint_doc_info_from_docstring(self, app): api = Api(app) blp = Blueprint('test', __name__, url_prefix='/test') @blp.route('/') class Resource(MethodView): def get(self): """ Docstring get summary. """ def put(self): """ Docstring put summary. Docstring put description """ @blp.doc( summary='Decorator patch summary', description='Decorator patch description', ) def patch(self): """ Docstring patch summary. Docstring patch description """ api.register_blueprint(blp) spec = api.spec.to_dict() path = spec['paths']['/test/'] assert path['get']['summary'] == 'Docstring get summary.' assert 'description' not in path['get'] assert path['put']['summary'] == 'Docstring put summary.' assert path['put']['description'] == 'Docstring put description' # @doc decorator overrides docstring assert path['patch']['summary'] == 'Decorator patch summary' assert path['patch']['description'] == 'Decorator patch description'
def test_blueprint_arguments_location(self, app, schemas, location_map, openapi_version): app.config['OPENAPI_VERSION'] = openapi_version api = Api(app) blp = Blueprint('test', __name__, url_prefix='/test') location, openapi_location = location_map if location is not None: @blp.route('/') @blp.arguments(schemas.DocSchema, location=location) def func(): """Dummy view func""" else: @blp.route('/') @blp.arguments(schemas.DocSchema) def func(): """Dummy view func""" location = location or 'json' api.register_blueprint(blp) spec = api.spec.to_dict() get = spec['paths']['/test/']['get'] if (openapi_version == '3.0.2' and location in REQUEST_BODY_CONTENT_TYPE): assert 'parameters' not in get assert 'requestBody' in get assert len(get['requestBody']['content']) == 1 assert REQUEST_BODY_CONTENT_TYPE[location] in get['requestBody'][ 'content'] else: loc = get['parameters'][0]['in'] assert loc == openapi_location assert 'requestBody' not in get if location in REQUEST_BODY_CONTENT_TYPE and location != 'json': assert get['consumes'] == [REQUEST_BODY_CONTENT_TYPE[location]] else: assert 'consumes' not in get
def test_etag_verify_check_etag_exception( self, app, method, debug, testing): app.config['DEBUG'] = debug app.config['TESTING'] = testing blp = Blueprint('test', __name__) with NoLoggingContext(app): with app.test_request_context('/', method=method): if (debug or testing) and method in ['PUT', 'PATCH', 'DELETE']: with pytest.raises( CheckEtagNotCalledError, match='ETag not checked in endpoint' ): blp._verify_check_etag() else: blp._verify_check_etag()
def test_blueprint_path_parameters(self, app, openapi_version): """Check auto and manual param docs are merged""" app.config['OPENAPI_VERSION'] = openapi_version api = Api(app) blp = Blueprint('test', __name__, url_prefix='/test') @blp.route('/<int:item_id>') @blp.doc(parameters=[{ 'name': 'item_id', 'in': 'path', 'description': 'Item ID' }]) def get(item_id): pass api.register_blueprint(blp) spec = api.spec.to_dict() params = spec['paths']['/test/{item_id}']['get']['parameters'] assert len(params) == 1 if openapi_version == '2.0': assert params == [{ 'name': 'item_id', 'in': 'path', 'required': True, 'description': 'Item ID', 'format': 'int32', 'type': 'integer' }] else: assert params == [{ 'name': 'item_id', 'in': 'path', 'required': True, 'description': 'Item ID', 'schema': { 'format': 'int32', 'type': 'integer' } }]
def test_blueprint_response_examples(self, app, openapi_version): app.config['OPENAPI_VERSION'] = openapi_version api = Api(app) blp = Blueprint('test', 'test', url_prefix='/test') examples = { 'example 1': {'summary': 'Example 1', 'value': {'name': 'One'}}, 'example 2': {'summary': 'Example 2', 'value': {'name': 'Two'}}, } @blp.route('/') @blp.response(examples=examples) def func(): pass api.register_blueprint(blp) get = api.spec.to_dict()['paths']['/test/']['get'] assert ( get['responses']['200']['content']['application/json']['examples'] == examples )
def test_blueprint_response_example(self, app, openapi_version): app.config['OPENAPI_VERSION'] = openapi_version api = Api(app) blp = Blueprint('test', 'test', url_prefix='/test') example = {'name': 'One'} @blp.route('/') @blp.response(example=example) def func(): pass api.register_blueprint(blp) get = api.spec.to_dict()['paths']['/test/']['get'] if openapi_version == '2.0': assert get['responses']['200']['examples']['application/json'] == example else: assert ( get['responses']['200']['content']['application/json']['example'] == example )
def test_blueprint_doc_merged_after_prepare_doc(self, app): app.config['OPENAPI_VERSION'] = '3.0.2' api = Api(app) blp = Blueprint('test', __name__, url_prefix='/test') doc_example = { 'content': { 'application/json': { 'example': { 'test': 123 } } } } class ItemSchema(ma.Schema): test = ma.fields.Int() @blp.route('/') class Resource(MethodView): @blp.doc(**{'requestBody': doc_example}) @blp.doc(**{'responses': {'200': doc_example}}) @blp.arguments(ItemSchema) @blp.response(ItemSchema) def get(self): pass api.register_blueprint(blp) spec = api.spec.to_dict() get = spec['paths']['/test/']['get'] print(get) assert get['requestBody']['content']['application/json'][ 'example'] == { 'test': 123 } assert get['responses']['200']['content']['application/json'][ 'example'] == { 'test': 123 }
def test_api_register_field(self, app, view_type, mapping): api = Api(app) blp = Blueprint('test', 'test', url_prefix='/test') class CustomField(ma.fields.Field): pass api.register_field(CustomField, *mapping) class Document(ma.Schema): field = CustomField() if view_type == 'function': @blp.route('/') @blp.arguments(Document) def test_func(args): return jsonify(None) else: @blp.route('/') class TestMethod(MethodView): @blp.arguments(Document) def get(self, args): return jsonify(None) api.register_blueprint(blp) spec = api.spec.to_dict() if len(mapping) == 2: properties = {'field': {'type': 'custom string'}} # If mapping format is None, it does not appear in the spec if mapping[1] is not None: properties['field']['format'] = mapping[1] else: properties = {'field': {'type': 'integer', 'format': 'int32'}} assert (spec['paths']['/test/']['get']['parameters'] == [{'in': 'body', 'required': True, 'name': 'body', 'schema': {'properties': properties, 'type': 'object'}, }])
def test_api_register_converter(self, app, view_type, custom_format, name): api = Api(app) blp = Blueprint('test', 'test', url_prefix='/test') class CustomConverter(BaseConverter): pass app.url_map.converters['custom_str'] = CustomConverter api.register_converter( CustomConverter, 'custom string', custom_format, name=name) if view_type == 'function': @blp.route('/<custom_str:val>') def test_func(val): return jsonify(val) else: @blp.route('/<custom_str:val>') class TestMethod(MethodView): def get(self, val): return jsonify(val) api.register_blueprint(blp) spec = api.spec.to_dict() # If custom_format is None (default), it does not appear in the spec if custom_format is not None: parameters = [{'in': 'path', 'name': 'val', 'required': True, 'type': 'custom string', 'format': 'custom'}] else: parameters = [{'in': 'path', 'name': 'val', 'required': True, 'type': 'custom string'}] assert spec['paths']['/test/{val}']['get']['parameters'] == parameters # Converter is registered in the app iff name it not None if name is not None: assert api._app.url_map.converters[name] == CustomConverter else: assert name not in api._app.url_map.converters
def test_blueprint_doc_method_view(self, app): api = Api(app) blp = Blueprint('test', __name__, url_prefix='/test') @blp.route('/') class Resource(MethodView): @blp.doc(summary='Dummy put', description='Do dummy put') def put(self): pass @blp.doc(summary='Dummy patch', description='Do dummy patch') def patch(self): pass api.register_blueprint(blp) spec = api.spec.to_dict() path = spec['paths']['/test/'] for method in ( 'put', 'patch', ): assert path[method]['summary'] == 'Dummy {}'.format(method) assert path[method]['description'] == 'Do dummy {}'.format(method)
def test_blueprint_arguments_location( self, app, schemas, location_map, openapi_version ): app.config['OPENAPI_VERSION'] = openapi_version api = Api(app) blp = Blueprint('test', __name__, url_prefix='/test') location, openapi_location = location_map if location is not None: @blp.route('/') @blp.arguments(schemas.DocSchema, location=location) def func(): """ Dummy view func. """ else: @blp.route('/') @blp.arguments(schemas.DocSchema) def func(): """ Dummy view func. """ api.register_blueprint(blp) spec = api.spec.to_dict() get = spec['paths']['/test/']['get'] if openapi_location not in ('body', 'formData') or openapi_version == '2.0': loc = get['parameters'][0]['in'] assert loc == openapi_location assert 'requestBody' not in get else: # In OpenAPI v3, 'body' parameter is in 'requestBody' assert 'parameters' not in get assert 'requestBody' in get
def pagination_blueprint(collection, schemas, as_method_view, custom_params): """Return a basic API sample with pagination""" DocSchema = schemas.DocSchema blp = Blueprint('test', __name__, url_prefix='/test') if custom_params: page, page_size, max_page_size = CUSTOM_PAGINATION_PARAMS else: page, page_size, max_page_size = None, None, None if as_method_view: @blp.route('/') class Resource(MethodView): @blp.response(DocSchema(many=True)) @blp.paginate(page=page, page_size=page_size, max_page_size=max_page_size) def get(self, pagination_parameters): pagination_parameters.item_count = len(collection.items) return collection.items[pagination_parameters.first_item: pagination_parameters.last_item + 1] else: @blp.route('/') @blp.response(DocSchema(many=True)) @blp.paginate(page=page, page_size=page_size, max_page_size=max_page_size) def get_resources(pagination_parameters): pagination_parameters.item_count = len(collection.items) return collection.items[pagination_parameters.first_item: pagination_parameters.last_item + 1] return blp
def test_api_register_converter(self, app, view_type, custom_format, openapi_version): api = Api(app) blp = Blueprint('test', 'test', url_prefix='/test') class CustomConverter(BaseConverter): pass app.url_map.converters['custom_str'] = CustomConverter api.register_converter(CustomConverter, 'custom string', custom_format) if view_type == 'function': @blp.route('/<custom_str:val>') def test_func(val): return jsonify(val) else: @blp.route('/<custom_str:val>') class TestMethod(MethodView): def get(self, val): return jsonify(val) api.register_blueprint(blp) spec = api.spec.to_dict() schema = {'type': 'custom string'} # If custom_format is None (default), it does not appear in the spec if custom_format is not None: schema['format'] = 'custom' parameter = {'in': 'path', 'name': 'val', 'required': True} if 'openapi_version' == '2.0': parameter.update(schema) else: parameter['schema'] = schema assert spec['paths']['/test/{val}']['parameters'] == [parameter]
def test_blueprint_route_path_parameter_default(self, app, as_method_view): api = Api(app) blp = Blueprint('test', __name__, url_prefix='/test') if as_method_view: @blp.route('/<int:user_id>') @blp.route('/', defaults={'user_id': 1}) class Resource(MethodView): def get(self, user_id): pass else: @blp.route('/<int:user_id>') @blp.route('/', defaults={'user_id': 1}) def func(user_id): pass api.register_blueprint(blp) paths = api.spec.to_dict()['paths'] assert 'parameters' not in paths['/test/'] assert paths['/test/{user_id}']['parameters'][0]['name'] == 'user_id'
def test_blueprint_response_tuple(self, app): api = Api(app) blp = Blueprint('test', __name__, url_prefix='/test') client = app.test_client() @blp.route('/response') @blp.response() def func_response(): return {} @blp.route('/response_code_int') @blp.response() def func_response_code_int(): return {}, 201 @blp.route('/response_code_str') @blp.response() def func_response_code_str(): return {}, '201 CREATED' @blp.route('/response_headers') @blp.response() def func_response_headers(): return {}, {'X-header': 'test'} @blp.route('/response_code_int_headers') @blp.response() def func_response_code_int_headers(): return {}, 201, {'X-header': 'test'} @blp.route('/response_code_str_headers') @blp.response() def func_response_code_str_headers(): return {}, '201 CREATED', {'X-header': 'test'} @blp.route('/response_wrong_tuple') @blp.response() def func_response_wrong_tuple(): return {}, 201, {'X-header': 'test'}, 'extra' @blp.route('/response_tuple_subclass') @blp.response() def func_response_tuple_subclass(): class MyTuple(tuple): pass return MyTuple((1, 2)) api.register_blueprint(blp) response = client.get('/test/response') assert response.status_code == 200 assert response.json == {} response = client.get('/test/response_code_int') assert response.status_code == 201 assert response.status == '201 CREATED' assert response.json == {} response = client.get('/test/response_code_str') assert response.status_code == 201 assert response.status == '201 CREATED' assert response.json == {} response = client.get('/test/response_headers') assert response.status_code == 200 assert response.json == {} assert response.headers['X-header'] == 'test' response = client.get('/test/response_code_int_headers') assert response.status_code == 201 assert response.status == '201 CREATED' assert response.json == {} assert response.headers['X-header'] == 'test' response = client.get('/test/response_code_str_headers') assert response.status_code == 201 assert response.status == '201 CREATED' assert response.json == {} assert response.headers['X-header'] == 'test' response = client.get('/test/response_wrong_tuple') assert response.status_code == 500 response = client.get('/test/response_tuple_subclass') assert response.status_code == 200 assert response.json == [1, 2]
from flask import jsonify, current_app from flask.views import MethodView from flask_rest_api import Blueprint, abort from app import db from app.auth import get_jwt_identity, get_jwt_groups UserApi = Blueprint('UserApi', __name__, description='Endpoints for batchman user management.') @UserApi.route('/profile') class Profile(MethodView): def get(self): defined_workgroups = current_app.config["WORKGROUPS"].keys() user_groups = get_jwt_groups() intersection = list(set(defined_workgroups) & set(user_groups)) workgroups = map( lambda g: { "display_name": current_app.config["WORKGROUPS"][g].get("DISPLAY_NAME", g), "name": g, "default_work_dir": current_app.config["WORKGROUPS"][g].get( "NEXTFLOW_S3_WORK_DIR", ""), "default_profile": current_app.config["WORKGROUPS"][g].get( "NEXTFLOW_DEFAULT_PROFILE", ""), }, intersection)
def app_with_etag(request, collection, schemas, app): """Return a basic API sample with ETag""" as_method_view = request.param DocSchema = schemas.DocSchema DocEtagSchema = schemas.DocEtagSchema blp = Blueprint('test', __name__, url_prefix='/test') if as_method_view: @blp.route('/') class Resource(MethodView): @blp.etag(DocEtagSchema(many=True)) @blp.response( DocSchema(many=True)) def get(self): return collection.items @blp.etag(DocEtagSchema) @blp.arguments(DocSchema) @blp.response(DocSchema, code=201) def post(self, new_item): return collection.post(new_item) @blp.route('/<int:item_id>') class ResourceById(MethodView): def _get_item(self, item_id): try: return collection.get_by_id(item_id) except ItemNotFound: abort(404) @blp.etag(DocEtagSchema) @blp.response(DocSchema) def get(self, item_id): return self._get_item(item_id) @blp.etag(DocEtagSchema) @blp.arguments(DocSchema) @blp.response(DocSchema) def put(self, new_item, item_id): item = self._get_item(item_id) blp.check_etag(item, DocEtagSchema) return collection.put(item_id, new_item) @blp.etag(DocEtagSchema) @blp.response(code=204) def delete(self, item_id): item = self._get_item(item_id) blp.check_etag(item, DocEtagSchema) del collection.items[collection.items.index(item)] else: @blp.route('/') @blp.etag(DocEtagSchema(many=True)) @blp.response(DocSchema(many=True)) def get_resources(): return collection.items @blp.route('/', methods=('POST',)) @blp.etag(DocEtagSchema) @blp.arguments(DocSchema) @blp.response(DocSchema, code=201) def post_resource(new_item): return collection.post(new_item) def _get_item(item_id): try: return collection.get_by_id(item_id) except ItemNotFound: abort(404) @blp.route('/<int:item_id>') @blp.etag(DocEtagSchema) @blp.response(DocSchema) def get_resource(item_id): return _get_item(item_id) @blp.route('/<int:item_id>', methods=('PUT',)) @blp.etag(DocEtagSchema) @blp.arguments(DocSchema) @blp.response(DocSchema) def put_resource(new_item, item_id): item = _get_item(item_id) blp.check_etag(item) return collection.put(item_id, new_item) @blp.route('/<int:item_id>', methods=('DELETE',)) @blp.etag(DocEtagSchema) @blp.response(code=204) def delete_resource(item_id): item = _get_item(item_id) blp.check_etag(item) del collection.items[collection.items.index(item)] api = Api(app) api.register_blueprint(blp) return app
from flask_login import logout_user from phonenumbers import NumberParseException from .schemas import CheckUserIdentificationParameters from .schemas import CreateAuthClientParameters from .schemas import RegisterClientParameters from .schemas import ResetPasswordParameters from app.extensions import db from app.resources.users.models import User from app.services.tencent.id_ocr import tencent_ocr from flask_rest_api import Blueprint log = logging.getLogger(__name__) bp = Blueprint('auth', 'auth', url_prefix='/auth', description='Authentication Related API') @bp.route('/phone_exist/<phone_num>') class PhoneExist(MethodView): """ Check if phone number is registered. """ @bp.response(code=200) @bp.response(code=404, description='Validation Error') def get(self, phone_num): user = User.query.filter_by(username=phone_num).first() log.debug('checking if user <%s> exists', phone_num)
def create_app_mock(config_cls=None): """Return a basic API sample""" class AlbumSchema(ma.Schema): """Album resource schema""" class Meta: """Album schema Meta properties""" strict = True id = ma.fields.Integer() name = ma.fields.String() # Smart hyperlinking, hateoas style ! _links = ma_hateoas.Hyperlinks( schema={ 'self': ma_hateoas.UrlFor(endpoint='albums.AlbumResourceById', album_id='<id>'), 'collection': ma_hateoas.UrlFor(endpoint='albums.AlbumResources') }) _embedded = ma_hateoas.Hyperlinks( schema={ 'songs': { '_links': { 'collection': ma_hateoas.UrlFor(endpoint='songs.SongResources', album_id='<id>') } } }) blp_albums = Blueprint('albums', __name__, url_prefix='/albums') @blp_albums.route('/') class AlbumResources(MethodView): """Album resources endpoints""" @blp_albums.arguments(AlbumSchema, location='query') @blp_albums.response(AlbumSchema(many=True)) @blp_albums.paginate(Page) def get(self, args): """Return a list of resources""" album_datas = [{ 'id': 0, 'name': 'Freak Out!' }, { 'id': 1, 'name': 'Absolutely Free' }] return album_datas @blp_albums.arguments(AlbumSchema) @blp_albums.response(AlbumSchema, code=201) def post(self, new_item): """Create and return a resource""" return new_item @blp_albums.route('/<int:album_id>') class AlbumResourceById(MethodView): """Album resource endpoints""" @blp_albums.response(AlbumSchema) def get(self, album_id): """Return a resource from its ID""" album_data = {'id': album_id, 'name': 'Freak Out!'} return album_data class SongSchema(ma.Schema): """Song resource schema""" class Meta: """Song schema Meta properties""" strict = True id = ma.fields.Integer() name = ma.fields.String() album_id = ma.fields.Integer() # Smart hyperlinking, hateoas style ! _links = ma_hateoas.Hyperlinks({ 'self': ma_hateoas.UrlFor(endpoint='songs.SongResourceById', song_id='<id>'), 'collection': ma_hateoas.UrlFor(endpoint='songs.SongResources'), 'parent': ma_hateoas.UrlFor(endpoint='albums.AlbumResourceById', album_id='<album_id>') }) blp_songs = Blueprint('songs', __name__, url_prefix='/songs') @blp_songs.route('/') class SongResources(MethodView): """Song resources endpoints""" @blp_songs.arguments(SongSchema, location='query') @blp_songs.response(SongSchema(many=True)) @blp_songs.paginate(Page) def get(self, args): """Return a list of resources""" song_datas = [{ 'id': 0, 'name': 'Hungry Freaks Daddy', 'album_id': 0 }, { 'id': 1, 'name': 'I Ain\'t Got No Heart', 'album_id': 0 }] return song_datas @blp_songs.arguments(SongSchema) @blp_songs.response(SongSchema, code=201) def post(self, new_item): """Create and return a resource""" return new_item @blp_songs.route('/<int:song_id>') class SongResourceById(MethodView): """Song resource endpoints""" @blp_songs.response(SongSchema) def get(self, song_id): """Return a resource from its ID""" song_data = { 'id': song_id, 'name': 'Hungry Freaks Daddy', 'album_id': 0 } return song_data app = Flask('API Test') app.response_class = JSONResponse if config_cls: app.config.from_object(config_cls) api = Api(app) api.register_blueprint(blp_albums) api.register_blueprint(blp_songs) return app
pets = f.Nested("PetSchemaLite", many=True, exclude=("human", )) not_allowed = f.String(load_only=True) # disallowed for create class PetSchemaLite(Schema): id = f.Integer(dump_only=True) # not editable genus = f.String() species = f.String() human = f.Nested(HumanSchema, exclude=("pets", )) class PetSchema(PetSchemaLite): edible = f.Boolean() pet_blp = Blueprint("pets", "pets", url_prefix="/pet") @pet_blp.route("") class PetCollection(CollectionView): model = Pet prefetch = [Pet.human, (Pet.human, Human.cars)] # joinedload access_checks_enabled = False create_enabled = True list_enabled = True def get(self): query = super().get() # check prefetch worked
from flask_jwt_extended import ( create_access_token, create_refresh_token, get_jwt_identity, jwt_refresh_token_required, set_access_cookies, set_refresh_cookies, unset_jwt_cookies ) from flask_rest_api import Blueprint, abort from flask_security.utils import verify_password, hash_password from ..models.auth import User from ..schemas.auth import LoginSchema, RefreshSchema, TokenSchema, UserSchema blueprint = Blueprint('auth', 'auth') @blueprint.route('/login', endpoint='auth_login') class AuthLoginAPI(MethodView): @blueprint.response(LoginSchema) @blueprint.arguments(TokenSchema) def post(self, args): """Authenticates and generates a token""" email = args.get('email', None) password = args.get('password', None) if email is None: abort(403, message='Email not provided') try: user = User.get(email=email) except User.DoesNotExist:
def test_blueprint_pagination_response_tuple(self, app): api = Api(app) blp = Blueprint('test', __name__, url_prefix='/test') client = app.test_client() @blp.route('/response') @blp.response() @blp.paginate(Page) def func_response(): return [1, 2] @blp.route('/response_code') @blp.response() @blp.paginate(Page) def func_response_code(): return [1, 2], 201 @blp.route('/response_headers') @blp.response() @blp.paginate(Page) def func_response_headers(): return [1, 2], {'X-header': 'test'} @blp.route('/response_code_headers') @blp.response() @blp.paginate(Page) def func_response_code_headers(): return [1, 2], 201, {'X-header': 'test'} @blp.route('/response_wrong_tuple') @blp.response() @blp.paginate(Page) def func_response_wrong_tuple(): return [1, 2], 201, {'X-header': 'test'}, 'extra' @blp.route('/response_tuple_subclass') @blp.response() @blp.paginate(Page) def func_response_tuple_subclass(): class MyTuple(tuple): pass return MyTuple((1, 2)) api.register_blueprint(blp) response = client.get('/test/response') assert response.status_code == 200 assert response.json == [1, 2] response = client.get('/test/response_code') assert response.status_code == 201 assert response.json == [1, 2] response = client.get('/test/response_headers') assert response.status_code == 200 assert response.json == [1, 2] assert response.headers['X-header'] == 'test' response = client.get('/test/response_code_headers') assert response.status_code == 201 assert response.json == [1, 2] assert response.headers['X-header'] == 'test' response = client.get('/test/response_wrong_tuple') assert response.status_code == 500 response = client.get('/test/response_tuple_subclass') assert response.status_code == 200 assert response.json == [1, 2]
def explicit_data_no_schema_etag_blueprint(collection, schemas): """Blueprint with explicit ETag computation, no schema ETag computed without schema from arbitrary data We're using item['db_field'] for ETag data as a dummy example. """ DocSchema = schemas.DocSchema blp = Blueprint('test', __name__, url_prefix='/test') @blp.route('/') class Resource(MethodView): @blp.etag @blp.response(DocSchema(many=True)) @blp.paginate() def get(self, pagination_parameters): pagination_parameters.item_count = len(collection.items) # It is better to rely on automatic ETag here, as it includes # pagination metadata. return collection.items[ pagination_parameters.first_item: pagination_parameters.last_item + 1 ] @blp.etag @blp.arguments(DocSchema) @blp.response(DocSchema) def post(self, new_item): # Compute ETag using arbitrary data and no schema blp.set_etag(new_item['db_field']) return collection.post(new_item) @blp.route('/<int:item_id>') class ResourceById(MethodView): def _get_item(self, item_id): try: return collection.get_by_id(item_id) except ItemNotFound: abort(404) @blp.etag @blp.response(DocSchema) def get(self, item_id): item = self._get_item(item_id) # Compute ETag using arbitrary data and no schema blp.set_etag(item['db_field']) return item @blp.etag @blp.arguments(DocSchema) @blp.response(DocSchema) def put(self, new_item, item_id): item = self._get_item(item_id) # Check ETag is a manual action, no shema used blp.check_etag(item['db_field']) new_item = collection.put(item_id, new_item) # Compute ETag using arbitrary data and no schema blp.set_etag(new_item['db_field']) return new_item @blp.etag @blp.response(code=204) def delete(self, item_id): item = self._get_item(item_id) # Check ETag is a manual action, no shema used blp.check_etag(item['db_field']) collection.delete(item_id) return blp
def test_etag_check_etag(self, app, schemas, etag_disabled): app.config['ETAG_DISABLED'] = etag_disabled blp = Blueprint('test', __name__) etag_schema = schemas.DocEtagSchema old_item = {'item_id': 1, 'db_field': 0} new_item = {'item_id': 1, 'db_field': 1} old_etag = blp._generate_etag(old_item) old_etag_with_schema = blp._generate_etag(old_item, etag_schema) with app.test_request_context('/', headers={'If-Match': old_etag}): blp.check_etag(old_item) if not etag_disabled: with pytest.raises(PreconditionFailed): blp.check_etag(new_item) else: blp.check_etag(new_item) with app.test_request_context( '/', headers={'If-Match': old_etag_with_schema}): blp.check_etag(old_item, etag_schema) if not etag_disabled: with pytest.raises(PreconditionFailed): blp.check_etag(new_item, etag_schema) else: blp.check_etag(new_item)
def implicit_data_explicit_schema_etag_blueprint(collection, schemas): """Blueprint with implicit ETag computation, explicit schema ETag computed automatically with specific ETag schema """ DocSchema = schemas.DocSchema DocEtagSchema = schemas.DocEtagSchema blp = Blueprint('test', __name__, url_prefix='/test') @blp.route('/') class Resource(MethodView): @blp.etag(DocEtagSchema(many=True)) @blp.response(DocSchema(many=True)) @blp.paginate() def get(self, pagination_parameters): pagination_parameters.item_count = len(collection.items) return collection.items[ pagination_parameters.first_item: pagination_parameters.last_item + 1 ] @blp.etag(DocEtagSchema) @blp.arguments(DocSchema) @blp.response(DocSchema) def post(self, new_item): return collection.post(new_item) @blp.route('/<int:item_id>') class ResourceById(MethodView): def _get_item(self, item_id): try: return collection.get_by_id(item_id) except ItemNotFound: abort(404) @blp.etag(DocEtagSchema) @blp.response(DocSchema) def get(self, item_id): item = self._get_item(item_id) return item @blp.etag(DocEtagSchema) @blp.arguments(DocSchema) @blp.response(DocSchema) def put(self, new_item, item_id): item = self._get_item(item_id) # Check ETag is a manual action, ETag schema is used blp.check_etag(item) new_item = collection.put(item_id, new_item) return new_item @blp.etag(DocEtagSchema) @blp.response(code=204) def delete(self, item_id): item = self._get_item(item_id) # Check ETag is a manual action, ETag schema is used blp.check_etag(item) collection.delete(item_id) return blp
def test_etag_set_etag(self, app, schemas, etag_disabled): app.config['ETAG_DISABLED'] = etag_disabled blp = Blueprint('test', __name__) etag_schema = schemas.DocEtagSchema item = {'item_id': 1, 'db_field': 0} etag = blp._generate_etag(item) etag_with_schema = blp._generate_etag(item, etag_schema) with app.test_request_context('/'): blp.set_etag(item) if not etag_disabled: assert _get_etag_ctx()['etag'] == etag del _get_etag_ctx()['etag'] else: assert 'etag' not in _get_etag_ctx() with app.test_request_context( '/', headers={'If-None-Match': etag}): if not etag_disabled: with pytest.raises(NotModified): blp.set_etag(item) else: blp.set_etag(item) assert 'etag' not in _get_etag_ctx() with app.test_request_context( '/', headers={'If-None-Match': etag_with_schema}): if not etag_disabled: with pytest.raises(NotModified): blp.set_etag(item, etag_schema) else: blp.set_etag(item, etag_schema) assert 'etag' not in _get_etag_ctx() with app.test_request_context( '/', headers={'If-None-Match': 'dummy'}): if not etag_disabled: blp.set_etag(item) assert _get_etag_ctx()['etag'] == etag del _get_etag_ctx()['etag'] blp.set_etag(item, etag_schema) assert _get_etag_ctx()['etag'] == etag_with_schema del _get_etag_ctx()['etag'] else: blp.set_etag(item) assert 'etag' not in _get_etag_ctx() blp.set_etag(item, etag_schema) assert 'etag' not in _get_etag_ctx()
from flask_rest_api import Blueprint, abort from flask_security.utils import hash_password from ..models.auth import User from ..schemas.auth import UserSchema from ..schemas.paging import PageInSchema, PageOutSchema, paginate from .methodviews import ProtectedMethodView blueprint = Blueprint('user', 'user') @blueprint.route('', endpoint='users') class UserListAPI(ProtectedMethodView): @blueprint.arguments(PageInSchema(), location='headers') @blueprint.response(PageOutSchema(UserSchema)) def get(self, pagination): """List users""" return paginate(User.select(), pagination) @blueprint.arguments(UserSchema) @blueprint.response(UserSchema) def post(self, args): """Create user""" user = User(**args) user.password = hash_password(user.password) user.save() return user @blueprint.route('/<user_id>', endpoint='user') class UserAPI(ProtectedMethodView):
from http import HTTPStatus from flask import request, g from werkzeug.wrappers import Response from app.models.Pair import PairModel, PairSchema from app.shared.ipLimitIntercept import IpLimitIntercept import json from flask_rest_api import Blueprint pair_api = Blueprint('pair_api', __name__, url_prefix="/pair") pair_schema = PairSchema() @pair_api.route('/', methods=['POST']) @IpLimitIntercept.ip_limit_intercept def create(*args, **kwargs): """ Create Pair Function """ req_data = request.get_json() data = pair_schema.load(req_data) error = None if error: return custom_response(error, HTTPStatus.BAD_REQUEST) # check if user already exist in the db pair_in_db = PairModel.get_one_pair(data) if pair_in_db: message = {'error': 'ERR_PAIR_ALREADY_EXISTED'} return custom_response(message, HTTPStatus.BAD_REQUEST)
from . import utils pages = FlaskBlueprint('pages', __name__) # https://github.com/Nobatek/flask-rest-api/issues/51 class Api(_Api): def handle_http_exception(self, error): # Don't serialize redirects if isinstance(error, RoutingException): return error return super().handle_http_exception(error) api_class = Api() api = Blueprint("api", __name__, description="All API.") api_datapoint = Blueprint("DataPoints", __name__, description="CRUD datapoint(s)") api_metric = Blueprint("Metrics", __name__, description="CRUD metric(s)") # Make sure all pages show our version. render_template = partial(_render_template, version=__version__) @api_class.definition("Metrics") class MetricSchema(ModelSchema): class Meta: model = orm.Metric