示例#1
0
    def test_call_on_each_endpoint__too_many_produces(self):
        yaml_str = """
swagger: '2.0'
host: pnt-login.elasticbeanstalk.com
schemes:
  - http
  - https
paths:
  /v1/auth/login:
    post:
      parameters:
        - in: query
          name: whatever
          description: User login credentials.
          required: true
          type: string
      produces:
        - foo/bar
        - bar/baz
      x-bind-server: pnt_login.handlers.do_login
"""
        swagger_dict = yaml.load(yaml_str, Loader=yaml.FullLoader)
        spec = ApiSpec(swagger_dict)

        with self.assertRaisesRegex(Exception, "Expecting only one type"):
            spec.call_on_each_endpoint(self.foo)
示例#2
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()
            }
        })
示例#3
0
    def test_call_on_each_endpoint__invalid_produces(self):
        yaml_str = """
swagger: '2.0'
host: pnt-login.elasticbeanstalk.com
schemes:
  - http
  - https
paths:
  /v1/auth/login:
    post:
      parameters:
        - in: query
          name: whatever
          description: User login credentials.
          required: true
          type: string
      produces:
        - foo/bar
      x-bind-server: pnt_login.handlers.do_login
"""
        swagger_dict = yaml.load(yaml_str, Loader=yaml.FullLoader)
        spec = ApiSpec(swagger_dict)

        with self.assertRaisesRegex(Exception, "Only 'application/json' or 'text/html' are supported."):
            spec.call_on_each_endpoint(self.foo)
示例#4
0
 def generate_server_app(self, yaml_str, callback=default_error_callback):
     swagger_dict = yaml.load(yaml_str, Loader=yaml.FullLoader)
     spec = ApiSpec(swagger_dict)
     spec.load_models()
     app = Flask('test')
     spawn_server_api('somename', app, spec, callback, None)
     return app, spec
示例#5
0
    def generate_client_and_spec(self,
                                 yaml_str,
                                 callback=default_error_callback,
                                 local=False):

        swagger_dict = yaml.load(yaml_str, Loader=yaml.FullLoader)
        spec = ApiSpec(swagger_dict)
        spec.load_models()
        callers_dict = generate_client_callers(spec, 10, callback, local, None)

        assert len(list(callers_dict.keys())) == 1
        assert 'do_test' in callers_dict

        handler = callers_dict['do_test']
        assert type(handler).__name__ == 'function'

        return handler, spec
示例#6
0
    def test_json_to_model(self):
        swagger_dict = yaml.load(Tests.yaml_complex_model, Loader=yaml.FullLoader)
        spec = ApiSpec(swagger_dict)
        spec.load_models()

        j = {
            'token': 'abcd',
            'bar': {
                'a': 1,
                'b': date.today().isoformat()
            }
        }

        m = spec.json_to_model('Foo', j)

        self.assertEqual(m.token, 'abcd')
        b = m.bar
        self.assertEqual(b.__class__.__name__, 'Bar')
        self.assertEqual(b.a, 1)
        self.assertEqual(str(b.b), str(date.today()))
示例#7
0
    def test_apispec_constructor__scheme_https(self):
        yaml_str = """
swagger: '2.0'
info:
  title: test
  version: '1.2.3'
  description: Just a test
host: pnt-login.elasticbeanstalk.com
schemes:
  - https
basePath: /v1
produces:
  - application/json
"""
        swagger_dict = yaml.load(yaml_str, Loader=yaml.FullLoader)
        spec = ApiSpec(swagger_dict)
        self.assertEqual(spec.host, 'pnt-login.elasticbeanstalk.com')
        self.assertEqual(spec.port, 443)
        self.assertEqual(spec.protocol, 'https')
        self.assertEqual(spec.version, '1.2.3')
