Exemple #1
0
    def test_model_to_json(self):
        # Test model_to_json on a deep structure, with object in object
        swagger_dict = yaml.load(Tests.yaml_complex_model, Loader=yaml.FullLoader)
        spec = ApiSpec(swagger_dict)
        spec.load_models()

        Foo = get_model('Foo')
        Bar = get_model('Bar')

        f = Foo(
            token='abcd',
            bar=Bar(
                a=1,
                b=datetime(2016, 8, 26, tzinfo=timezone.utc)
            )
        )

        print("foo: " + pprint.pformat(f))

        j = spec.model_to_json(f)
        self.assertDictEqual(j, {
            'token': 'abcd',
            'bar': {
                'a': 1,
                'b': datetime(2016, 8, 26, tzinfo=timezone.utc).isoformat()
            }
        })
    def test__getitem__setitem__delitem(self):
        o = get_model('Foo')()
        self.assertEqual(o.s, None)
        self.assertEqual(o['s'], None)

        o['s'] = 'bob'
        self.assertEqual(o.s, 'bob')
        self.assertEqual(o['s'], 'bob')

        o['s'] = None
        self.assertEqual(o.s, None)
        self.assertEqual(o['s'], None)

        o['s'] = 'bob'
        self.assertEqual(o.s, 'bob')
        del o['s']
        self.assertEqual(o.s, None)

        # But local attributes may not be set this way
        with self.assertRaises(Exception) as context:
            o['local']
        self.assertTrue(
            "Model 'Foo' has no attribute local" in str(context.exception))

        with self.assertRaises(Exception) as context:
            o['local'] = 123
        self.assertTrue(
            "Model 'Foo' has no attribute local" in str(context.exception))

        with self.assertRaises(Exception) as context:
            del o['local']
        self.assertTrue(
            "Model 'Foo' has no attribute local" in str(context.exception))
Exemple #3
0
    def test_swagger_server_param_in_body(self, func):
        func.__name__ = 'return_token'

        app, spec = self.generate_server_app(self.yaml_in_body)

        SessionToken = get_model('SessionToken')
        Credentials = get_model('Credentials')
        func.return_value = SessionToken(token='456')

        with app.test_client() as c:
            r = c.get('/v1/in/body', data=json.dumps({
                'email': '[email protected]',
                'int': '123123',
            }))
            self.assertReplyOK(r, '456')
            func.assert_called_once_with(Credentials(email='[email protected]', int='123123'))
Exemple #4
0
    def test_client_with_path_body_param(self):
        handler, spec = self.generate_client_and_spec(
            self.yaml_path_body_param)

        responses.add(responses.GET,
                      "http://some.server.com:80/v1/some/123/path",
                      body=json.dumps({
                          "foo": "a",
                          "bar": "b"
                      }),
                      status=200,
                      content_type="application/json")

        # Send a valid parameter object
        model_class = get_model('Param')
        param = model_class(arg1='a', arg2='b')

        res = handler(param, foo=123)
        self.assertEqual(type(res).__name__, 'Result')
        self.assertEqual(res.foo, 'a')
        self.assertEqual(res.bar, 'b')

        # Only 1 parameter expected
        with self.assertRaises(ValidationError) as e:
            res = handler(foo=123)
        self.assertTrue('expects exactly' in str(e.exception))

        with self.assertRaises(ValidationError) as e:
            res = handler(1, 2, foo=123)
        self.assertTrue('expects exactly' in str(e.exception))

        with self.assertRaises(ValidationError) as e:
            res = handler(param)
        self.assertTrue('Missing some arguments' in str(e.exception))
