Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions cterasdk/asynchronous/core/files/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,18 @@ async def public_link(self, path, access='RO', expire_in=30):
"""
return await io.public_link(self._core, self.normalize(path), access, expire_in)

async def copy(self, *paths, destination=None):
async def copy(self, *paths, destination=None, wait=False):
"""
Copy one or more files or folders

:param list[str] paths: List of paths
:param str destination: Destination
:param bool,optional wait: ``True`` Wait for task to complete, or ``False`` to return an awaitable task object.
:returns: Task status object, or an awaitable task object
:rtype: cterasdk.common.object.Object or :class:`cterasdk.lib.tasks.AwaitablePortalTask`
"""
try:
return await io.copy(self._core, *[self.normalize(path) for path in paths], destination=self.normalize(destination))
return await io.copy(self._core, *[self.normalize(path) for path in paths], destination=self.normalize(destination), wait=wait)
except ValueError:
raise ValueError('Copy destination was not specified.')

Expand Down Expand Up @@ -175,39 +178,51 @@ async def makedirs(self, path):
"""
return await io.makedirs(self._core, self.normalize(path))

async def rename(self, path, name):
async def rename(self, path, name, *, wait=False):
"""
Rename a file

:param str path: Path of the file or directory to rename
:param str name: The name to rename to
:param bool,optional wait: ``True`` Wait for task to complete, or ``False`` to return an awaitable task object.
:returns: Task status object, or an awaitable task object
:rtype: cterasdk.common.object.Object or :class:`cterasdk.lib.tasks.AwaitablePortalTask`
"""
return await io.rename(self._core, self.normalize(path), name)
return await io.rename(self._core, self.normalize(path), name, wait=wait)

async def delete(self, *paths):
async def delete(self, *paths, wait=False):
"""
Delete one or more files or folders

:param str path: Path
:param bool,optional wait: ``True`` Wait for task to complete, or ``False`` to return an awaitable task object.
:returns: Task status object, or an awaitable task object
:rtype: cterasdk.common.object.Object or :class:`cterasdk.lib.tasks.AwaitablePortalTask`
"""
return await io.remove(self._core, *[self.normalize(path) for path in paths])
return await io.remove(self._core, *[self.normalize(path) for path in paths], wait=wait)

async def undelete(self, *paths):
async def undelete(self, *paths, wait=False):
"""
Recover one or more files or folders

:param str path: Path
:param bool,optional wait: ``True`` Wait for task to complete, or ``False`` to return an awaitable task object.
:returns: Task status object, or an awaitable task object
:rtype: cterasdk.common.object.Object or :class:`cterasdk.lib.tasks.AwaitablePortalTask`
"""
return await io.recover(self._core, *[self.normalize(path) for path in paths])
return await io.recover(self._core, *[self.normalize(path) for path in paths], wait=wait)

async def move(self, *paths, destination=None):
async def move(self, *paths, destination=None, wait=False):
"""
Move one or more files or folders

:param list[str] paths: List of paths
:param str destination: Destination
:param bool,optional wait: ``True`` Wait for task to complete, or ``False`` to return an awaitable task object.
:returns: Task status object, or an awaitable task object
:rtype: cterasdk.common.object.Object or :class:`cterasdk.lib.tasks.AwaitablePortalTask`
"""
try:
return await io.move(self._core, *[self.normalize(path) for path in paths], destination=self.normalize(destination))
return await io.move(self._core, *[self.normalize(path) for path in paths], destination=self.normalize(destination), wait=wait)
except ValueError:
raise ValueError('Move destination was not specified.')
34 changes: 20 additions & 14 deletions cterasdk/asynchronous/core/files/io.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import logging
from ....cio.common import encode_request_parameter
from ....cio.common import encode_request_parameter, a_await_or_future
from ....cio import core as fs
from ....exceptions.io import ResourceNotFoundError, NotADirectory, ResourceExistsError
from .. import query
from ....lib import FetchResourcesResponse


