예제 #1
0
class connector:
    def __init__(self, config):
        self.config = config
        options = {
            'webdav_hostname': config['Connector']['Host'],
            'webdav_login': config['Connector']['Username'],
            'webdav_password': config['Connector']['Password']
        }
        self.client = Client(options)
        self.client.verfiy = False

        self.base = config['Connector']['Base']

    def listdir(self):
        l = self.client.list(self.base)
        l.pop(l.index(self.base + '/'))
        print(l)
        return l

    def remove(self, name):
        self.client.clean(self.base + '/' + name)

    def check(self, name):
        return self.client.check(self.base + '/' + name)

    def get(self, name):
        return self.client.resource(self.base + '/' + name)

    def create(self, name):
        self.client.upload_to(None, self.base + '/' + name)

    def upload(self, name):
        self.client.upload_file(self.base + '/' + name, name)
class ClientTestCase(TestCase):
    remote_path_file = 'test_dir/test.txt'
    remote_path_file2 = 'test_dir2/test.txt'
    remote_path_dir = 'test_dir'
    remote_path_dir2 = 'test_dir2'
    local_base_dir = 'tests/'
    local_file = 'test.txt'
    local_file_path = local_base_dir + 'test.txt'
    local_path_dir = local_base_dir + 'res/test_dir'

    def setUp(self):
        options = {
            'webdav_hostname': 'https://webdav.yandex.ru',
            'webdav_login': '******',
            'webdav_password': '******'
        }
        self.client = Client(options)
        if path.exists(path=self.local_path_dir):
            shutil.rmtree(path=self.local_path_dir)

    def tearDown(self):
        if path.exists(path=self.local_path_dir):
            shutil.rmtree(path=self.local_path_dir)
        if self.client.check(remote_path=self.remote_path_dir):
            self.client.clean(remote_path=self.remote_path_dir)
        if self.client.check(remote_path=self.remote_path_dir2):
            self.client.clean(remote_path=self.remote_path_dir2)

    def test_list(self):
        self._prepare_for_downloading()
        file_list = self.client.list()
        self.assertIsNotNone(file_list, 'List of files should not be None')
        self.assertGreater(file_list.__len__(), 0,
                           'Expected that amount of files more then 0')

    def test_free(self):
        self.assertGreater(
            self.client.free(), 0,
            'Expected that free space on WebDAV server is more then 0 bytes')

    def test_check(self):
        self.assertTrue(self.client.check(),
                        'Expected that root directory is exist')

    def test_mkdir(self):
        if self.client.check(remote_path=self.remote_path_dir):
            self.client.clean(remote_path=self.remote_path_dir)
        self.client.mkdir(remote_path=self.remote_path_dir)
        self.assertTrue(self.client.check(remote_path=self.remote_path_dir),
                        'Expected the directory is created.')

    @unittest.skip(
        "Yandex brakes response for file it contains property resourcetype as collection but it should "
        "be empty for file")
    def test_download_to(self):
        self._prepare_for_downloading()
        buff = BytesIO()
        self.client.download_from(buff=buff, remote_path=self.remote_path_file)
        self.assertEquals(buff.getvalue(),
                          'test content for testing of webdav client')

    @unittest.skip(
        "Yandex brakes response for file it contains property resourcetype as collection but it should "
        "be empty for file")
    def test_download(self):
        self._prepare_for_downloading()
        self.client.download(local_path=self.local_path_dir,
                             remote_path=self.remote_path_dir)
        self.assertTrue(path.exists(self.local_path_dir),
                        'Expected the directory is downloaded.')
        self.assertTrue(path.isdir(self.local_path_dir),
                        'Expected this is a directory.')
        self.assertTrue(
            path.exists(self.local_path_dir + os.path.sep + self.local_file),
            'Expected the file is downloaded')
        self.assertTrue(
            path.isfile(self.local_path_dir + os.path.sep +
                        self.local_path_file), 'Expected this is a file')

    @unittest.skip(
        "Yandex brakes response for file it contains property resourcetype as collection but it should "
        "be empty for file")
    def test_download_sync(self):
        self._prepare_for_downloading()
        os.mkdir(self.local_path_dir)

        def callback():
            self.assertTrue(
                path.exists(self.local_path_dir + os.path.sep +
                            self.local_file),
                'Expected the file is downloaded')
            self.assertTrue(
                path.isfile(self.local_path_dir + os.path.sep +
                            self.local_file), 'Expected this is a file')

        self.client.download_sync(local_path=self.local_path_dir +
                                  os.path.sep + self.local_file,
                                  remote_path=self.remote_path_file,
                                  callback=callback)
        self.assertTrue(
            path.exists(self.local_path_dir + os.path.sep + self.local_file),
            'Expected the file has already been downloaded')

    @unittest.skip(
        "Yandex brakes response for file it contains property resourcetype as collection but it should "
        "be empty for file")
    def test_download_async(self):
        self._prepare_for_downloading()
        os.mkdir(self.local_path_dir)

        def callback():
            self.assertTrue(
                path.exists(self.local_path_dir + os.path.sep +
                            self.local_file),
                'Expected the file is downloaded')
            self.assertTrue(
                path.isfile(self.local_path_dir + os.path.sep +
                            self.local_file), 'Expected this is a file')

        self.client.download_async(local_path=self.local_path_dir +
                                   os.path.sep + self.local_file,
                                   remote_path=self.remote_path_file,
                                   callback=callback)
        self.assertFalse(
            path.exists(self.local_path_dir + os.path.sep + self.local_file),
            'Expected the file has not been downloaded yet')

    def test_upload_from(self):
        self._prepare_for_uploading()
        buff = StringIO(u'test content for testing of webdav client')
        self.client.upload_to(buff=buff, remote_path=self.remote_path_file)
        self.assertTrue(self.client.check(self.remote_path_file),
                        'Expected the file is uploaded.')

    def test_upload(self):
        self._prepare_for_uploading()
        self.client.upload(remote_path=self.remote_path_file,
                           local_path=self.local_path_dir)
        self.assertTrue(self.client.check(self.remote_path_dir),
                        'Expected the directory is created.')
        self.assertTrue(self.client.check(self.remote_path_file),
                        'Expected the file is uploaded.')

    def test_upload_file(self):
        self._prepare_for_uploading()
        self.client.upload_file(remote_path=self.remote_path_file,
                                local_path=self.local_file_path)
        self.assertTrue(self.client.check(remote_path=self.remote_path_file),
                        'Expected the file is uploaded.')

    def test_upload_sync(self):
        self._prepare_for_uploading()

        def callback():
            self.assertTrue(self.client.check(self.remote_path_dir),
                            'Expected the directory is created.')
            self.assertTrue(self.client.check(self.remote_path_file),
                            'Expected the file is uploaded.')

        self.client.upload(remote_path=self.remote_path_file,
                           local_path=self.local_path_dir)

    def test_upload_async(self):
        self._prepare_for_uploading()

        def callback():
            self.assertTrue(self.client.check(self.remote_path_dir),
                            'Expected the directory is created.')
            self.assertTrue(self.client.check(self.remote_path_file),
                            'Expected the file is uploaded.')

        self.client.upload(remote_path=self.remote_path_file,
                           local_path=self.local_path_dir)

    def test_copy(self):
        self._prepare_for_downloading()
        self.client.mkdir(remote_path=self.remote_path_dir2)
        self.client.copy(remote_path_from=self.remote_path_file,
                         remote_path_to=self.remote_path_file2)
        self.assertTrue(self.client.check(remote_path=self.remote_path_file2))

    def test_move(self):
        self._prepare_for_downloading()
        self.client.mkdir(remote_path=self.remote_path_dir2)
        self.client.move(remote_path_from=self.remote_path_file,
                         remote_path_to=self.remote_path_file2)
        self.assertFalse(self.client.check(remote_path=self.remote_path_file))
        self.assertTrue(self.client.check(remote_path=self.remote_path_file2))

    def test_clean(self):
        self._prepare_for_downloading()
        self.client.clean(remote_path=self.remote_path_dir)
        self.assertFalse(self.client.check(remote_path=self.remote_path_file))
        self.assertFalse(self.client.check(remote_path=self.remote_path_dir))

    def test_info(self):
        self._prepare_for_downloading()
        result = self.client.info(remote_path=self.remote_path_file)
        self.assertEquals(result['name'], 'test.txt')
        self.assertEquals(result['size'], '41')
        self.assertTrue('created' in result)
        self.assertTrue('modified' in result)

    def test_directory_is_dir(self):
        self._prepare_for_downloading()
        self.assertTrue(self.client.is_dir(self.remote_path_dir),
                        'Should return True for directory')

    def test_file_is_not_dir(self):
        self._prepare_for_downloading()
        self.assertFalse(self.client.is_dir(self.remote_path_file),
                         'Should return False for file')

    def test_get_property_of_non_exist(self):
        self._prepare_for_downloading()
        result = self.client.get_property(remote_path=self.remote_path_file,
                                          option={'name': 'aProperty'})
        self.assertEquals(
            result, None, 'For not found property should return value as None')

    def test_set_property(self):
        self._prepare_for_downloading()
        self.client.set_property(remote_path=self.remote_path_file,
                                 option={
                                     'namespace': 'test',
                                     'name': 'aProperty',
                                     'value': 'aValue'
                                 })
        result = self.client.get_property(remote_path=self.remote_path_file,
                                          option={
                                              'namespace': 'test',
                                              'name': 'aProperty'
                                          })
        self.assertEquals(result, 'aValue', 'Property value should be set')

    def test_set_property_batch(self):
        self._prepare_for_downloading()
        self.client.set_property_batch(remote_path=self.remote_path_file,
                                       option=[{
                                           'namespace': 'test',
                                           'name': 'aProperty',
                                           'value': 'aValue'
                                       }, {
                                           'namespace': 'test',
                                           'name': 'aProperty2',
                                           'value': 'aValue2'
                                       }])
        result = self.client.get_property(remote_path=self.remote_path_file,
                                          option={
                                              'namespace': 'test',
                                              'name': 'aProperty'
                                          })
        self.assertEquals(result, 'aValue',
                          'First property value should be set')
        result = self.client.get_property(remote_path=self.remote_path_file,
                                          option={
                                              'namespace': 'test',
                                              'name': 'aProperty2'
                                          })
        self.assertEquals(result, 'aValue2',
                          'Second property value should be set')

    def _prepare_for_downloading(self):
        if not self.client.check(remote_path=self.remote_path_dir):
            self.client.mkdir(remote_path=self.remote_path_dir)
        if not self.client.check(remote_path=self.remote_path_file):
            self.client.upload_file(remote_path=self.remote_path_file,
                                    local_path=self.local_file_path)
        if not path.exists(self.local_path_dir):
            os.makedirs(self.local_path_dir)

    def _prepare_for_uploading(self):
        if not self.client.check(remote_path=self.remote_path_dir):
            self.client.mkdir(remote_path=self.remote_path_dir)
        if not path.exists(path=self.local_path_dir):
            os.makedirs(self.local_path_dir)
        if not path.exists(path=self.local_path_dir + os.sep +
                           self.local_file):
            shutil.copy(src=self.local_file_path,
                        dst=self.local_path_dir + os.sep + self.local_file)