示例#8
0
    def test_validate(self):
        swagger_dict = yaml.load(Tests.yaml_complex_model, Loader=yaml.FullLoader)
        spec = ApiSpec(swagger_dict)
        spec.load_models()

        f = {
            'token': 'abcd',
            'bar': {
                'a': 1,
                'b': date.today().isoformat()
            }
        }

        # validate ok!
        spec.validate('Foo', f)

        # missing required property
        f = {'bar': {}}
        try:
            spec.validate('Foo', f)
        except Exception as e:
            self.assertTrue("'a' is a required property" in str(e))
        else:
            assert 0

        # invalid type
        f = {'a': 'oaeuaoue'}
        try:
            spec.validate('Bar', f)
        except Exception as e:
            self.assertTrue("'oaeuaoue' is not of type 'number'" in str(e))
        else:
            assert 0

        # invalid object reference type
        f = {'bar': '123'}
        try:
            spec.validate('Foo', f)
        except Exception as e:
            self.assertTrue("'123' is not of type 'object'" in str(e))
        else:
            assert 0
示例#9
0
    def test_call_on_each_endpoint(self):
        yaml_str = """
swagger: '2.0'
info:
  title: test
  version: '0.0.1'
  description: Just a test
host: pnt-login.elasticbeanstalk.com
schemes:
  - http
basePath: /v1
produces:
  - application/json
paths:
  /v1/auth/login:
    post:
      summary: blabla
      description: blabla
      parameters:
        - in: body
          name: whatever
          description: User login credentials.
          required: true
          schema:
            $ref: '#/definitions/Credentials'
      produces:
        - application/json
      x-bind-server: pnt_login.handlers.do_login
      x-bind-client: login
      x-auth-required: false
      responses:
        200:
          description: A session token
          schema:
            $ref: '#/definitions/SessionToken'

  /v1/auth/logout:
    get:
      summary: blabla
      description: blabla
      parameters:
        - in: query
          name: baboom
          description: foooo
          required: true
          type: string
      produces:
        - application/json
      x-bind-server: pnt_login.babar
      x-decorate-request: foo.bar.baz
      responses:
        200:
          description: A session token
          schema:
            $ref: '#/definitions/SessionToken'

  /v1/version:
    get:
      summary: blabla
      description: blabla
      produces:
        - application/json
      x-bind-server: do_version
      x-decorate-server: foo.bar.baz
      responses:
        200:
          description: A session token
          schema:
            $ref: '#/definitions/SessionToken'

  /v1/versionhtml:
    get:
      summary: blabla
      description: blabla
      produces:
        - text/html
      x-bind-server: do_version_html
      x-decorate-server: foo.bar.baz
      responses:
        200:
          description: A session token
          schema:
            $ref: '#/definitions/SessionToken'

  /v1/hybrid/{foo}:
    get:
      summary: blabla
      description: blabla
      produces:
        - application/json
      parameters:
        - in: body
          name: whatever
          description: User login credentials.
          required: true
          schema:
            $ref: '#/definitions/Credentials'
        - in: path
          name: foo
          description: foooo
          required: true
          type: string
      x-bind-server: do_version
      x-decorate-server: foo.bar.baz
      responses:
        200:
          description: A session token
          schema:
            $ref: '#/definitions/SessionToken'

  /v1/ignoreme:
    get:
      summary: blabla
      description: blabla
      produces:
        - application/json
      x-no-bind-server: true
      responses:
        200:
          description: A session token
          schema:
            $ref: '#/definitions/SessionToken'

definitions:

  SessionToken:
    type: object
    description: An authenticated user''s session token
    properties:
      token:
        type: string
        description: Session token.

  Credentials:
    type: object
    description: A user''s login credentials.
    properties:
      email:
        type: string
        description: User''s email.
      password_hash:
        type: string
        description: MD5 of user''s password, truncated to 16 first hexadecimal characters.
"""

        Tests.call_count = 0

        swagger_dict = yaml.load(yaml_str, Loader=yaml.FullLoader)
        spec = ApiSpec(swagger_dict)

        def test_callback(data):
            print("Looking at %s %s" % (data.method, data.path))

            Tests.call_count = Tests.call_count + 1

            self.assertEqual(type(data.operation).__name__, 'Operation')

            if data.path == '/v1/auth/logout/':
                self.assertEqual(data.method, 'GET')
                self.assertEqual(data.handler_server, 'pnt_login.babar')
                self.assertIsNone(data.handler_client)
                self.assertIsNone(data.decorate_server)
                self.assertEqual(data.decorate_request, 'foo.bar.baz')
                self.assertFalse(data.param_in_body)
                self.assertTrue(data.param_in_query)
                self.assertFalse(data.no_params)
                self.assertTrue(data.produces_json)
                self.assertFalse(data.produces_html)

            elif data.path == '/v1/auth/login':
                self.assertEqual(data.method, 'POST')
                self.assertEqual(data.handler_server, 'pnt_login.handlers.do_login')
                self.assertEqual(data.handler_client, 'login')
                self.assertIsNone(data.decorate_server)
                self.assertIsNone(data.decorate_request)
                self.assertTrue(data.param_in_body)
                self.assertFalse(data.param_in_query)
                self.assertFalse(data.param_in_path)
                self.assertFalse(data.no_params)
                self.assertTrue(data.produces_json)
                self.assertFalse(data.produces_html)

            elif data.path == '/v1/version':
                self.assertEqual(data.method, 'GET')
                self.assertEqual(data.handler_server, 'do_version')
                self.assertIsNone(data.handler_client)
                self.assertIsNone(data.decorate_request)
                self.assertEqual(data.decorate_server, 'foo.bar.baz')
                self.assertFalse(data.param_in_body)
                self.assertFalse(data.param_in_query)
                self.assertFalse(data.param_in_path)
                self.assertTrue(data.no_params)
                self.assertTrue(data.produces_json)
                self.assertFalse(data.produces_html)

            elif data.path == '/v1/versionhtml':
                self.assertEqual(data.method, 'GET')
                self.assertEqual(data.handler_server, 'do_version_html')
                self.assertIsNone(data.handler_client)
                self.assertIsNone(data.decorate_request)
                self.assertEqual(data.decorate_server, 'foo.bar.baz')
                self.assertFalse(data.param_in_body)
                self.assertFalse(data.param_in_query)
                self.assertFalse(data.param_in_path)
                self.assertTrue(data.no_params)
                self.assertFalse(data.produces_json)
                self.assertTrue(data.produces_html)

            elif data.path == '/v1/hybrib':
                self.assertEqual(data.method, 'GET')
                self.assertEqual(data.handler_server, 'do_version')
                self.assertIsNone(data.handler_client)
                self.assertIsNone(data.decorate_request)
                self.assertEqual(data.decorate_server, 'foo.bar.baz')
                self.assertTrue(data.param_in_body)
                self.assertFalse(data.param_in_query)
                self.assertTrue(data.param_in_path)
                self.assertTrue(data.no_params)
                self.assertTrue(data.produces_json)
                self.assertFalse(data.produces_html)

        spec.call_on_each_endpoint(test_callback)

        self.assertEqual(Tests.call_count, 5)
