Beispiel #1
0
 def test_non_blueprint_rest_error_routing(self, app, mocker):
     blueprint = Blueprint('test', __name__)
     api = restx.Api(blueprint)
     api.add_resource(HelloWorld, '/hi', endpoint="hello")
     api.add_resource(GoodbyeWorld(404), '/bye', endpoint="bye")
     app.register_blueprint(blueprint, url_prefix='/blueprint')
     api2 = restx.Api(app)
     api2.add_resource(HelloWorld(api), '/hi', endpoint="hello")
     api2.add_resource(GoodbyeWorld(404), '/bye', endpoint="bye")
     with app.test_request_context('/hi', method='POST'):
         assert api._should_use_fr_error_handler() is False
         assert api2._should_use_fr_error_handler() is True
         assert api._has_fr_route() is False
         assert api2._has_fr_route() is True
     with app.test_request_context('/blueprint/hi', method='POST'):
         assert api._should_use_fr_error_handler() is True
         assert api2._should_use_fr_error_handler() is False
         assert api._has_fr_route() is True
         assert api2._has_fr_route() is False
     api._should_use_fr_error_handler = mocker.Mock(return_value=False)
     api2._should_use_fr_error_handler = mocker.Mock(return_value=False)
     with app.test_request_context('/bye'):
         assert api._has_fr_route() is False
         assert api2._has_fr_route() is True
     with app.test_request_context('/blueprint/bye'):
         assert api._has_fr_route() is True
         assert api2._has_fr_route() is False
Beispiel #2
0
 def test_registration_prefix_overrides_blueprint_prefix(self, app):
     blueprint = Blueprint('test', __name__, url_prefix='/bp')
     api = restx.Api(blueprint)
     api.add_resource(HelloWorld, '/hi', endpoint='hello')
     app.register_blueprint(blueprint, url_prefix='/reg')
     with app.test_request_context('/reg/hi'):
         assert request.endpoint == 'test.hello'
Beispiel #3
0
    def test_accept_default_override_accept(self, app, client):
        api = restx.Api(app)
        api.add_resource(Foo, '/test/')

        res = client.get('/test/', headers=[('Accept', 'text/plain')])
        assert res.status_code == 200
        assert res.content_type == 'application/json'
Beispiel #4
0
    def test_add_resource(self, mocker, mock_app):
        api = restx.Api(mock_app)
        api.output = mocker.Mock()
        api.add_resource(views.MethodView, "/foo")

        mock_app.add_url_rule.assert_called_with("/foo",
                                                 view_func=api.output())
Beispiel #5
0
    def test_accept_default_override_accept(self, app, client):
        api = restx.Api(app)
        api.add_resource(Foo, "/test/")

        res = client.get("/test/", headers=[("Accept", "text/plain")])
        assert res.status_code == 200
        assert res.content_type == "application/json"
def app(controller: layabase.CRUDController):
    application = flask.Flask(__name__)
    application.testing = True
    api = flask_restx.Api(application)
    namespace = api.namespace("Test", path="/")

    controller.flask_restx.init_models(namespace)

    @namespace.route("/test")
    class TestResource(flask_restx.Resource):
        @namespace.expect(controller.flask_restx.query_get_parser)
        @namespace.marshal_with(controller.flask_restx.get_response_model)
        def get(self):
            return []

        @namespace.expect(controller.flask_restx.json_post_model)
        def post(self):
            return []

        @namespace.expect(controller.flask_restx.json_put_model)
        def put(self):
            return []

        @namespace.expect(controller.flask_restx.query_delete_parser)
        def delete(self):
            return []

    return application
    def test_namespace_loggers_log_to_flask_app_logger(self, app, client,
                                                       caplog):
        # capture Flask app logs
        caplog.set_level(logging.INFO, logger=app.logger.name)

        api = restx.Api(app)
        ns1 = api.namespace("ns1", path="/ns1")
        ns2 = api.namespace("ns2", path="/ns2")

        @ns1.route("/")
        class Ns1(restx.Resource):
            def get(self):
                ns1.logger.info("hello from ns1")
                pass

        @ns2.route("/")
        class Ns2(restx.Resource):
            def get(self):
                ns2.logger.info("hello from ns2")
                pass

        # debug log not shown
        client.get("/ns1/")
        matching = [r for r in caplog.records if r.message == "hello from ns1"]
        assert len(matching) == 1

        # info log shown
        client.get("/ns2/")
        matching = [r for r in caplog.records if r.message == "hello from ns2"]
        assert len(matching) == 1
