diff --git a/http_prompt/context/__init__.py b/http_prompt/context/__init__.py index 790aea1..bb7c87f 100644 --- a/http_prompt/context/__init__.py +++ b/http_prompt/context/__init__.py @@ -34,16 +34,41 @@ def __init__(self, url=None, spec=None): elif path[-1] == '/': # Path ends with a trailing slash path_tokens[-1] = path_tokens[-1] + '/' self.root.add_path(*path_tokens) - endpoint = paths[path] + endpoint = dict(paths[path]) + # path parameters (apply to all paths if not overriden) + # exclude $ref as we have no system to handle that now + global_parameters = list(endpoint.pop('parameters', [])) + # not used + endpoint.pop('servers', None) + endpoint.pop('$ref', None) + endpoint.pop('summary', None) + endpoint.pop('description', None) for method, info in endpoint.items(): - params = info.get('parameters') + params = info.get('parameters', []) + params = list(global_parameters + params) if params: + parameter_key = lambda i: ( + i.get('$ref', None), + i.get('name', None), + i.get('in', None) + ) + # parameter is overriden based on $ref/in/name value + # last value (local definition) takes precedence + params_map = dict([ + (parameter_key(p), p) + for p in params + ]) + params = params_map.values() for param in params: if param.get("$ref"): for section in param.get("$ref").split('/'): param = param.get(section) if not section == "#" else spec if param.get('in') != 'path': + # Note that for completion mechanism, only + # name/node_type is used + # Parameters from methods/location + # are merged full_path = path_tokens + [param['name']] self.root.add_path(*full_path, node_type='file') diff --git a/tests/context/test_context.py b/tests/context/test_context.py index 70e0b87..7c46f43 100644 --- a/tests/context/test_context.py +++ b/tests/context/test_context.py @@ -106,3 +106,56 @@ def test_spec(): assert len(users_children) == 2 assert users_children[0].name == 'Accept' assert users_children[1].name == 'since' + + +def test_override(): + """Parameters can be defined at path level + """ + c = Context('http://localhost', spec={ + 'paths': { + '/users': { + 'parameters': [ + {'name': 'username', 'in': 'query'}, + {'name': 'Accept', 'in': 'header'} + ], + 'get': { + 'parameters': [ + {'name': 'custom1', 'in': 'query'} + ] + }, + 'post': { + 'parameters': [ + {'name': 'custom2', 'in': 'query'}, + ] + }, + }, + '/orgs': { + 'parameters': [ + {'name': 'username', 'in': 'query'}, + {'name': 'Accept', 'in': 'header'} + ], + 'get': {} + } + } + }) + assert c.url == 'http://localhost' + + root_children = list(sorted(c.root.children)) + # one path + assert len(root_children) == 2 + assert root_children[0].name == 'orgs' + assert root_children[1].name == 'users' + + orgs_methods = list(sorted(list(root_children)[0].children)) + # path parameters are used even if no method parameter + assert len(orgs_methods) == 2 + assert next(filter(lambda i:i.name == 'username', orgs_methods), None) is not None + assert next(filter(lambda i:i.name == 'Accept', orgs_methods), None) is not None + + users_methods = list(sorted(list(root_children)[1].children)) + # path and methods parameters are merged + assert len(users_methods) == 4 + assert next(filter(lambda i:i.name == 'username', users_methods), None) is not None + assert next(filter(lambda i:i.name == 'custom1', users_methods), None) is not None + assert next(filter(lambda i:i.name == 'custom2', users_methods), None) is not None + assert next(filter(lambda i:i.name == 'Accept', users_methods), None) is not None