예제 #3
0
class WebDAV:
    """Commerce Cloud WebDAV session.

    Args:
        client (CommerceCloudClientSession): Active client session with Commerce Cloud for a bearer token.
        instance (str, optional): Optional commerce cloud instance, useful for opening clients to multiple instances using the same bearer token. Defaults to None.
        cert (str, optional): Path to TLS client certificate. Defaults to None.
        key ([type], optional): Export key for the TLS certificate. Defaults to None.
        verify (bool, optional): Verify TLS certificates, set to false for self signed. Defaults to True.
    """
    def __init__(self,
                 client,
                 instance=None,
                 cert=None,
                 key=None,
                 verify=True):
        self.client = client

        self._instance = instance or self.client.instance
        self.options = {"webdav_hostname": self._instance.rstrip("/")}
        self.verify = verify
        self.token = self.client.Token
        self.options.update({"webdav_token": self.token["access_token"]})
        self.webdav_client = Client(self.options)
        self.webdav_client.verify = self.verify
        if cert and key:
            self.cert = str(Path(cert).resolve())
            self.key = str(Path(key).resolve())
            self.options.update({"cert_path": self.cert, "key_path": self.key})

    def reauth(self):
        """Checks token expiry and re-initialises the Client if a new token is needed.
        """
        if self.token["expires_at"] < int(time.time()):
            self.client.getToken()
            self.options.update({"webdav_token": self.token["access_token"]})
            self.webdav_client = Client(self.options)

    def reconnect(self):
        """Re-initalise the Client session.
        """
        self.webdav_client = Client(self.options)

    @property
    def hostname(self):
        """Return the hostname the WebDAV client connection is connected to.

        Returns:
            str: Hostname including prefix eg https://
        """
        return self.options["webdav_hostname"]

    @property
    def netloc(self):
        """Return a urlparse netloc string of the connected hostname.

        Returns:
            str: netloc of hostname.
        """
        url = urlparse(self.options["webdav_hostname"])
        return url.netloc

    @retry(
        retry_on_exceptions=(RetryException),
        max_calls_total=3,
        retry_window_after_first_call_in_seconds=10,
    )
    def GetInfo(self, remote_filepath: str, headers: dict = None) -> list:
        """Get properties for entity

        [extended_summary]

        Args:
            remote_filepath (str): Path to remote resource.
            headers (dict, optional): Additional headers to apply to request. Defaults to None.

        Raises:
            RetryException: Adds to retries counter on failure.

        Returns:
            list: WebDAV attribute information.
        """
        try:
            return self.webdav_client.info(remote_filepath)
        except (NoConnection, ConnectionException, WebDavException):
            self.reauth()
            raise RetryException

    @retry(
        retry_on_exceptions=(RetryException),
        max_calls_total=3,
        retry_window_after_first_call_in_seconds=10,
    )
    def GetDirectoryList(self,
                         filepath: str,
                         get_info: bool = False,
                         headers: dict = None) -> list:
        """Get list of files and folders in a path from WebDAV endpoint.

        [extended_summary]

        Args:
            filepath (str): Path to get directory listing for.
            get_info (bool): returns dictionary of attributes instead of file list.
            headers (dict, optional): Additional headers to apply to request. Defaults to None.

        Returns:
            list: Directory listing.
        """
        try:
            return self.webdav_client.list(filepath, get_info=get_info)
        except (NoConnection, ConnectionException, WebDavException):
            self.reauth()
            raise RetryException

    @retry(
        retry_on_exceptions=(RetryException),
        max_calls_total=3,
        retry_window_after_first_call_in_seconds=10,
    )
    def Upload(self, local_filepath: str, remote_filepath: str):
        """Upload file or directory recursively to WebDAV endpoint.

        [extended_summary]

        Args:
            local_filepath (str): Local path to file or directory to upload.
            remote_filepath (str): Remote path to upload to.
        """
        local_filepath = str(Path(local_filepath).resolve())
        try:
            self.webdav_client.upload_sync(remote_filepath, local_filepath)
        except (NoConnection, ConnectionException, WebDavException):
            self.reauth()
            raise RetryException

    @retry(
        retry_on_exceptions=(RetryException),
        max_calls_total=3,
        retry_window_after_first_call_in_seconds=10,
    )
    def StreamUpload(self, payload, remote_path: str, file_name: str):
        """Upload FileIO, StringIO, BytesIO or string to WebDAV

        [extended_summary]

        Args:
            payload: Stream payload
            remote_path (str): Remote path relative to host.
            file_name (str): Name for the file uploaded.
        """
        try:
            self.webdav_client.upload_to(payload, f"{remote_path}/{file_name}")
        except (NoConnection, ConnectionException, WebDavException):
            self.reauth()
            raise RetryException

    @retry(
        retry_on_exceptions=(RetryException),
        max_calls_total=3,
        retry_window_after_first_call_in_seconds=10,
    )
    def MakeDir(self, remote_path: str):
        """Make new directory at path specified.

        Args:
            remote_path (str): Path of proposed new directory.
        """
        try:
            self.webdav_client.mkdir(remote_path)
        except (NoConnection, ConnectionException, WebDavException):
            self.reauth()
            raise RetryException

    @retry(
        retry_on_exceptions=(RetryException),
        max_calls_total=3,
        retry_window_after_first_call_in_seconds=10,
    )
    def Move(self,
             remote_path_source: str,
             remote_path_dest: str,
             overwrite: bool = False):
        """Make new directory at path specified.

        Args:
            remote_path_source (str): Path of source resource.
            remote_path_dest (str): Path of destination resource.
            overwrite (bool): Overwrite destination resource. Defaults to False.
        """
        try:
            self.webdav_client.move(remote_path_source, remote_path_dest,
                                    overwrite)
        except (NoConnection, ConnectionException, WebDavException):
            self.reauth()
            raise RetryException

    @retry(
        retry_on_exceptions=(RetryException),
        max_calls_total=3,
        retry_window_after_first_call_in_seconds=10,
    )
    def Delete(self, remote_filepath: str):
        """Delete file on remote WebDAV endpoint.

        Args:
            remote_filepath (str): Location of resource to delete.
        """
        try:
            self.webdav_client.clean(remote_filepath)
        except (NoConnection, ConnectionException, WebDavException):
            self.reauth()
            raise RetryException

    @retry(
        retry_on_exceptions=(RetryException),
        max_calls_total=3,
        retry_window_after_first_call_in_seconds=10,
    )
    def Download(self, local_filepath: str, remote_filepath: str):
        """Download file/folder from WebDAV endpoint.

        This is a synchronous operation, and the file is downloaded in full to the local_filepath.

        Args:
            local_filepath (str): Local path to download to, including filename of file saved.
            remote_filepath (str): Remote path to file to download.
        """
        local_filepath = str(Path(local_filepath).resolve())
        try:
            self.webdav_client.download_sync(remote_filepath, local_filepath)
        except (NoConnection, ConnectionException, WebDavException):
            self.reauth()
            raise RetryException

    @retry(
        retry_on_exceptions=(RetryException),
        max_calls_total=3,
        retry_window_after_first_call_in_seconds=10,
    )
    def Pull(self, local_filepath: str, remote_filepath: str):
        """Sync file/folder from WebDAV endpoint to local storage.

        This downloads missing or nwer modified files from the remote to local storage.
        You can use it to do "resumeable" transfers, but the checks are slow for deeply nested files.

        Args:
            local_filepath (str): Local path to download to, including filename of file saved.
            remote_filepath (str): Remote path to file to download.
        """
        local_filepath = str(Path(local_filepath).resolve())
        try:
            self.webdav_client.pull(remote_filepath, local_filepath)
            return True
        except (NoConnection, ConnectionException, WebDavException):
            self.reauth()
            raise RetryException
        return False

    @retry(
        retry_on_exceptions=(RetryException),
        max_calls_total=3,
        retry_window_after_first_call_in_seconds=10,
    )
    def Push(self, local_filepath: str, remote_filepath: str):
        """Sync file/folder from local storage to WebDAV endpoint.

        This uploads missing or nwer modified files from the local to remote storage.
        You can use it to do "resumeable" transfers, but the checks are slow for deeply nested files.

        Args:
            local_filepath (str): Local path to download to, including filename of file saved.
            remote_filepath (str): Remote path to file to download.
        """
        local_filepath = str(Path(local_filepath).resolve())
        try:
            self.webdav_client.push(local_filepath, remote_filepath)
            return True
        except (NoConnection, ConnectionException, WebDavException):
            self.reauth()
            raise RetryException
        return False

    @retry(
        retry_on_exceptions=(RetryException),
        max_calls_total=10,
        retry_window_after_first_call_in_seconds=15,
    )
    def StreamDownload(self,
                       remote_filepath: str,
                       buffer=None,
                       decode: bool = False):
        """Download a file in chunks to a local file buffer.

        You must provide a BytesIO object or one will be created for you.

        Args:
            remote_filepath (str): Path to remote resource to download.
            buffer ([type], optional): Buffer write streamed content to.
            decode (bool, optional): Optionally try to decode downloaded file into a string. Defaults to False.

        Raises:
            RetryException: Adds to retries counter on failure.

        Returns:
            Bytes: Returns a BytesIO object for further use.
        """
        self.reauth()
        if buffer is None:
            buffer = BytesIO()
        try:
            self.webdav_client.download_from(buff=buffer,
                                             remote_path=remote_filepath)
            if decode is True:
                return buffer.getvalue().decode("utf-8")
            else:
                buffer.seek(0)
                return buffer
        except (NoConnection, ConnectionException, WebDavException):
            raise RetryException

    @retry(
        retry_on_exceptions=(RetryException),
        max_calls_total=10,
        retry_window_after_first_call_in_seconds=60,
    )
    def HashObject(self, remote_filepath: str) -> str:
        """Generate a MD5 hashsum for a remote resource.

        This is streamed into memory, hashed and discarded. Optimised for low memory but
        high bandwidth environments.

        Args:
            remote_filepath (str): Path to remote resource.

        Raises:
            RetryException: Adds to retries counter on failure.

        Returns:
            str: MDSSUM of the file requested.
        """
        self.reauth()
        try:
            sum = md5(self.StreamDownload(remote_filepath).getbuffer())
            return {
                "filepath": remote_filepath,
                "hashtype": "MD5",
                "hashsum": sum.hexdigest(),
            }

        except (NoConnection, ConnectionException, WebDavException):
            self.reconnect()
            raise RetryException

    def RecursiveFileListing(self, remote_filepath: str) -> str:
        """Recursive filetree walker, returns paths found.

        Args:
            remote_filepath (str): [description]

        Raises:
            RetryException: Adds to retries counter on failure.

        Yields:
            Iterator[str]: Yields resource paths for any files found.
        """
        @retry(
            retry_on_exceptions=(RetryException),
            max_calls_total=10,
            retry_window_after_first_call_in_seconds=60,
        )
        def get_list(self, path):
            self.reauth()
            try:
                return self.webdav_client.list(path, get_info=True)
            except (NoConnection, ConnectionException, WebDavException):
                self.reconnect()
                raise RetryException

        def get_files(self, path):
            return [x for x in get_list(self, path) if x["isdir"] is False]

        def get_dirs(self, path):
            return [
                x["path"] for x in get_list(self, path) if x["isdir"] is True
            ]

        yield from get_files(self, remote_filepath)
        for subdir in get_dirs(self, remote_filepath):
            yield from self.RecursiveFileListing(subdir)

    def RecursiveFolderListing(self, remote_filepath: str) -> str:
        """Recursive filetree walker, returns paths found.

        Args:
            remote_filepath (str): [description]

        Raises:
            RetryException: Adds to retries counter on failure.

        Yields:
            Iterator[str]: Yields resource paths for any files found.
        """
        @retry(
            retry_on_exceptions=(RetryException),
            max_calls_total=10,
            retry_window_after_first_call_in_seconds=60,
        )
        def get_list(self, path):
            self.reauth()
            try:
                return self.webdav_client.list(path, get_info=True)
            except (NoConnection, ConnectionException, WebDavException):
                self.reconnect()
                raise RetryException

        def get_dirs(self, path):
            return [
                x["path"] for x in get_list(self, path) if x["isdir"] is True
            ]

        dirlist = get_dirs(self, remote_filepath)

        yield from dirlist
        for subdir in get_dirs(self, remote_filepath):
            yield from self.RecursiveFolderListing(subdir)
