diff --git a/swh/web/ui/service.py b/swh/web/ui/service.py --- a/swh/web/ui/service.py +++ b/swh/web/ui/service.py @@ -148,6 +148,30 @@ return map(converters.from_directory_entry, directory_entries) +def lookup_directory_with_path(directory_sha1_git, path_string): + """Return directory information for entry with path path_string w.r.t. + root directory pointed by directory_sha1_git + + Args: + - directory_sha1_git: sha1_git corresponding to the directory + to which we append paths to (hopefully) find the entry + - the relative path to the entry starting from the directory pointed by + directory_sha1_git + """ + _, sha1_git_bin = query.parse_hash_with_algorithms_or_throws( + directory_sha1_git, + ['sha1'], + 'Only sha1_git is supported.') + + queried_dir = backend.directory_entry_get_by_path( + sha1_git_bin, path_string) + + if not queried_dir: + return None + + return converters.from_directory_entry(queried_dir) + + def lookup_release(release_sha1_git): """Return information about the release with sha1 release_sha1_git. diff --git a/swh/web/ui/tests/test_service.py b/swh/web/ui/tests/test_service.py --- a/swh/web/ui/tests/test_service.py +++ b/swh/web/ui/tests/test_service.py @@ -297,6 +297,37 @@ @patch('swh.web.ui.service.backend') @istest + def lookup_directory_with_path_not_found(self, mock_backend): + # given + mock_backend.lookup_directory_with_path = MagicMock(return_value=None) + + sha1_git = '65a55bbdf3629f916219feb3dcc7393ded1bc8db' + + # when + actual_directory = mock_backend.lookup_directory_with_path( + sha1_git, 'some/path/here') + + self.assertIsNone(actual_directory) + + @patch('swh.web.ui.service.backend') + @istest + def lookup_directory_with_path_found(self, mock_backend): + # given + sha1_git = '65a55bbdf3629f916219feb3dcc7393ded1bc8db' + entry = {'id': 'dir-id', + 'type': 'dir', + 'name': 'some/path/foo'} + + mock_backend.lookup_directory_with_path = MagicMock(return_value=entry) + + # when + actual_directory = mock_backend.lookup_directory_with_path( + sha1_git, 'some/path/here') + + self.assertEqual(entry, actual_directory) + + @patch('swh.web.ui.service.backend') + @istest def lookup_release(self, mock_backend): # given mock_backend.release_get = MagicMock(return_value={ diff --git a/swh/web/ui/tests/views/test_api.py b/swh/web/ui/tests/views/test_api.py --- a/swh/web/ui/tests/views/test_api.py +++ b/swh/web/ui/tests/views/test_api.py @@ -1737,6 +1737,59 @@ @patch('swh.web.ui.views.api.service') @istest + def api_directory_with_path_found(self, mock_service): + # given + expected_dir = { + 'sha1_git': '18d8be353ed3480476f032475e7c233eff7371d5', + 'type': 'file', + 'name': 'bla', + 'target': '4568be353ed3480476f032475e7c233eff737123', + 'target_url': '/api/1/content/' + 'sha1_git:4568be353ed3480476f032475e7c233eff737123/', + } + + mock_service.lookup_directory_with_path.return_value = expected_dir + + # when + rv = self.app.get('/api/1/directory/' + '18d8be353ed3480476f032475e7c233eff7371d5/bla/') + + # then + self.assertEquals(rv.status_code, 200) + self.assertEquals(rv.mimetype, 'application/json') + + response_data = json.loads(rv.data.decode('utf-8')) + self.assertEquals(response_data, expected_dir) + + mock_service.lookup_directory_with_path.assert_called_once_with( + '18d8be353ed3480476f032475e7c233eff7371d5', 'bla') + + @patch('swh.web.ui.views.api.service') + @istest + def api_directory_with_path_not_found(self, mock_service): + # given + mock_service.lookup_directory_with_path.return_value = None + path = 'some/path/to/dir/' + + # when + rv = self.app.get(('/api/1/directory/' + '66618d8be353ed3480476f032475e7c233eff737/%s') + % path) + path = path.strip('/') # Path stripped of lead/trail separators + + # then + self.assertEquals(rv.status_code, 404) + self.assertEquals(rv.mimetype, 'application/json') + + response_data = json.loads(rv.data.decode('utf-8')) + self.assertEquals(response_data, { + 'error': (('Entry with path %s relative to ' + 'directory with sha1_git ' + '66618d8be353ed3480476f032475e7c233eff737 not found.') + % path)}) + + @patch('swh.web.ui.views.api.service') + @istest def api_lookup_entity_by_uuid_not_found(self, mock_service): # when mock_service.lookup_entity_by_uuid.return_value = [] diff --git a/swh/web/ui/views/api.py b/swh/web/ui/views/api.py --- a/swh/web/ui/views/api.py +++ b/swh/web/ui/views/api.py @@ -653,11 +653,15 @@ @app.route('/api/1/directory/') @app.route('/api/1/directory//') -def api_directory(sha1_git='dcf3289b576b1c8697f2a2d46909d36104208ba3'): +@app.route('/api/1/directory///') +def api_directory(sha1_git='dcf3289b576b1c8697f2a2d46909d36104208ba3', + path=None): """Return information about release with id sha1_git. Args: - sha1_git: Directory's sha1_git. + sha1_git: Directory's sha1_git. If path exists: starting directory for + relative navigation. + path: The path to the queried directory Raises: BadInputExc in case of unknown algo_hash or bad hash. @@ -665,14 +669,25 @@ Example: GET /api/1/directory/8d7dc91d18546a91564606c3e3695a5ab568d179 + GET /api/1/directory/8d7dc91d18546a91564606c3e3695a5ab568d179/path/dir/ """ - error_msg = 'Directory with sha1_git %s not found.' % sha1_git - return _api_lookup( - sha1_git, - lookup_fn=service.lookup_directory, - error_msg_if_not_found=error_msg, - enrich_fn=utils.enrich_directory) + if path: + error_msg_path = ('Entry with path %s relative to directory ' + 'with sha1_git %s not found.') % (path, sha1_git) + return _api_lookup( + sha1_git, + service.lookup_directory_with_path, + error_msg_path, + utils.enrich_directory, + path) + else: + error_msg_nopath = 'Directory with sha1_git %s not found.' % sha1_git + return _api_lookup( + sha1_git, + service.lookup_directory, + error_msg_nopath, + utils.enrich_directory) # @app.route('/api/1/browse/')