Beispiel #8
0
    def test_validation_on_list(self, app, client):
        '''It should perform validation on lists'''
        api = restx.Api(app, validate=True)

        person = api.model(
            'Person', {
                'name': restx.fields.String(required=True),
                'age': restx.fields.Integer(required=True),
            })

        family = api.model(
            'Family', {
                'name': restx.fields.String(required=True),
                'members': restx.fields.List(restx.fields.Nested(person))
            })

        @api.route('/validation/')
        class List(restx.Resource):
            @api.expect(family)
            def post(self):
                return {}

        self.assert_errors(client, '/validation/', {
            'name': 'Doe',
            'members': [{
                'name': 'Jonn'
            }, {
                'age': 42
            }]
        }, 'members.0.age', 'members.1.name')
Beispiel #9
0
    def test_url_variables_enabled(self, app):
        api = restx.Api(app)

        parser = api.parser()
        parser.add_argument('int', type=int)
        parser.add_argument('default', type=int, default=5)
        parser.add_argument('str', type=str)

        @api.route('/test/')
        class Test(restx.Resource):
            @api.expect(parser)
            def get(self):
                pass

        data = api.as_postman(urlvars=True)

        validate(data, schema)

        assert len(data['requests']) == 1
        request = data['requests'][0]
        qs = parse_qs(urlparse(request['url']).query, keep_blank_values=True)

        assert 'int' in qs
        assert qs['int'][0] == '0'

        assert 'default' in qs
        assert qs['default'][0] == '5'

        assert 'str' in qs
        assert qs['str'][0] == ''
Beispiel #10
0
    def test_api_payload_strict_verification(self, app, client):
        api = restx.Api(app, validate=True)
        ns = restx.Namespace("apples")
        api.add_namespace(ns)

        fields = ns.model(
            "Person",
            {
                "name": restx.fields.String(required=True),
                "age": restx.fields.Integer,
                "birthdate": restx.fields.DateTime,
            },
            strict=True,
        )

        @ns.route("/validation/")
        class Payload(restx.Resource):
            payload = None

            @ns.expect(fields)
            def post(self):
                Payload.payload = ns.payload
                return {}

        data = {
            "name": "John Doe",
            "agge": 15,  # typo
        }

        resp = client.post_json("/apples/validation/", data, status=400)
        assert re.match(
            "Additional properties are not allowed \(u*'agge' was unexpected\)",
            resp["errors"][""])
Beispiel #11
0
    def test_api_payload(self, app, client):
        api = restx.Api(app, validate=True)
        ns = restx.Namespace("apples")
        api.add_namespace(ns)

        fields = ns.model(
            "Person",
            {
                "name": restx.fields.String(required=True),
                "age": restx.fields.Integer,
                "birthdate": restx.fields.DateTime,
            },
        )

        @ns.route("/validation/")
        class Payload(restx.Resource):
            payload = None

            @ns.expect(fields)
            def post(self):
                Payload.payload = ns.payload
                return {}

        data = {
            "name": "John Doe",
            "age": 15,
        }

        client.post_json("/apples/validation/", data)

        assert Payload.payload == data
Beispiel #12
0
    def test_namespace_errorhandler_with_propagate_true(self, app, client):
        """Exceptions with errorhandler on a namespace should not be
        returned to client, even if PROPAGATE_EXCEPTIONS is set."""
        app.config["PROPAGATE_EXCEPTIONS"] = True
        api = restx.Api(app)
        namespace = restx.Namespace('test_namespace')
        api.add_namespace(namespace)

        @namespace.route("/test/", endpoint="test")
        class TestResource(restx.Resource):
            def get(self):
                raise RuntimeError("error")

        @namespace.errorhandler(RuntimeError)
        def handle_custom_exception(error):
            return {"message": str(error), "test": "value"}, 400

        response = client.get("/test_namespace/test/")
        assert response.status_code == 400
        assert response.content_type == "application/json"

        data = json.loads(response.data.decode("utf8"))
        assert data == {
            "message": "error",
            "test": "value",
        }
Beispiel #13
0
    def test_handle_smart_errors(self, app):
        api = restx.Api(app)
        view = restx.Resource

        api.add_resource(view, "/foo", endpoint="bor")
        api.add_resource(view, "/fee", endpoint="bir")
        api.add_resource(view, "/fii", endpoint="ber")

        with app.test_request_context("/faaaaa"):
            response = api.handle_error(NotFound())
            assert response.status_code == 404
            assert json.loads(response.data.decode()) == {
                "message": NotFound.description,
            }

        with app.test_request_context("/fOo"):
            response = api.handle_error(NotFound())
            assert response.status_code == 404
            assert "did you mean /foo ?" in response.data.decode()

        app.config["RESTX_ERROR_404_HELP"] = False

        response = api.handle_error(NotFound())
        assert response.status_code == 404
        assert json.loads(response.data.decode()) == {
            "message": NotFound.description
        }