Exemple #5
0
    def test_client_with_body_param(self):
        handler, spec = self.generate_client_and_spec(self.yaml_body_param)

        responses.add(responses.POST,
                      "http://some.server.com:80/v1/some/path",
                      body=json.dumps({
                          "foo": "a",
                          "bar": "b"
                      }),
                      status=200,
                      content_type="application/json")

        # Only 1 parameter expected
        with self.assertRaises(ValidationError):
            res = handler()
        with self.assertRaises(ValidationError):
            res = handler(1, 2)

        # Send a valid parameter object
        model_class = get_model('Param')
        param = model_class(arg1='a', arg2='b')

        res = handler(param)
        self.assertEqual(type(res).__name__, 'Result')
        self.assertEqual(res.foo, 'a')
        self.assertEqual(res.bar, 'b')
    def test__delattr(self):
        o = get_model('Foo')()

        o.s = 'bob'
        self.assertEqual(o.s, 'bob')
        del o.s
        self.assertTrue(hasattr(o, 's'))
        self.assertEqual(o.s, None)

        o.s = 'bob'
        self.assertEqual(o.s, 'bob')
        delattr(o, 's')
        self.assertTrue(hasattr(o, 's'))
        self.assertEqual(o.s, None)

        o.local = 'bob'
        self.assertEqual(o.local, 'bob')
        del o.local
        self.assertFalse(hasattr(o, 'local'))
        with self.assertRaises(Exception) as context:
            o.local
        self.assertTrue(
            "Model 'Foo' has no attribute local" in str(context.exception))

        o.local = 'bob'
        self.assertEqual(o.local, 'bob')
        delattr(o, 'local')
        self.assertFalse(hasattr(o, 'local'))
        with self.assertRaises(Exception) as context:
            o.local
        self.assertTrue(
            "Model 'Foo' has no attribute local" in str(context.exception))
Exemple #7
0
    def test_client_error_callback_return_dict(self):
        def callback(e):
            return {'error': str(e)}

        handler, spec = self.generate_client_and_spec(
            self.yaml_path_body_param,
            callback=callback,
        )

        responses.add(responses.GET,
                      "http://some.server.com:80/v1/some/123/path",
                      body=json.dumps({
                          "foo": "a",
                          "bar": "b"
                      }),
                      status=200,
                      content_type="application/json")

        # Send a valid parameter object
        model_class = get_model('Param')
        param = model_class(arg1='a', arg2='b')

        res = handler(param)
        print("got: %s" % res)
        self.assertDictEqual(
            res, {
                'error':
                'Missing some arguments to format url: http://some.server.com:80/v1/some/<foo>/path'
            })
    def test__eq(self):
        Foo = get_model('Foo')
        Bar = get_model('Bar')
        a = Foo(s='abc', i=12, o=Bar(s='def'))
        b = Foo(s='abc', i=12, o=Bar(s='def'))

        self.assertEqual(a, b)
        self.assertNotEqual(a, 'bob')

        # Adding local parameters does not affect eq
        a.local = 'whatever'
        self.assertEqual(a, b)

        # Changing bravado values makes them different
        a.o.s = '123'
        self.assertNotEqual(a, b)
    def test__hasattr(self):
        o = get_model('Foo')()

        self.assertTrue(hasattr(o, 's'))
        self.assertFalse(hasattr(o, 'local'))

        o.local = None
        self.assertTrue(hasattr(o, 'local'))
    def test__setattr__getattr(self):
        o = get_model('Foo')()

        # set/get a bravado attribute
        self.assertEqual(o.s, None)
        self.assertEqual(getattr(o, 's'), None)

        o.s = 'bob'
        self.assertEqual(o.s, 'bob')
        self.assertEqual(getattr(o, 's'), 'bob')

        # Make sure it's really the Bravado instance's attribute that was updated
        self.assertTrue('s' not in dir(o))
        self.assertEqual(getattr(o, '__bravado_instance').s, 'bob')

        o.s = None
        self.assertEqual(o.s, None)
        self.assertEqual(getattr(o, 's'), None)

        setattr(o, 's', 'bob')
        self.assertTrue('s' not in dir(o))
        self.assertEqual(getattr(o, '__bravado_instance').s, 'bob')
        self.assertEqual(o.s, 'bob')
        self.assertEqual(getattr(o, 's'), 'bob')

        setattr(o, 's', None)
        self.assertTrue('s' not in dir(o))
        self.assertEqual(getattr(o, '__bravado_instance').s, None)
        self.assertEqual(o.s, None)
        self.assertEqual(getattr(o, 's'), None)

        # set/get a local attribute
        with self.assertRaises(Exception) as context:
            o.local
        self.assertTrue(
            "Model 'Foo' has no attribute local" in str(context.exception))

        with self.assertRaises(Exception) as context:
            getattr(o, 'local')
        self.assertTrue(
            "Model 'Foo' has no attribute local" in str(context.exception))

        o.local = 'bob'
        self.assertTrue('local' in dir(o))
        self.assertEqual(o.local, 'bob')
        self.assertEqual(getattr(o, 'local'), 'bob')

        o.local = None
        self.assertEqual(o.local, None)
        self.assertEqual(getattr(o, 'local'), None)

        setattr(o, 'local', 'bob')
        self.assertEqual(o.local, 'bob')
        self.assertEqual(getattr(o, 'local'), 'bob')

        setattr(o, 'local', None)
        self.assertEqual(o.local, None)
        self.assertEqual(getattr(o, 'local'), None)
    def test__to_json__from_json(self):
        Foo = get_model('Foo')
        Bar = get_model('Bar')
        Baz = get_model('Baz')
        a = Foo(s='abc',
                i=12,
                lst=['a', 'b', 'c'],
                o=Bar(s='1', o=Baz(s='2')),
                lo=[
                    Baz(s='r'),
                    Baz(s='t'),
                    Baz(s='u'),
                    Baz(),
                ])
        self.assertTrue(isinstance(a, PyMacaronModel))

        j = a.to_json()
        self.assertEqual(
            j, {
                's': 'abc',
                'i': 12,
                'lst': ['a', 'b', 'c'],
                'o': {
                    'o': {
                        's': '2'
                    },
                    's': '1'
                },
                'lo': [{
                    's': 'r'
                }, {
                    's': 't'
                }, {
                    's': 'u'
                }, {}],
            })

        o = Foo.from_json(j)
        self.assertTrue(isinstance(o, PyMacaronModel))
        # TODO: o now has multiple attributes set to None, while a lacks them,
        # and bravado's __eq__ does not see None and absence as equal...
        # self.assertEqual(o, a)

        jj = o.to_json()
        self.assertEqual(jj, j)
