diff --git a/pythonanywhere/api/files_api.py b/pythonanywhere/api/files_api.py index cb888c6..987b77b 100644 --- a/pythonanywhere/api/files_api.py +++ b/pythonanywhere/api/files_api.py @@ -18,11 +18,7 @@ class Files: Covers: - GET, POST and DELETE for files path endpoint - POST, GET and DELETE for files sharing endpoint - - ********************************** - TODOS: - - GET for tree - ********************************** + - GET for tree endpoint "path" methods: - use :method: `Files.path_get` to get contents of file or directory from `path` @@ -38,6 +34,7 @@ class Files: base_url = get_api_endpoint().format(username=getpass.getuser(), flavor="files") path_endpoint = urljoin(base_url, "path") sharing_endpoint = urljoin(base_url, "sharing/") + tree_endpoint = urljoin(base_url, "tree/") def _error_msg(self, result): """TODO: error responses should be unified at the API side """ @@ -133,3 +130,13 @@ def sharing_delete(self, path): result = call_api(url, "DELETE") return result.status_code + + def tree_get(self, path): + url = f"{self.tree_endpoint}?path={path}" + + result = call_api(url, "GET") + + if result.ok: + return result.json() + + raise Exception(f"GET to {url} failed, got {result}{self._error_msg(result)}") diff --git a/tests/test_api_files.py b/tests/test_api_files.py index 5b2c959..d70d2b0 100644 --- a/tests/test_api_files.py +++ b/tests/test_api_files.py @@ -303,3 +303,66 @@ def test_returns_204_on_sucessful_unshare(self, api_token, api_responses): api_responses.add(method=responses.DELETE, url=url, status=204) assert Files().sharing_delete(valid_path) == 204 + + +@pytest.mark.files +class TestFilesTreeGet(TestFiles): + def test_returns_list_of_the_regular_files_and_subdirectories_of_a_directory( + self, api_token, api_responses + ): + url = urljoin(self.base_url, f"tree/?path={self.home_dir_path}") + self.default_home_dir_files["foo"] = { + "type": "directory", "url": f"{self.base_url}path{self.home_dir_path}/foo" + }, + tree = f'["{self.home_dir_path}/README.txt", "{self.home_dir_path}/foo/"]' + api_responses.add( + responses.GET, + url=url, + status=200, + body=bytes(tree, "utf"), + headers={"Content-Type": "application/json"}, + ) + + result = Files().tree_get(self.home_dir_path) + + assert result == [f"{self.home_dir_path}/{file}" for file in ["README.txt", "foo/"]] + + def test_raises_when_path_not_pointing_to_directory(self, api_token, api_responses): + invalid_path = "/hmoe/oof" + url = urljoin(self.base_url, f"tree/?path={invalid_path}") + api_responses.add( + responses.GET, + url=url, + status=400, + body=bytes(f'{{"detail": "{invalid_path} is not a directory"}}', "utf"), + headers={"Content-Type": "application/json"}, + ) + + with pytest.raises(Exception) as e: + Files().tree_get(invalid_path) + + expected_error_msg = ( + f"GET to {url} failed, got : {invalid_path} is not a directory" + ) + print(e.value) + assert str(e.value) == expected_error_msg + + def test_raises_when_path_does_not_exist(self, api_token, api_responses): + invalid_path = "/hmoe/oof" + url = urljoin(self.base_url, f"tree/?path={invalid_path}") + api_responses.add( + responses.GET, + url=url, + status=400, + body=bytes(f'{{"detail": "{invalid_path} does not exist"}}', "utf"), + headers={"Content-Type": "application/json"}, + ) + + with pytest.raises(Exception) as e: + Files().tree_get(invalid_path) + + expected_error_msg = ( + f"GET to {url} failed, got : {invalid_path} does not exist" + ) + print(e.value) + assert str(e.value) == expected_error_msg