예제 #4
0
class WebDev(object):
    def __init__(self, options ):
        # self.client = wc.Client(options)
        self.client = Client(options)


    def read(self, name):
        with BytesIO() as buf:
            self.client.download_from(buf, name)
            return buf.getvalue()


    def write(self,content, target):
        data = self.read(target)

        with BytesIO() as buf:
            buf.write(data)
            buf.write(content.encode())
            self.client.upload_to(buf.getvalue(), target)


    def mkdir(self, dir_name):
        self.client.mkdir(dir_name)


    def list(self):
        data = self.client.list()
        print(data)


    def clear(self, target):
        self.client.clean(target)


    def check(self,remote_path):
        data = self.client.check(remote_path)
        if data:
            print('{} is exists'.format(remote_path))
        else:
            print('{} is not exists'.format(remote_path))


    def upload(self,remote_path, local_path):
        self.client.upload(remote_path, local_path)


    def help_dev(self):
        print(dir(self.client))


    def mkfile(self, remote_path):
        with BytesIO() as buf:
            buf.write(b'')
            self.client.upload_to(buf.getvalue(), remote_path)


    def download_file(self, remote_path):
        self.client.download_file(remote_path, remote_path)


    def rename(self, old_name, new_name):
        self.client.copy(old_name, old_name)
        self.client.clean(old_name)


    def set_property(self,remote_path, option):
        self.client.set_property(remote_path, option=[option])