Exemple #12
0
    def test_swagger_server_param_in_query__missing_required_param(self, func):
        func.__name__ = 'return_token'
        app, spec = self.generate_server_app(self.yaml_in_query)

        SessionToken = get_model('SessionToken')
        func.return_value = SessionToken(token='456')

        with app.test_client() as c:
            r = c.get('/v1/in/query?bar=bbbb')
            self.assertError(r, 400, 'BAD REQUEST')
            func.assert_not_called()
Exemple #13
0
    def test_swagger_server_no_param(self, func):
        func.__name__ = 'return_token'

        app, spec = self.generate_server_app(self.yaml_no_param)

        SessionToken = get_model('SessionToken')
        func.return_value = SessionToken(token='123')

        with app.test_client() as c:
            r = c.get('/v1/no/param')
            self.assertReplyOK(r, '123')
            func.assert_called_once_with()
Exemple #14
0
    def test_swagger_server_param_in_path(self, func):
        func.__name__ = 'return_token'

        app, spec = self.generate_server_app(self.yaml_in_path)

        SessionToken = get_model('SessionToken')
        func.return_value = SessionToken(token='456')

        with app.test_client() as c:
            r = c.get('/v1/in/1234/foo/bob234')
            self.assertReplyOK(r, '456')
            func.assert_called_once_with(item='1234', path='bob234')
Exemple #15
0
    def test_unmarshal_request_error__missing_required_argument(self, func):
        func.__name__ = 'return_token'

        app, spec = self.generate_server_app(self.yaml_in_body)

        SessionToken = get_model('SessionToken')
        func.return_value = SessionToken(token='456')

        with app.test_client() as c:
            r = c.get('/v1/in/body', data=json.dumps({'bazzoom': 'thiswontwork'}))
            self.assertError(r, 400, 'BAD REQUEST')
            func.assert_not_called()
Exemple #16
0
    def test_swagger_server_param_in_query(self, func):
        func.__name__ = 'return_token'

        app, spec = self.generate_server_app(self.yaml_in_query)

        SessionToken = get_model('SessionToken')
        func.return_value = SessionToken(token='456')

        with app.test_client() as c:
            r = c.get('/v1/in/query?foo=aaaa&bar=bbbb')
            self.assertReplyOK(r, '456')
            func.assert_called_once_with(bar='bbbb', foo='aaaa')
