Skip to content

Commit df48d1d

Browse files
authored
Saimon/support webdav listdir exists walk (#299)
1 parent 7f6abc8 commit df48d1d

31 files changed

Lines changed: 550 additions & 181 deletions

File tree

cterasdk/asynchronous/core/files/browser.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ async def listdir(self, path, depth=None, include_deleted=False):
7070
"""
7171
return await io.listdir(self._core, self.normalize(path), depth=depth, include_deleted=include_deleted)
7272

73+
async def exists(self, path):
74+
"""
75+
Check if item exists
76+
77+
:param str path: Path
78+
"""
79+
return await io.exists(self._core, self.normalize(path))
80+
7381
async def versions(self, path):
7482
"""
7583
List snapshots of a file or directory

cterasdk/asynchronous/core/files/io.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,18 @@ async def listdir(core, path, depth=None, include_deleted=False, search_criteria
1616
return await core.v1.api.execute('', 'fetchResources', param)
1717

1818

19-
async def root(core, path):
19+
async def exists(core, path):
20+
try:
21+
await metadata(core, path)
22+
return True
23+
except exceptions.ResourceNotFoundError:
24+
return False
25+
26+
27+
async def metadata(core, path):
2028
response = await listdir(core, path, 0)
2129
if response.root is None:
22-
raise exceptions.RemoteStorageException(path.absolute)
30+
raise exceptions.ResourceNotFoundError(path.absolute)
2331
return response.root
2432

2533

@@ -81,7 +89,7 @@ async def move(core, *paths, destination=None):
8189

8290

8391
async def retrieve_remote_dir(core, directory):
84-
resource = await root(core, directory)
92+
resource = await metadata(core, directory)
8593
if not resource.isFolder:
8694
raise exceptions.RemoteStorageException('The destination path is not a directory', None, path=directory.absolute)
8795
return str(resource.cloudFolderInfo.uid)

cterasdk/asynchronous/core/notifications.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from .base_command import BaseCommand
77
from ...common import Object
88
from ...lib import CursorResponse
9-
from ...exceptions import ClientResponseException, NotificationsError
9+
from ...exceptions import HTTPError, NotificationsError
1010

1111

1212
class Notifications(BaseCommand):
@@ -81,7 +81,7 @@ async def ancestors(self, descendant):
8181
logging.getLogger('cterasdk.metadata.connector').debug('Getting ancestors. %s', {'guid': param.guid, 'folder_id': param.folder_id})
8282
try:
8383
return await self._core.v2.api.post('/metadata/ancestors', param)
84-
except ClientResponseException:
84+
except HTTPError:
8585
logging.getLogger('cterasdk.metadata.connector').error('Could not retrieve ancestors. %s',
8686
{'folder_id': param.folder_id, 'guid': param.guid})
8787
raise

cterasdk/asynchronous/edge/files/browser.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,23 @@ async def listdir(self, path):
1313
1414
:param str path: Path
1515
"""
16-
return await io.listdir(self._edge, path)
16+
return await io.listdir(self._edge, self.normalize(path))
17+
18+
async def walk(self, path):
19+
"""
20+
Walk Directory Contents
21+
22+
:param str path: Path to walk
23+
"""
24+
return io.walk(self._edge, path)
25+
26+
async def exists(self, path):
27+
"""
28+
Check if item exists
29+
30+
:param str path: Path
31+
"""
32+
return await io.exists(self._edge, self.normalize(path))
1733

1834
async def handle(self, path):
1935
"""

cterasdk/asynchronous/edge/files/io.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,33 @@
22
from ....cio.common import encode_request_parameter
33
from ....cio import edge as fs
44
from ....cio import exceptions
5+
from ....exceptions import HTTPError
56

67

78
logger = logging.getLogger('cterasdk.edge')
89

910

1011
async def listdir(edge, path):
11-
with fs.listdir(path) as param:
12-
return await edge.api.execute('/status/fileManager', 'listPhysicalFolders', param)
12+
return fs.format_listdir_response(path.reference.as_posix(), await edge.io.propfind(path.absolute, 1))
13+
14+
15+
async def walk(edge, path):
16+
paths = [fs.EdgePath.instance('/', path)]
17+
while len(paths) > 0:
18+
path = paths.pop(0)
19+
entries = await listdir(edge, path)
20+
for e in entries:
21+
if e.is_dir:
22+
paths.append(fs.EdgePath.instance('/', e))
23+
yield e
24+
25+
26+
async def exists(edge, path):
27+
try:
28+
await edge.io.propfind(path.absolute, 0)
29+
return True
30+
except HTTPError:
31+
return False
1332

1433

1534
async def mkdir(edge, path):

cterasdk/cio/edge.py

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import logging
2+
from datetime import datetime
23
from contextlib import contextmanager
4+
from pathlib import Path
35
from ..common import Object
6+
from ..objects.uri import unquote
47
from . import common, exceptions
58

69

@@ -10,17 +13,49 @@
1013
class EdgePath(common.BasePath):
1114
"""Path for CTERA Edge Filer"""
1215

16+
def __init__(self, scope, reference):
17+
"""
18+
Initialize a CTERA Edge Filer Path.
19+
20+
:param str scope: Scope.
21+
:param str reference: Reference.
22+
"""
23+
if isinstance(reference, Object):
24+
super().__init__(scope, reference.path)
25+
elif isinstance(reference, str):
26+
super().__init__(scope, reference)
27+
else:
28+
message = 'Path validation failed: ensure the path exists and is correctly formatted.'
29+
logger.error(message)
30+
raise ValueError(message)
31+
1332
@staticmethod
1433
def instance(scope, reference):
1534
return EdgePath(scope, reference)
1635

1736

18-
@contextmanager
19-
def listdir(path):
20-
param = Object()
21-
param.path = path
22-
logger.info('Listing directory: %s', path)
23-
yield param
37+
def fetch_reference(href):
38+
namespace = 'localFiles/'
39+
return unquote(href[href.index(namespace)+len(namespace):])
40+
41+
42+
def format_listdir_response(parent, response):
43+
entries = []
44+
for e in response:
45+
path = fetch_reference(e.href)
46+
if parent != path:
47+
is_dir = e.getcontenttype == 'httpd/unix-directory'
48+
param = Object(
49+
path=path,
50+
name=Path(path).name,
51+
is_dir=is_dir,
52+
is_file=not is_dir,
53+
created_at=e.creationdate,
54+
last_modified=datetime.strptime(e.getlastmodified, "%a, %d %b %Y %H:%M:%S GMT").isoformat(),
55+
size=e.getcontentlength
56+
)
57+
entries.append(param)
58+
return entries
2459

2560

2661
@contextmanager

cterasdk/clients/base.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22
import threading
3-
from . import async_requests
3+
from . import async_requests, errors
44
from .settings import ClientSessionSettings, TraceSettings
55
from ..common import utils
66

@@ -103,11 +103,13 @@ def join_headers(self, request):
103103
def baseurl(self):
104104
return self._builder()
105105

106-
def request(self, request, *, on_response=None):
107-
return self._request(request, on_response=on_response)
106+
def request(self, request, *, on_response=None, on_error=None):
107+
on_error = on_error if on_error else errors.DefaultHandler()
108+
return self._request(request, on_response=on_response, on_error=on_error)
108109

109-
async def async_request(self, request, *, on_response=None):
110-
return await self._request(request, on_response=on_response)
110+
async def a_request(self, request, *, on_response=None, on_error=None):
111+
on_error = on_error if on_error else errors.DefaultHandler()
112+
return await self._request(request, on_response=on_response, on_error=on_error)
111113

112114
async def close(self):
113115
await self._session.close()

0 commit comments

Comments
 (0)