Example #1
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"][""])
Example #2
0
    def test_errorhandler_with_namespace(self, app, client):
        api = restx.Api(app)

        ns = restx.Namespace("ExceptionHandler", path="/")

        class CustomException(RuntimeError):
            pass

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

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

        api.add_namespace(ns)

        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',
        }
Example #3
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",
        }
Example #4
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
Example #5
0
    def test_errorhandler_with_namespace(self, app, client):
        api = restx.Api(app)

        ns = restx.Namespace("ExceptionHandler", path="/")

        class CustomException(RuntimeError):
            pass

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

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

        api.add_namespace(ns)

        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",
        }
Example #6
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
Example #7
0
    def test_multiple_ns_with_authorizations(self, app):
        api = restx.Api()
        a1 = {'apikey': {'type': 'apiKey', 'in': 'header', 'name': 'X-API'}}
        a2 = {
            'oauth2': {
                'type': 'oauth2',
                'flow': 'accessCode',
                'tokenUrl': 'https://somewhere.com/token',
                'scopes': {
                    'read': 'Grant read-only access',
                    'write': 'Grant read-write access',
                }
            }
        }
        ns1 = restx.Namespace('ns1', authorizations=a1)
        ns2 = restx.Namespace('ns2', authorizations=a2)

        @ns1.route('/')
        class Ns1(restx.Resource):
            @ns1.doc(security='apikey')
            def get(self):
                pass

        @ns2.route('/')
        class Ns2(restx.Resource):
            @ns1.doc(security='oauth2')
            def post(self):
                pass

        api.add_namespace(ns1, path='/ns1')
        api.add_namespace(ns2, path='/ns2')
        api.init_app(app)

        assert {
            "apikey": []
        } in api.__schema__["paths"]["/ns1/"]["get"]["security"]
        assert {
            "oauth2": []
        } in api.__schema__["paths"]["/ns2/"]["post"]["security"]
        unified_auth = copy.copy(a1)
        unified_auth.update(a2)
        assert api.__schema__["securityDefinitions"] == unified_auth
Example #8
0
    def test_multiple_ns_with_authorizations(self, app):
        api = restx.Api()
        a1 = {"apikey": {"type": "apiKey", "in": "header", "name": "X-API"}}
        a2 = {
            "oauth2": {
                "type": "oauth2",
                "flow": "accessCode",
                "tokenUrl": "https://somewhere.com/token",
                "scopes": {
                    "read": "Grant read-only access",
                    "write": "Grant read-write access",
                },
            }
        }
        ns1 = restx.Namespace("ns1", authorizations=a1)
        ns2 = restx.Namespace("ns2", authorizations=a2)

        @ns1.route("/")
        class Ns1(restx.Resource):
            @ns1.doc(security="apikey")
            def get(self):
                pass

        @ns2.route("/")
        class Ns2(restx.Resource):
            @ns1.doc(security="oauth2")
            def post(self):
                pass

        api.add_namespace(ns1, path="/ns1")
        api.add_namespace(ns2, path="/ns2")
        api.init_app(app)

        assert {
            "apikey": []
        } in api.__schema__["paths"]["/ns1/"]["get"]["security"]
        assert {
            "oauth2": []
        } in api.__schema__["paths"]["/ns2/"]["post"]["security"]
        unified_auth = copy.copy(a1)
        unified_auth.update(a2)
        assert api.__schema__["securityDefinitions"] == unified_auth
Example #9
0
    def test_ns_path_prefixes(self, app):
        api = restx.Api()
        ns = restx.Namespace('test_ns', description='Test namespace')

        @ns.route('/test/', endpoint='test_resource')
        class TestResource(restx.Resource):
            pass

        api.add_namespace(ns, '/api_test')
        api.init_app(app)

        with app.test_request_context():
            assert url_for('test_resource') == '/api_test/test/'
Example #10
0
    def test_ns_path_prefixes(self, app):
        api = restx.Api()
        ns = restx.Namespace("test_ns", description="Test namespace")

        @ns.route("/test/", endpoint="test_resource")
        class TestResource(restx.Resource):
            pass

        api.add_namespace(ns, "/api_test")
        api.init_app(app)

        with app.test_request_context():
            assert url_for("test_resource") == "/api_test/test/"
Example #11
0
import flask_restx

from serializers.user_serializer import UserLoginSerializer
from core.user_service import UserService
from core.response_factory import *

login_schema = UserLoginSerializer()
auth_namespace = flask_restx.Namespace("Auth")
auth_namespace.add_model(login_schema.api_model.name, login_schema.api_model)