Exemple #17
0
    def test_unmarshal_request_error__wrong_argument_format(self, func):
        func.__name__ = 'return_token'

        app, spec = self.generate_server_app(self.yaml_in_body)

        SessionToken = get_model('SessionToken')
        func.return_value = SessionToken(token='456')

        with app.test_client() as c:
            data = json.dumps({
                'email': '[email protected]',
                'int': [1, 2, 3],
            })

            r = c.get('/v1/in/body', data=data)
    def test__update_from_dict(self):
        foo = get_model('Foo')()

        foo.update_from_dict({'s': 'bob'})
        self.assertEqual(
            foo.to_json(),
            {'s': 'bob'},
        )

        foo.update_from_dict({'s': 'abc', 'i': 12})
        self.assertEqual(
            foo.to_json(),
            {
                's': 'abc',
                'i': 12
            },
        )

        foo.update_from_dict({})
        self.assertEqual(
            foo.to_json(),
            {
                's': 'abc',
                'i': 12
            },
        )

        foo.update_from_dict({'i': None})
        self.assertEqual(
            foo.to_json(),
            {'s': 'abc'},
        )

        foo.update_from_dict({'s': None, 'i': 32}, ignore_none=True)
        self.assertEqual(
            foo.to_json(),
            {
                's': 'abc',
                'i': 32
            },
        )

        foo.update_from_dict({'s': None})
        self.assertEqual(
            foo.to_json(),
            {'i': 32},
        )
Exemple #19
0
    def test_requests_parameters_with_body_param(self, requests):
        handler, spec = self.generate_client_and_spec(self.yaml_body_param)
        model_class = get_model('Param')
        param = model_class(arg1='a', arg2='b')

        with self.assertRaises(PyMacaronCoreException):
            handler(param)

        requests.post.assert_called_once_with(
            'http://some.server.com:80/v1/some/path',
            data=json.dumps({
                "arg1": "a",
                "arg2": "b"
            }),
            headers={'Content-Type': 'application/json'},
            params=None,
            timeout=(10, 10),
            verify=True)
Exemple #20
0
 def callback(e):
     return get_model('SessionToken')(token=str(e))
Exemple #21
0
    def __init__(self,
                 name,
                 yaml_str=None,
                 yaml_path=None,
                 timeout=10,
                 error_callback=None,
                 formats=None,
                 do_persist=True,
                 host=None,
                 port=None,
                 local=False,
                 proto=None,
                 verify_ssl=True):
        """An API Specification"""

        self.name = name

        # Is the endpoint callable directly as a python method from within the server?
        # (true is the flask server also serves that api)
        self.local = local

        # Callback to handle exceptions
        self.error_callback = default_error_callback

        # Flag: true if this api has spawned_api
        self.is_server = False
        self.app = None

        # Object holding the client side code to call the API
        self.client = APIClient()

        # Object holding constructors for the API's objects
        self.model = APIModels()

        self.client_timeout = timeout

        # Support versions of PyYAML with and without Loader
        import pkg_resources
        v = pkg_resources.get_distribution("PyYAML").version
        yamlkwargs = {}
        if v > '3.15':
            yamlkwargs['Loader'] = yaml.FullLoader

        if yaml_path:
            log.info("Loading swagger file at %s" % yaml_path)
            swagger_dict = yaml.load(open(yaml_path), **yamlkwargs)
        elif yaml_str:
            swagger_dict = yaml.load(yaml_str, **yamlkwargs)
        else:
            raise Exception("No swagger file specified")

        self.api_spec = ApiSpec(swagger_dict, formats, host, port, proto,
                                verify_ssl)

        model_names = self.api_spec.load_models(do_persist=do_persist)

        # Add aliases to all models into self.model, so a developer may write:
        # 'ApiPool.<api_name>.model.<model_name>(*args)' to instantiate a model
        for model_name in model_names:
            setattr(self.model, model_name, get_model(model_name))

        if error_callback:
            self.error_callback = error_callback

        # Auto-generate client callers, so a developer may write:
        # 'ApiPool.<api_name>.call.login(param)' to call the login endpoint
        self._generate_client_callers()