示例#10
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()
示例#11
0
class API():
    """Describes a REST client/server API, with sugar coating:
    - easily instantiating the objects defined in the API
    - auto-generation of client code

    usage: See apipool.py
    """
    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()

    def _generate_client_callers(self, app=None):
        # If app is defined, we are doing local calls
        if app:
            callers_dict = generate_client_callers(self.api_spec,
                                                   self.client_timeout,
                                                   self.error_callback, True,
                                                   app)
        else:
            callers_dict = generate_client_callers(self.api_spec,
                                                   self.client_timeout,
                                                   self.error_callback, False,
                                                   None)

        for method, caller in list(callers_dict.items()):
            setattr(self.client, method, caller)

    def spawn_api(self, app, decorator=None):
        """Auto-generate server endpoints implementing the API into this Flask app"""
        if decorator:
            assert type(decorator).__name__ == 'function'
        self.is_server = True
        self.app = app

        if self.local:
            # Re-generate client callers, this time as local and passing them the app
            self._generate_client_callers(app)

        return spawn_server_api(self.name, app, self.api_spec,
                                self.error_callback, decorator)

    def get_version(self):
        """Return the version of the API (as defined in the swagger file)"""
        return self.api_spec.version

    def model_to_json(self, object):
        """Take a model instance and return it as a json struct"""
        return object.to_json()

    def json_to_model(self,
                      model_name,
                      j,
                      validate=False,
                      keep_datetime=False):
        """Take a json strust and a model name, and return a model instance"""
        if validate:
            self.api_spec.validate(model_name, j)
        o = getattr(self.model, model_name)
        return o.from_json(j, keep_datetime=keep_datetime)