Beispiel #14
0
    def test_errorhandler_lazy(self, app, client):
        api = restx.Api()

        class CustomException(RuntimeError):
            pass

        @api.route("/test/", endpoint="test")
        class TestResource(restx.Resource):
            def get(self):
                raise CustomException("error")

        @api.errorhandler(CustomException)
        def handle_custom_exception(error):
            return {"message": str(error), "test": "value"}, 400

        api.init_app(app)

        response = client.get("/test/")
        assert response.status_code == 400
        assert response.content_type == "application/json"

        data = json.loads(response.data.decode("utf8"))
        assert data == {
            "message": "error",
            "test": "value",
        }
Beispiel #15
0
 def test_url_with_api_and_blueprint_prefix(self, app):
     blueprint = Blueprint('test', __name__, url_prefix='/bp')
     api = restx.Api(blueprint, prefix='/api')
     api.add_resource(HelloWorld, '/hi', endpoint='hello')
     app.register_blueprint(blueprint)
     with app.test_request_context('/bp/api/hi'):
         assert request.endpoint == 'test.hello'
Beispiel #16
0
    def test_root_endpoint(self, app):
        api = restx.Api(app, version='1.0')

        with app.test_request_context():
            url = url_for('root')
            assert url == '/'
            assert api.base_url == 'http://localhost/'
Beispiel #17
0
    def test_validation_with_inheritance(self, app, client):
        '''It should perform validation with inheritance (allOf/$ref)'''
        api = restx.Api(app, validate=True)

        fields = api.model('Parent', {
            'name': restx.fields.String(required=True),
        })

        child_fields = api.inherit('Child', fields, {
            'age': restx.fields.Integer,
        })

        @api.route('/validation/')
        class Inheritance(restx.Resource):
            @api.expect(child_fields)
            def post(self):
                return {}

        client.post_json('/validation/', {
            'name': 'John Doe',
            'age': 15,
        })

        self.assert_errors(client, '/validation/', {
            'age': '15',
        }, 'name', 'age')
Beispiel #18
0
    def test_method_security_headers(self, app):
        api = restx.Api(app,
                        authorizations={
                            'apikey': {
                                'type': 'apiKey',
                                'in': 'header',
                                'name': 'X-API'
                            }
                        })

        @api.route('/secure/')
        class Secure(restx.Resource):
            @api.doc('secure', security='apikey')
            def get(self):
                pass

        @api.route('/unsecure/')
        class Unsecure(restx.Resource):
            @api.doc('unsecure')
            def get(self):
                pass

        data = api.as_postman()

        validate(data, schema)
        requests = dict((r['name'], r['headers']) for r in data['requests'])

        assert requests['unsecure'] == ''
        assert requests['secure'] == 'X-API:'
Beispiel #19
0
    def test_api_payload(self, app, client):
        api = restx.Api(app, validate=True)
        ns = restx.Namespace('apples')
        api.add_namespace(ns)

        fields = ns.model('Person', {
            'name': restx.fields.String(required=True),
            'age': restx.fields.Integer,
            'birthdate': restx.fields.DateTime,
        })

        @ns.route('/validation/')
        class Payload(restx.Resource):
            payload = None

            @ns.expect(fields)
            def post(self):
                Payload.payload = ns.payload
                return {}

        data = {
            'name': 'John Doe',
            'age': 15,
        }

        client.post_json('/apples/validation/', data)

        assert Payload.payload == data
Beispiel #20
0
    def test_export_with_namespace(self, app):
        api = restx.Api(app)
        ns = api.namespace('test', 'A test namespace')

        @ns.route('/test')
        class Test(restx.Resource):
            @api.doc('test_post')
            def post(self):
                '''A test post'''
                pass

        data = api.as_postman()

        validate(data, schema)

        assert len(data['requests']) == 1
        request = data['requests'][0]
        assert request['name'] == 'test_post'
        assert request['description'] == 'A test post'

        assert len(data['folders']) == 1
        folder = data['folders'][0]
        assert folder['name'] == 'test'
        assert folder['description'] == 'A test namespace'

        assert request['folder'] == folder['id']
    def test_override_app_level(self, app, client, caplog):
        caplog.set_level(logging.INFO, logger=app.logger.name)

        api = restx.Api(app)
        ns1 = api.namespace("ns1", path="/ns1")
        ns1.logger.setLevel(logging.DEBUG)
        ns2 = api.namespace("ns2", path="/ns2")

        @ns1.route("/")
        class Ns1(restx.Resource):
            def get(self):
                ns1.logger.debug("hello from ns1")
                pass

        @ns2.route("/")
        class Ns2(restx.Resource):
            def get(self):
                ns2.logger.debug("hello from ns2")
                pass

        # debug log shown from ns1
        client.get("/ns1/")
        matching = [r for r in caplog.records if r.message == "hello from ns1"]
        assert len(matching) == 1

        # debug not shown from ns2
        client.get("/ns2/")
        matching = [r for r in caplog.records if r.message == "hello from ns2"]
        assert len(matching) == 0