logger = logging.getLogger('cterasdk.core')
Expand All @@ -12,7 +11,7 @@
async def listdir(core, path, depth=None, include_deleted=False, search_criteria=None, limit=None):
with fs.fetch_resources(path, depth, include_deleted, search_criteria, limit) as param:
if param.depth > 0:
return query.iterator(core, '', param, 'fetchResources', callback_response=FetchResourcesResponse)
return query.iterator(core, '', param, 'fetchResources', callback_response=fs.FetchResourcesResponse)
return await core.v1.api.execute('', 'fetchResources', param)


Expand Down Expand Up @@ -54,7 +53,7 @@ async def walk(core, scope, path, include_deleted=False):
async def mkdir(core, path):
with fs.makedir(path) as param:
response = await core.v1.api.execute('', 'makeCollection', param)
fs.accept_response(response, path.reference.as_posix())
fs.accept_response(response)


async def makedirs(core, path):
Expand All @@ -67,29 +66,34 @@ async def makedirs(core, path):
logger.debug('Resource already exists: %s', path.reference.as_posix())


async def rename(core, path, name):
async def rename(core, path, name, *, wait=False):
with fs.rename(path, name) as param:
return await core.v1.api.execute('', 'moveResources', param)
ref = await core.v1.api.execute('', 'moveResources', param)
return await a_await_or_future(core, ref, wait)


async def remove(core, *paths):
async def remove(core, *paths, wait=False):
with fs.delete(*paths) as param:
return await core.v1.api.execute('', 'deleteResources', param)
ref = await core.v1.api.execute('', 'deleteResources', param)
return await a_await_or_future(core, ref, wait)


async def recover(core, *paths):
async def recover(core, *paths, wait=False):
with fs.recover(*paths) as param:
return await core.v1.api.execute('', 'restoreResources', param)
ref = await core.v1.api.execute('', 'restoreResources', param)
return await a_await_or_future(core, ref, wait)


async def copy(core, *paths, destination=None):
async def copy(core, *paths, destination=None, wait=False):
with fs.copy(*paths, destination=destination) as param:
return await core.v1.api.execute('', 'copyResources', param)
ref = await core.v1.api.execute('', 'copyResources', param)
return await a_await_or_future(core, ref, wait)


async def move(core, *paths, destination=None):
async def move(core, *paths, destination=None, wait=False):
with fs.move(*paths, destination=destination) as param:
return await core.v1.api.execute('', 'moveResources', param)
ref = await core.v1.api.execute('', 'moveResources', param)
return await a_await_or_future(core, ref, wait)


async def ensure_directory(core, directory, suppress_error=False):
Expand Down Expand Up @@ -159,6 +163,8 @@ def upload(name, size, destination, fd):
:returns: Callable function to start the upload.
:rtype: callable
"""
fs.destination_prerequisite_conditions(destination, name)

async def wrapper(core):
"""
Upload file from metadata and file handle.
Expand Down
17 changes: 17 additions & 0 deletions cterasdk/asynchronous/core/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import logging
from ...lib.tasks import AwaitablePortalTask
from .base_command import BaseCommand


logger = logging.getLogger('cterasdk.core')


class Tasks(BaseCommand):
""" Portal Background Task APIs """

def awaitable_task(self, ref):
return AwaitablePortalTask(self._core, ref)

async def wait(self, ref, timeout=None, poll_interval=None):
awaitable_task = AwaitablePortalTask(self._core, ref)
return await awaitable_task.a_wait(timeout, poll_interval)
24 changes: 24 additions & 0 deletions cterasdk/cio/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,27 @@ def encode_request_parameter(param):
return dict(
inputXML=utf8_decode(toxmlstr(param))
)


def await_or_future(ctera, ref, wait):
"""
Wait for task completion, or return an awaitable task object.

:param str ref: Task reference
:param bool wait: ``True`` to wait for task completion, ``False`` to return an awaitable task object
"""
if wait:
return ctera.tasks.wait(ref)
return ctera.tasks.awaitable_task(ref)