@auth_namespace.route('/login')
class UserLoginApi(flask_restx.Resource):
    @auth_namespace.expect(login_schema.api_model)
    @auth_namespace.response(HTTPStatus.CREATED.real,
                             "You are successfully log in")
    def post(self):
        """Log in using username and password"""
        auth_data = login_schema.loads_required(flask.request.data)
        auth = UserService.get_user_auth(**auth_data)

        if auth:
            access_token = UserService.get_token(auth_data['username'])
            return create_success_response(data=access_token)
        else:
            return create_failed_response("Invalid username or password")
Example #12
0
from http import HTTPStatus
import time

import flask
import flask_restx

from core.response_factory import create_success_response, create_success_plain_response
from serializers.measurement_serializer import MeasurementSerializer
from service.measurement_service import MeasurementService

FOUR_HOURS_IN_SECONDS = 60 * 60 * 4

measurement_schema = MeasurementSerializer()
measurement_namespace = flask_restx.Namespace("Measurement")
measurement_namespace.add_model(measurement_schema.api_model.name,
                                measurement_schema.api_model)

measurement_timestamp_parser = flask_restx.reqparse.RequestParser()
measurement_timestamp_parser.add_argument(
    'minTimestamp',
    help="Minimum Read Datetime Default is last 4 hours. ",
    type=float)
measurement_timestamp_parser.add_argument(
    'maxTimestamp', help="Maximum Read Datetime. Default is now. ", type=float)


@measurement_namespace.route('/')
class MeasurementAllApi(flask_restx.Resource):
    @measurement_namespace.expect(measurement_timestamp_parser)
    @measurement_namespace.response(HTTPStatus.OK.real, "List of measurement",
                                    [measurement_schema.api_model])
from http import HTTPStatus

import flask
import flask_restx

from core.utils import scan_with_pagination
from core.request_arguments_parser import core_request_arguments_parser
from core.response_factory import create_success_response, create_success_plain_response
from serializers.measurement_serializer import MeasurementTypeSerializer
from service.measurement_service import MeasurementTypeService

measurement_type_schema = MeasurementTypeSerializer()
measurement_type_namespace = flask_restx.Namespace("MeasurementType")
measurement_type_namespace.add_model(measurement_type_schema.api_model.name,
                                     measurement_type_schema.api_model)