Beispiel #22
0
    def test_root_endpoint(self, app):
        api = restx.Api(app, version="1.0")

        with app.test_request_context():
            url = url_for("root")
            assert url == "/"
            assert api.base_url == "http://localhost/"
Beispiel #23
0
    def test_accept_default_application_json(self, app, client):
        api = restx.Api(app)
        api.add_resource(Foo, "/test/")

        res = client.get("/test/", headers={"Accept": None})
        assert res.status_code == 200
        assert res.content_type == "application/json"
Beispiel #24
0
    def test_defaults_to_app_level(self, app, client, caplog):
        caplog.set_level(logging.INFO, logger=app.logger.name)

        api = restx.Api(app)
        ns1 = api.namespace('ns1', path='/ns1')
        ns2 = api.namespace('ns2', path='/ns2')

        @ns1.route('/')
        class Ns1(restx.Resource):
            def get(self):
                ns1.logger.debug("hello from ns1")
                pass

        @ns2.route('/')
        class Ns2(restx.Resource):
            def get(self):
                ns2.logger.info("hello from ns2")
                pass

        # debug log not shown
        client.get("/ns1/")
        matching = [r for r in caplog.records if r.message == "hello from ns1"]
        assert len(matching) == 0

        # info log shown
        client.get("/ns2/")
        matching = [r for r in caplog.records if r.message == "hello from ns2"]
        assert len(matching) == 1
Beispiel #25
0
    def test_accept_no_default_no_match_not_acceptable(self, app, client):
        api = restx.Api(app, default_mediatype=None)
        api.add_resource(Foo, "/test/")

        res = client.get("/test/", headers=[("Accept", "text/plain")])
        assert res.status_code == 406
        assert res.content_type == "application/json"
Beispiel #26
0
    def test_blunder_in_errorhandler_is_not_suppressed_in_logs(
            self, app, client, caplog):

        api = restx.Api(app)

        class CustomException(RuntimeError):
            pass

        class ProgrammingBlunder(Exception):
            pass

        @api.route('/test/', endpoint="test")
        class TestResource(restx.Resource):
            def get(self):
                raise CustomException('error')

        @api.errorhandler(CustomException)
        def handle_custom_exception(error):
            raise ProgrammingBlunder(
                "This exception needs to be logged, not suppressed, then cause 500"
            )

        with caplog.at_level(logging.ERROR):
            response = client.get('/test/')
        exc_type, value, traceback = caplog.records[0].exc_info
        assert exc_type is ProgrammingBlunder
        assert response.status_code == 500
Beispiel #27
0
    def test_accept_default_application_json(self, app, client):
        api = restx.Api(app)
        api.add_resource(Foo, '/test/')

        res = client.get('/test/', headers={'Accept': None})
        assert res.status_code == 200
        assert res.content_type == 'application/json'
Beispiel #28
0
    def test_errorhandler_lazy(self, app, client):
        api = restx.Api()

        class CustomException(RuntimeError):
            pass

        @api.route('/test/', endpoint='test')
        class TestResource(restx.Resource):
            def get(self):
                raise CustomException('error')

        @api.errorhandler(CustomException)
        def handle_custom_exception(error):
            return {'message': str(error), 'test': 'value'}, 400

        api.init_app(app)

        response = client.get('/test/')
        assert response.status_code == 400
        assert response.content_type == 'application/json'

        data = json.loads(response.data.decode('utf8'))
        assert data == {
            'message': 'error',
            'test': 'value',
        }
Beispiel #29
0
    def test_accept_no_default_no_match_not_acceptable(self, app, client):
        api = restx.Api(app, default_mediatype=None)
        api.add_resource(Foo, '/test/')

        res = client.get('/test/', headers=[('Accept', 'text/plain')])
        assert res.status_code == 406
        assert res.content_type == 'application/json'
Beispiel #30
0
    def test_handle_smart_errors(self, app):
        api = restx.Api(app)
        view = restx.Resource

        api.add_resource(view, '/foo', endpoint='bor')
        api.add_resource(view, '/fee', endpoint='bir')
        api.add_resource(view, '/fii', endpoint='ber')

        with app.test_request_context("/faaaaa"):
            response = api.handle_error(NotFound())
            assert response.status_code == 404
            assert json.loads(response.data.decode()) == {
                'message': NotFound.description,
            }

        with app.test_request_context("/fOo"):
            response = api.handle_error(NotFound())
            assert response.status_code == 404
            assert 'did you mean /foo ?' in response.data.decode()

        app.config['ERROR_404_HELP'] = False

        response = api.handle_error(NotFound())
        assert response.status_code == 404
        assert json.loads(response.data.decode()) == {
            'message': NotFound.description
        }