Exemple #22
0
    def handler_wrapper(**path_params):
        if os.environ.get('PYM_DEBUG', None) == '1':
            log.debug("PYM_DEBUG: Request headers are: %s" % dict(request.headers))

        # Get caller's pym-call-id or generate one
        call_id = request.headers.get('PymCallID', None)
        if not call_id:
            call_id = str(uuid.uuid4())
        stack.top.call_id = call_id

        # Append current server to call path, or start one
        call_path = request.headers.get('PymCallPath', None)
        if call_path:
            call_path = "%s.%s" % (call_path, api_name)
        else:
            call_path = api_name
        stack.top.call_path = call_path

        if endpoint.param_in_body or endpoint.param_in_query or endpoint.param_in_formdata:
            # Turn the flask request into something bravado-core can process...
            has_data = endpoint.param_in_body or endpoint.param_in_formdata
            try:
                req = FlaskRequestProxy(request, has_data)
            except BadRequest:
                ee = error_callback(ValidationError("Cannot parse json data: have you set 'Content-Type' to 'application/json'?"))
                return _responsify(api_spec, ee, 400)

            try:
                # Note: unmarshall validates parameters but does not fail
                # if extra unknown parameters are submitted
                parameters = unmarshal_request(req, endpoint.operation)
                # Example of parameters: {'body': RegisterCredentials()}
            except jsonschema.exceptions.ValidationError as e:
                ee = error_callback(ValidationError(str(e)))
                return _responsify(api_spec, ee, 400)

        # Call the endpoint, with proper parameters depending on whether
        # parameters are in body, query or url
        args = []
        kwargs = {}

        if endpoint.param_in_path:
            kwargs = path_params

        if endpoint.param_in_body:
            # Remove the parameters already defined in path_params
            for k in list(path_params.keys()):
                del parameters[k]
            lst = list(parameters.values())
            assert len(lst) == 1

            # Now convert the Bravado body object into a pymacaron model
            body = lst[0]
            cls = get_model(body.__class__.__name__)
            body = cls.from_bravado(body)
            args.append(body)

        if endpoint.param_in_query:
            kwargs.update(parameters)

        if endpoint.param_in_formdata:
            for k in list(path_params.keys()):
                del parameters[k]
            kwargs.update(parameters)

        if os.environ.get('PYM_DEBUG', None) == '1':
            log.debug("PYM_DEBUG: Request args are: [args: %s] [kwargs: %s]" % (args, kwargs))

        result = handler_func(*args, **kwargs)

        if not result:
            e = error_callback(PyMacaronCoreException("Have nothing to send in response"))
            return _responsify(api_spec, e, 500)

        # Did we get the expected response?
        if endpoint.produces_html:
            if type(result) is not tuple:
                e = error_callback(PyMacaronCoreException("Method %s should return %s but returned %s" %
                                                          (endpoint.handler_server, endpoint.produces, type(result))))
                return _responsify(api_spec, e, 500)

            # Return an html page
            return result

        elif endpoint.produces_json:
            if not hasattr(result, '__module__') or not hasattr(result, '__class__'):
                e = error_callback(PyMacaronCoreException("Method %s did not return a class instance but a %s" %
                                                          (endpoint.handler_server, type(result))))
                return _responsify(api_spec, e, 500)

            # If it's already a flask Response, just pass it through.
            # Errors in particular may be either passed back as flask Responses, or
            # raised as exceptions to be caught and formatted by the error_callback
            result_type = result.__module__ + "." + result.__class__.__name__
            if result_type == 'flask.wrappers.Response':
                return result

            # We may have got a pymacaron Error instance, in which case
            # it has a http_reply() method...
            if hasattr(result, 'http_reply'):
                # Let's transform this Error into a flask Response
                log.info("Looks like a pymacaron error instance - calling .http_reply()")
                return result.http_reply()

            # Otherwise, assume no error occured and make a flask Response out of
            # the result.

            # TODO: check that result is an instance of a model expected as response from this endpoint
            result_json = api_spec.model_to_json(result)

            # Send a Flask Response with code 200 and result_json
            r = jsonify(result_json)
            r.status_code = 200
            return r