@measurement_type_namespace.route('/')
class MeasurementTypeAllApi(flask_restx.Resource):
    @measurement_type_namespace.expect(core_request_arguments_parser)
    @measurement_type_namespace.response(HTTPStatus.OK.real,
                                         "List of measurement types",
                                         [measurement_type_schema.api_model])
    def get(self):
        """ Returns list of measurement types """
        measurement_types = scan_with_pagination(MeasurementTypeService)
        return create_success_response(data=measurement_type_schema.serialize(
            measurement_types, many=True))

    @measurement_type_namespace.expect(measurement_type_schema.api_model)
    @measurement_type_namespace.response(
Example #14
0
import json

import flask_restx
import boto3
import botocore.exceptions

from core.things_helper import create_thing, get_thing_certificates
from core.utils import scan_with_pagination
from core.response_factory import *
from core.request_arguments_parser import core_request_arguments_parser
from serializers.device_serializer import DeviceSerializer
from service.device_service import DeviceService
from flask_jwt_extended import jwt_required

device_schema = DeviceSerializer()
device_namespace = flask_restx.Namespace("Device")
device_namespace.add_model(device_schema.api_model.name,
                           device_schema.api_model)

device_shadow_parser = flask_restx.reqparse.RequestParser()
device_shadow_parser.add_argument('region',
                                  help="The AWS zone, default is eu-west-2",
                                  type=str)


@device_namespace.route('/')
class DeviceAllApi(flask_restx.Resource):
    @device_namespace.expect(core_request_arguments_parser)
    @device_namespace.response(HTTPStatus.OK.real, "List of devices",
                               [device_schema.api_model])
    def get(self):
from http import HTTPStatus

import flask
import flask_restx

from core.utils import scan_with_pagination
from core.request_arguments_parser import core_request_arguments_parser
from core.response_factory import create_success_response, create_success_plain_response
from serializers.device_serializer import DeviceTypeSerializer
from service.device_service import DeviceTypeService
from flask_jwt_extended import jwt_required

device_type_schema = DeviceTypeSerializer()
device_type_namespace = flask_restx.Namespace("DeviceType")
device_type_namespace.add_model(device_type_schema.api_model.name,
                                device_type_schema.api_model)


@device_type_namespace.route('/')
class DeviceTypeAllApi(flask_restx.Resource):
    @device_type_namespace.expect(core_request_arguments_parser)
    @device_type_namespace.response(HTTPStatus.OK.real, "List of device types",
                                    [device_type_schema.api_model])
    def get(self):
        """ Returns list of device types """
        device_types = scan_with_pagination(DeviceTypeService)
        return create_success_response(
            data=device_type_schema.serialize(device_types, many=True))

    @device_type_namespace.expect(device_type_schema.api_model)
    @device_type_namespace.response(HTTPStatus.CREATED.real,
Example #16
0
from http import HTTPStatus

import flask
import flask_restx

from core.utils import scan_with_pagination
from core.request_arguments_parser import core_request_arguments_parser
from core.response_factory import create_success_response, create_success_plain_response
from serializers.device_serializer import DeviceGroupSerializer
from service.device_service import DeviceGroupService
from flask_jwt_extended import jwt_required

device_group_schema = DeviceGroupSerializer()
device_group_namespace = flask_restx.Namespace("DeviceGroup")
device_group_namespace.add_model(device_group_schema.api_model.name,
                                 device_group_schema.api_model)


@device_group_namespace.route('/')
class DeviceGroupAllApi(flask_restx.Resource):
    @device_group_namespace.expect(core_request_arguments_parser)
    @device_group_namespace.response(HTTPStatus.OK.real,
                                     "List of device groups",
                                     [device_group_schema.api_model])
    def get(self):
        """ Returns list of device groups """
        device_groups = scan_with_pagination(DeviceGroupService)
        return create_success_response(
            data=device_group_schema.serialize(device_groups, many=True))

    @device_group_namespace.expect(device_group_schema.api_model)
Example #17
0
    def add_restful_api(self,
                        name: str,
                        backend: object,
                        endpoint: str = None):
        """
        Creates a flask_restplus restful api from the
        routines available in given class
        """
        if endpoint is None:
            endpoint = name.replace(" ", "_").lower()
        ns = flask_restplus.Namespace(name, inspect.getdoc(backend),
                                      '/{}'.format(endpoint))

        def isempty(o):
            "Checks if an annotation or a default value is empty"
            return o == inspect.Parameter.empty

        def build_resource(callable, post_parser, get_parser, is_post, is_get):
            "Instantiates an ApiMember in closure"

            def invoke_callable(parser, *args, **kwargs):
                for key, value in parser.parse_args().items():
                    kwargs[key] = value
                return callable(*args, **kwargs)

            if is_post:
                if is_get:

                    class ApiMember(flask_restplus.Resource):
                        @ns.expect(post_parser)
                        def post(self, *args, **kwargs):
                            return invoke_callable(post_parser, *args,
                                                   **kwargs)

                        @ns.expect(get_parser)
                        def get(self, *args, **kwargs):
                            return invoke_callable(get_parser, *args, **kwargs)
                else:

                    class ApiMember(flask_restplus.Resource):
                        @ns.expect(post_parser)
                        def post(self, *args, **kwargs):
                            return invoke_callable(post_parser, *args,
                                                   **kwargs)
            else:

                class ApiMember(flask_restplus.Resource):
                    @ns.expect(get_parser)
                    def get(self, *args, **kwargs):
                        return invoke_callable(get_parser, *args, **kwargs)

            return ApiMember

        member_resources = {}
        for name, value in inspect.getmembers(backend, inspect.ismethod):
            if name.startswith('_'):
                continue
            is_post = hasattr(value, 'is_post') and value.is_post
            is_get = hasattr(value, 'is_get') and value.is_get
            if not (is_post or is_get):
                is_post = True
            signature = inspect.signature(value)
            post_parser = ns.parser()
            get_parser = ns.parser()
            for p in signature.parameters.values():
                param_type = str if isempty(p.annotation) else p.annotation
                param_action = 'store'
                param_location = {'get': 'args', 'post': 'form'}
                param_choices = ()
                if isinstance(param_type, listof):
                    param_type = param_type.subtype
                    param_action = 'append'
                try:
                    if issubclass(param_type, file):
                        is_get = False
                        is_post = True
                        param_type = FileStorage
                        param_location = {'get': 'files', 'post': 'files'}
                except:
                    pass
                if isinstance(param_type, enum):
                    param_choices = tuple(param_type.entries.keys())
                    param_type = str
                post_parser.add_argument(
                    p.name,
                    location=param_location['post'],
                    type=param_type,
                    action=param_action,
                    choices=param_choices,
                    required=isempty(p.default),
                    default=None if isempty(p.default) else p.default)
                get_parser.add_argument(
                    p.name,
                    location=param_location['get'],
                    type=param_type,
                    action=param_action,
                    choices=param_choices,
                    required=isempty(p.default),
                    default=None if isempty(p.default) else p.default)

            resource = build_resource(value, post_parser, get_parser, is_post,
                                      is_get)
            member_resources[value] = resource
            ns.route('/' + name)(resource)

        self.add_namespace(ns)

        def backend_url_for(backend, method):
            "Provide the backend with a means to get urls for its methods"
            if not method in backend.member_resources:
                return
            return self.url_for(backend.member_resources[method])

        backend.member_resources = member_resources
        backend.url_for = MethodType(backend_url_for, backend)