class API(object): def __init__(self, path_or_url, verbose=False, loop=None, stream=None): self._content = get_content(path_or_url) self._parser = SwaggerParser(swagger_dict=self._content) self.spec = self._parser.specification self.verbose = verbose self.host = self.spec['host'] if 'basePath' in self.spec: self.host += self.spec['basePath'] schemes = self.spec.get('schemes', ['https']) self.scheme = schemes[0] self.running = True self._operations = self._get_operations() self.session = LoggedClientSession(loop, stream, verbose=verbose) def close(self): self.session.close() self.running = False async def __aenter__(self): self.session.__aenter__() return self async def __aexit__(self, exc_type, exc_val, exc_tb): self.session.__aexit__(exc_type, exc_val, exc_tb) self.session.close() self.running = False def __enter__(self): self.session.__enter__() return self def __exit__(self, exc_type, exc_val, exc_tb): self.session.__exit__(exc_type, exc_val, exc_tb) self.session.close() self.running = False def __getattr__(self, name): if name == 'operations': ops = list(self._get_operations().keys()) ops.sort() return ops if name in self._operations: return partial(self._caller, name) raise AttributeError(name) async def _caller(self, operation_id, vars=None, **options): op = self._operations[operation_id] if vars is None: vars = {} data_reader = options.pop('data_reader', None) endpoint = op['endpoint'].format(**vars) verb = op['verb'] options.update(op) resp_options = options.get('response', {}) req_options = options.get('request', {}) func = getattr(self.session, verb.lower()) extra = {} extra['data'] = req_options.get('body') or req_options.get('data') extra['headers'] = req_options.get('headers') extra['params'] = req_options.get('params') async with func(endpoint, **extra) as resp: return await self._check_response(resp, resp_options, data_reader, options) async def _check_response(self, resp, resp_options, data_reader, options): check = options.get('check', True) status = resp.status # provided by the scenario (maybe should put it in responses) if check and 'status' in resp_options: wanted = int(resp_options['status']) if status != wanted: print("Bad Status code on %r" % options['endpoint']) print("Wanted %d, Got %d" % (wanted, status)) body = await resp.text() raise AssertionError(body) if check and 'headers' in resp_options: for name, expected in resp_options['headers'].items(): got = resp.headers.get(name) if got != expected: print('Bad value for header %s' % name) print('Got %r, expected %r' % (got, expected)) body = await resp.text() raise AssertionError(body) # provided by swagger elif check: if 'default' not in options['responses']: # default means the status can be anything # so we're skipping this test # Note that in the future if we do more than asserting # the status code, we will nee to iterate over the responses # options even when default is present statuses = [int(st) for st in options['responses'].keys()] if status not in statuses: print("Bad Status code on %r" % options['endpoint']) statuses = ' or '.join(['%d' % s for s in statuses]) print("Wanted %s, Got %d" % (statuses, status)) body = await resp.text() raise AssertionError(body) # extracting variables if needed vars = resp_options.get('vars') if vars and data_reader: json_data = await resp.json() for varname, data in vars.items(): default = data.get('default') query = data['query'] value = json_data.get(query, default) data_reader(varname, value) return resp def _get_operations(self): ops = {} for path, spec in self.spec['paths'].items(): endpoint = urlunparse((self.scheme, self.host, path, '', '', '')) for verb, options in spec.items(): if verb == 'parameters': continue verb = verb.upper() options['verb'] = verb.upper() options['endpoint'] = endpoint ops[options['operationId']] = options return ops