diff --git a/openapi_core/templating/paths/finders.py b/openapi_core/templating/paths/finders.py index bc90d45b..394ce190 100644 --- a/openapi_core/templating/paths/finders.py +++ b/openapi_core/templating/paths/finders.py @@ -39,8 +39,9 @@ def find(self, request): raise ServerNotFound(request.full_url_pattern) def _get_paths_iter(self, full_url_pattern): + template_paths = [] for path_pattern, path in iteritems(self.spec.paths): - # simple path + # simple path. Return right away since it is always the most concrete if full_url_pattern.endswith(path_pattern): path_result = TemplateResult(path_pattern, {}) yield (path, path_result) @@ -49,7 +50,11 @@ def _get_paths_iter(self, full_url_pattern): result = search(path_pattern, full_url_pattern) if result: path_result = TemplateResult(path_pattern, result.named) - yield (path, path_result) + template_paths.append((path, path_result)) + + # Fewer variables -> more concrete path + for path in sorted(template_paths, key=lambda p: len(p[1].variables)): + yield path def _get_operations_iter(self, request_method, paths_iter): for path, path_result in paths_iter: diff --git a/tests/unit/templating/test_paths_finders.py b/tests/unit/templating/test_paths_finders.py index 953cc1e7..3b95aa0d 100644 --- a/tests/unit/templating/test_paths_finders.py +++ b/tests/unit/templating/test_paths_finders.py @@ -435,3 +435,90 @@ def test_valid(self, finder, path_2, operation_2, server): assert result == ( path_2, operation_2, server, path_result, server_result, ) + + +class TestConcretePaths( + BaseTestSpecServer, BaseTestSimpleServer): + + path_name = '/keys/{id}/tokens' + + @pytest.fixture + def operation_2(self): + return Operation('get', '/keys/master/tokens', {}, {}) + + @pytest.fixture + def operations_2(self, operation_2): + return { + 'get': operation_2, + } + + @pytest.fixture + def path(self, operations): + return Path('/keys/{id}/tokens', operations) + + @pytest.fixture + def path_2(self, operations_2): + return Path('/keys/master/tokens', operations_2) + + @pytest.fixture + def paths(self, path, path_2): + return { + path.name: path, + path_2.name: path_2, + } + + def test_valid(self, finder, path_2, operation_2, server): + request_uri = '/keys/master/tokens' + request = MockRequest( + 'http://petstore.swagger.io', 'get', request_uri) + result = finder.find(request) + + path_result = TemplateResult(path_2.name, {}) + server_result = TemplateResult(self.server_url, {}) + assert result == ( + path_2, operation_2, server, path_result, server_result, + ) + + +class TestTemplateConcretePaths( + BaseTestSpecServer, BaseTestSimpleServer): + + path_name = '/keys/{id}/tokens/{id2}' + + @pytest.fixture + def operation_2(self): + return Operation('get', '/keys/{id}/tokens/master', {}, {}) + + @pytest.fixture + def operations_2(self, operation_2): + return { + 'get': operation_2, + } + + @pytest.fixture + def path(self, operations): + return Path('/keys/{id}/tokens/{id2}', operations) + + @pytest.fixture + def path_2(self, operations_2): + return Path('/keys/{id}/tokens/master', operations_2) + + @pytest.fixture + def paths(self, path, path_2): + return { + path.name: path, + path_2.name: path_2, + } + + def test_valid(self, finder, path_2, operation_2, server): + token_id = '123' + request_uri = '/keys/{0}/tokens/master'.format(token_id) + request = MockRequest( + 'http://petstore.swagger.io', 'get', request_uri) + result = finder.find(request) + + path_result = TemplateResult(path_2.name, {'id': '123'}) + server_result = TemplateResult(self.server_url, {}) + assert result == ( + path_2, operation_2, server, path_result, server_result, + )