async def a_await_or_future(ctera, ref, wait):
"""
Wait for task completion, or return an awaitable task object.

:param str ref: Task reference
:param bool wait: ``True`` to wait for task completion, ``False`` to return an awaitable task object
"""
if wait:
return await ctera.tasks.wait(ref)
return ctera.tasks.awaitable_task(ref)
43 changes: 31 additions & 12 deletions cterasdk/cio/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
from contextlib import contextmanager
from ..objects.uri import quote, unquote
from ..common import Object, DateTimeUtils
from ..core.enum import ProtectionLevel, CollaboratorType, SearchType, PortalAccountType, FileAccessMode
from ..core.enum import ProtectionLevel, CollaboratorType, SearchType, PortalAccountType, FileAccessMode, FileAccessError
from ..core.types import PortalAccount, UserAccount, GroupAccount
from ..exceptions.io import ResourceExistsError, PathValidationError, NameSyntaxError, ReservedNameError
from ..exceptions.io import ResourceExistsError, PathValidationError, NameSyntaxError, ReservedNameError, RestrictedRoot
from ..lib.iterator import DefaultResponse
from . import common


Expand Down Expand Up @@ -177,6 +178,17 @@ def build(self):
return self.param


class FetchResourcesResponse(DefaultResponse):

def __init__(self, response):
accept_response(response.errorType)
super().__init__(response)

@property
def objects(self):
return self._response.items


@contextmanager
def fetch_resources(path, depth, include_deleted, search_criteria, limit):
"""
Expand Down Expand Up @@ -278,6 +290,13 @@ def handle(path):
yield path.reference


def destination_prerequisite_conditions(destination, name):
if not destination.reference.root:
raise RestrictedRoot()
if any(c in name for c in ['\\', '/', ':', '?', '&', '<', '>', '"', '|']):
raise NameSyntaxError()


@contextmanager
def upload(core, name, destination, size, fd):
fd, size = common.encode_stream(fd, size)
Expand Down Expand Up @@ -489,28 +508,28 @@ def obtain_current_accounts(param):
return current_accounts


def accept_response(response, reference):
def accept_response(error_type):
"""
Check if response contains an error.
"""
error = {
"FileWithTheSameNameExist": ResourceExistsError(),
"DestinationNotExists": PathValidationError(),
"InvalidName": NameSyntaxError(),
"ReservedName": ReservedNameError()
}.get(response, None)
FileAccessError.FileWithTheSameNameExist: ResourceExistsError(),
FileAccessError.DestinationNotExists: PathValidationError(),
FileAccessError.InvalidName: NameSyntaxError(),
FileAccessError.ReservedName: ReservedNameError()
}.get(error_type, None)
try:
if error:
raise error
except ResourceExistsError as error:
logger.info('Resource already exists: a file or folder with this name already exists. %s', {'path': reference})
logger.info('Resource already exists: a file or folder with this name already exists.')
raise error
except PathValidationError as error:
logger.error('Path validation failed: the specified destination path does not exist. %s', {'path': reference})
logger.error('Path validation failed: the specified destination path does not exist.')
raise error
except NameSyntaxError as error:
logger.error('Invalid name: the name contains characters that are not allowed. %s', {'name': reference})
logger.error('Invalid name: the name contains characters that are not allowed.')
raise error
except ReservedNameError as error:
logger.error('Reserved name error: the name is reserved and cannot be used. %s', {'name': reference})
logger.error('Reserved name error: the name is reserved and cannot be used.')
raise error
25 changes: 25 additions & 0 deletions cterasdk/common/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,28 @@ class ScheduleType:
Monthly = 'monthly'
Interval = 'interval'
Window = 'window'


class TaskRunningStatus:
"""
Task Status

:ivar str Disabled: Task is disabled.
:ivar str Running: Task is currently running.
:ivar str Completed: Task completed successfully.
:ivar str Failed: Task failed.
:ivar str Retrying: Task is retrying after a failure.
:ivar str Waiting: Task is waiting to be run.
:ivar str Stopping: Task is in the process of stopping.
:ivar str Stopped: Task has been stopped.
:ivar str Warnings: Task completed with warnings.
"""
Disabled = 'disabled'
Running = 'running'
Completed = 'completed'
Failed = 'failed'
Retrying = 'retrying'
Waiting = 'waiting'
Stopping = 'stopping'
Stopped = 'stopped'
Warnings = 'completed with warnings'
Loading