def test_json_to_model(self): swagger_dict = yaml.load(Tests.yaml_complex_model) spec = ApiSpec(swagger_dict) j = { 'token': 'abcd', 'bar': { 'a': 1, 'b': date.today().isoformat() } } m = spec.json_to_model('Foo', j) Foo = spec.definitions['Foo'] Bar = spec.definitions['Bar'] 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()) + " 00:00:00")
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 """ # The API's swagger representation api_spec = None # Object holding the client side code to call the API client = APIClient() # Object holding constructors for the API's objects model = APIModels() # Default timeout when calling server endpoint, in sec client_timeout = 10 # Callback to handle exceptions error_callback = default_error_callback # Flag: true if this api has spawned_api is_server = False # The api's name name = None def __init__(self, name, yaml_str=None, yaml_path=None, timeout=None, error_callback=None, formats=None, do_persist=True, host=None, port=None): """An API Specification""" self.name = name if yaml_path: log.info("Loading swagger file at %s" % yaml_path) swagger_dict = yaml.load(open(yaml_path)) elif yaml_str: swagger_dict = yaml.load(yaml_str) else: raise Exception("No swagger file specified") self.api_spec = ApiSpec(swagger_dict, formats, host, port) if timeout: self.client_timeout = timeout if error_callback: self.error_callback = error_callback # Auto-generate class methods for every object model defined # in the swagger spec, calling that model's constructor # Ex: # klue_api.Version(version='1.2.3') => return a Version object for model_name in self.api_spec.definitions: model_generator = generate_model_instantiator(model_name, self.api_spec.definitions) # Associate model generator to ApiPool().<api_name>.model.<model_name> setattr(self.model, model_name, model_generator) # Make this bravado-core model persistent? if do_persist: spec = swagger_dict['definitions'][model_name] if 'x-persist' in spec: self._make_persistent(model_name, spec['x-persist']) # Auto-generate client callers # so we can write # api.call.login(param) => call /v1/login/ on server with param as json parameter callers_dict = generate_client_callers(self.api_spec, self.client_timeout, self.error_callback) for method, caller in callers_dict.items(): setattr(self.client, method, caller) # # WARNING: ugly piece of monkey-patching below. Hopefully will replace # with native bravado-core code in the future... # def _make_persistent(self, model_name, pkg_name): """Monkey-patch object persistence (ex: to/from database) into a bravado-core model class""" # Load class at path pkg_name c = get_function(pkg_name) for name in ('load_from_db', 'save_to_db'): if not hasattr(c, name): raise KlueException("Class %s has no static method '%s'" % (pkg_name, name)) log.info("Making %s persistent via %s" % (model_name, pkg_name)) # Replace model generator with one that adds 'save_to_db' to every instance model = getattr(self.model, model_name) n = self._wrap_bravado_model_generator(model, c.save_to_db) setattr(self.model, model_name, n) # Add class method load_from_db to model generator model = getattr(self.model, model_name) setattr(model, 'load_from_db', c.load_from_db) def _wrap_bravado_model_generator(self, model, method): def new_creator(*args, **kwargs): r = model(*args, **kwargs) r.save_to_db = types.MethodType(method, r) return r return new_creator # # End of ugly-monkey-patching # 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 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 self.api_spec.model_to_json(object) def json_to_model(self, model_name, j, validate=False): """Take a json strust and a model name, and return a model instance""" if validate: self.api_spec.validate(model_name, j) return self.api_spec.json_to_model(model_name, j)