async def intra_move(self, # type: ignore dest_provider: 'DropboxProvider', src_path: WaterButlerPath, dest_path: WaterButlerPath) -> typing.Tuple[BaseDropboxMetadata, bool]: if dest_path.full_path.lower() == src_path.full_path.lower(): # Dropbox does not support changing the casing in a file name raise core_exceptions.InvalidPathError( 'In Dropbox to change case, add or subtract other characters.') try: data = await self.dropbox_request( self.build_url('files', 'move_v2'), { 'from_path': src_path.full_path.rstrip('/'), 'to_path': dest_path.full_path.rstrip('/'), }, expects=(200, 201, 409), throws=core_exceptions.IntraMoveError, ) data = data['metadata'] except pd_exceptions.DropboxNamingConflictError: await dest_provider.delete(dest_path) resp, _ = await self.intra_move(dest_provider, src_path, dest_path) return resp, False dest_folder = dest_provider.folder if data['.tag'] == 'file': return DropboxFileMetadata(data, dest_folder), True folder = DropboxFolderMetadata(data, dest_folder) folder.children = [item for item in await dest_provider.metadata(dest_path)] # type: ignore return folder, True
async def intra_move(self, dest_provider, src_path, dest_path): if dest_path.full_path.lower() == src_path.full_path.lower(): # Dropbox does not support changing the casing in a file name raise exceptions.InvalidPathError('In Dropbox to change case, add or subtract other characters.') try: data = await self.dropbox_request( self.build_url('files', 'move'), { 'from_path': src_path.full_path.rstrip('/'), 'to_path': dest_path.full_path.rstrip('/'), }, expects=(200, 201, 409), throws=exceptions.IntraMoveError, ) except DropboxNamingConflictError: await dest_provider.delete(dest_path) resp, _ = await self.intra_move(dest_provider, src_path, dest_path) return resp, False dest_folder = dest_provider.folder if data['.tag'] == 'file': return DropboxFileMetadata(data, dest_folder), True folder = DropboxFolderMetadata(data, dest_folder) folder.children = [item for item in await dest_provider.metadata(dest_path)] return folder, True
async def intra_move( self, # type: ignore dest_provider: 'DropboxProvider', src_path: WaterButlerPath, dest_path: WaterButlerPath ) -> typing.Tuple[BaseDropboxMetadata, bool]: if dest_path.full_path.lower() == src_path.full_path.lower(): # Dropbox does not support changing the casing in a file name raise exceptions.InvalidPathError( 'In Dropbox to change case, add or subtract other characters.') try: data = await self.dropbox_request( self.build_url('files', 'move'), { 'from_path': src_path.full_path.rstrip('/'), 'to_path': dest_path.full_path.rstrip('/'), }, expects=(200, 201, 409), throws=exceptions.IntraMoveError, ) except DropboxNamingConflictError: await dest_provider.delete(dest_path) resp, _ = await self.intra_move(dest_provider, src_path, dest_path) return resp, False dest_folder = dest_provider.folder if data['.tag'] == 'file': return DropboxFileMetadata(data, dest_folder), True folder = DropboxFolderMetadata(data, dest_folder) folder.children = [ item for item in await dest_provider.metadata(dest_path) ] # type: ignore return folder, True
async def test_intra_copy_folder(self, provider, provider_fixtures): src_path = WaterButlerPath('/pfile/', prepend=provider.folder) dest_path = WaterButlerPath('/pfile_renamed/', prepend=provider.folder) url = provider.build_url('files', 'copy_v2') data = { 'from_path': src_path.full_path.rstrip('/'), 'to_path': dest_path.full_path.rstrip('/') } aiohttpretty.register_json_uri( 'POST', url, data=data, body=provider_fixtures['intra_move_copy_folder_metadata_v2']) url = provider.build_url('files', 'list_folder') data = {'path': dest_path.full_path} aiohttpretty.register_json_uri( 'POST', url, data=data, status=HTTPStatus.OK, body=provider_fixtures['folder_children']) result = await provider.intra_copy(provider, src_path, dest_path) expected = DropboxFolderMetadata( provider_fixtures['intra_move_copy_folder_metadata_v2'] ['metadata'], provider.folder) expected.children = [ DropboxFileMetadata(item, provider.folder) for item in provider_fixtures['folder_children']['entries'] ] assert expected == result[0]
def intra_copy(self, dest_provider, src_path, dest_path): dest_folder = dest_provider.folder try: if self == dest_provider: resp = yield from self.make_request( 'POST', self.build_url('fileops', 'copy'), data={ 'root': 'auto', 'from_path': src_path.full_path, 'to_path': dest_path.full_path, }, expects=(200, 201), throws=exceptions.IntraCopyError, ) else: from_ref_resp = yield from self.make_request( 'GET', self.build_url('copy_ref', 'auto', src_path.full_path), ) from_ref_data = yield from from_ref_resp.json() resp = yield from self.make_request( 'POST', self.build_url('fileops', 'copy'), data={ 'root': 'auto', 'from_copy_ref': from_ref_data['copy_ref'], 'to_path': dest_path, }, headers=dest_provider.default_headers, expects=(200, 201), throws=exceptions.IntraCopyError, ) except exceptions.IntraCopyError as e: if e.code != 403: raise yield from dest_provider.delete(dest_path) resp, _ = yield from self.intra_copy(dest_provider, src_path, dest_path) return resp, False # TODO Refactor into a function data = yield from resp.json() if not data['is_dir']: return DropboxFileMetadata(data, dest_folder), True folder = DropboxFolderMetadata(data, dest_folder) folder.children = [] for item in data['contents']: if item['is_dir']: folder.children.append(DropboxFolderMetadata( item, dest_folder)) else: folder.children.append(DropboxFileMetadata(item, dest_folder)) return folder, True
async def intra_copy(self, dest_provider, src_path, dest_path): dest_folder = dest_provider.folder try: if self == dest_provider: resp = await self.make_request( 'POST', self.build_url('fileops', 'copy'), data={ 'root': 'auto', 'from_path': src_path.full_path, 'to_path': dest_path.full_path, }, expects=(200, 201), throws=exceptions.IntraCopyError, ) else: from_ref_resp = await self.make_request( 'GET', self.build_url('copy_ref', 'auto', src_path.full_path), ) from_ref_data = await from_ref_resp.json() resp = await self.make_request( 'POST', self.build_url('fileops', 'copy'), data={ 'root': 'auto', 'from_copy_ref': from_ref_data['copy_ref'], 'to_path': dest_path, }, headers=dest_provider.default_headers, expects=(200, 201), throws=exceptions.IntraCopyError, ) except exceptions.IntraCopyError as e: if e.code != 403: raise await dest_provider.delete(dest_path) resp, _ = await self.intra_copy(dest_provider, src_path, dest_path) return resp, False # TODO Refactor into a function data = await resp.json() if not data['is_dir']: return DropboxFileMetadata(data, dest_folder), True folder = DropboxFolderMetadata(data, dest_folder) folder.children = [] for item in data['contents']: if item['is_dir']: folder.children.append(DropboxFolderMetadata(item, dest_folder)) else: folder.children.append(DropboxFileMetadata(item, dest_folder)) return folder, True
async def test_intra_move_replace_folder(self, provider, provider_fixtures, error_fixtures): url = provider.build_url('files', 'delete_v2') path = await provider.validate_path('/newfolder/') data = {'path': path.full_path} aiohttpretty.register_json_uri('POST', url, data=data, status=HTTPStatus.OK) url = provider.build_url('files', 'list_folder') data = {'path': path.full_path} aiohttpretty.register_json_uri( 'POST', url, data=data, body=provider_fixtures['folder_children'], status=HTTPStatus.OK ) src_path = WaterButlerPath('/pfile/', prepend=provider.folder) dest_path = WaterButlerPath('/pfile_renamed/', prepend=provider.folder) url = provider.build_url('files', 'move_v2') data = { 'from_path': src_path.full_path.rstrip('/'), 'to_path': dest_path.full_path.rstrip('/') } aiohttpretty.register_json_uri('POST', url, **{ "responses": [ { 'headers': {'Content-Type': 'application/json'}, 'data': data, 'body': json.dumps(error_fixtures['rename_conflict_folder_metadata']).encode('utf-8'), 'status': HTTPStatus.CONFLICT }, { 'headers': {'Content-Type': 'application/json'}, 'data': data, 'body': json.dumps( provider_fixtures['intra_move_copy_folder_metadata_v2']).encode('utf-8') }, ] }) result = await provider.intra_move(provider, src_path, dest_path) expected = DropboxFolderMetadata( provider_fixtures['intra_move_copy_folder_metadata_v2']['metadata'], provider.folder ) expected.children = [ DropboxFileMetadata(item, provider.folder) for item in provider_fixtures['folder_children']['entries'] ] assert expected == result[0]
async def create_folder(self, path, **kwargs): """ :param str path: The path to create a folder at """ WaterButlerPath.validate_folder(path) response = await self.make_request( 'POST', self.build_url('fileops', 'create_folder'), params={ 'root': 'auto', 'path': path.full_path }, expects=(200, 403), throws=exceptions.CreateFolderError ) data = await response.json() if response.status == 403: if 'because a file or folder already exists at path' in data.get('error'): raise exceptions.FolderNamingConflict(str(path)) raise exceptions.CreateFolderError(data, code=403) return DropboxFolderMetadata(data, self.folder)
def metadata(self, path, **kwargs): resp = yield from self.make_request( 'GET', self.build_url('metadata', 'auto', path.full_path), expects=(200, ), throws=exceptions.MetadataError ) data = yield from resp.json() # Dropbox will match a file or folder by name within the requested path if path.is_file and data['is_dir']: raise exceptions.MetadataError( "Could not retrieve file '{}'".format(path), code=http.client.NOT_FOUND, ) if data.get('is_deleted'): raise exceptions.MetadataError( "Could not retrieve {kind} '{path}'".format( kind='folder' if data['is_dir'] else 'file', path=path, ), code=http.client.NOT_FOUND, ) if data['is_dir']: ret = [] for item in data['contents']: if item['is_dir']: ret.append(DropboxFolderMetadata(item, self.folder).serialized()) else: ret.append(DropboxFileMetadata(item, self.folder).serialized()) return ret return DropboxFileMetadata(data, self.folder).serialized()
async def intra_copy(self, # type: ignore dest_provider: 'DropboxProvider', src_path: WaterButlerPath, dest_path: WaterButlerPath) \ -> typing.Tuple[typing.Union[DropboxFileMetadata, DropboxFolderMetadata], bool]: dest_folder = dest_provider.folder try: if self == dest_provider: data = await self.dropbox_request( self.build_url('files', 'copy'), { 'from_path': src_path.full_path.rstrip('/'), 'to_path': dest_path.full_path.rstrip('/'), }, expects=(200, 201, 409), throws=exceptions.IntraCopyError, ) else: from_ref_data = await self.dropbox_request( self.build_url('files', 'copy_reference', 'get'), {'path': src_path.full_path.rstrip('/')}, throws=exceptions.IntraCopyError, ) from_ref = from_ref_data['copy_reference'] data = await dest_provider.dropbox_request( self.build_url('files', 'copy_reference', 'save'), { 'copy_reference': from_ref, 'path': dest_path.full_path.rstrip('/') }, expects=(200, 201, 409), throws=exceptions.IntraCopyError, ) data = data['metadata'] except DropboxNamingConflictError: await dest_provider.delete(dest_path) resp, _ = await self.intra_copy(dest_provider, src_path, dest_path) return resp, False if data['.tag'] == 'file': return DropboxFileMetadata(data, dest_folder), True folder = DropboxFolderMetadata(data, dest_folder) folder.children = [ item for item in await dest_provider.metadata(dest_path) ] # type: ignore return folder, True
def intra_move(self, dest_provider, src_path, dest_path): if dest_path.full_path.lower() == src_path.full_path.lower(): # Dropbox does not support changing the casing in a file name raise exceptions.InvalidPathError( 'In Dropbox to change case, add or subtract other characters.') dest_folder = dest_provider.folder try: resp = yield from self.make_request( 'POST', self.build_url('fileops', 'move'), data={ 'root': 'auto', 'to_path': dest_path.full_path, 'from_path': src_path.full_path, }, expects=(200, ), throws=exceptions.IntraMoveError, ) except exceptions.IntraMoveError as e: if e.code != 403: raise yield from dest_provider.delete(dest_path) resp, _ = yield from self.intra_move(dest_provider, src_path, dest_path) return resp, False data = yield from resp.json() if not data['is_dir']: return DropboxFileMetadata(data, dest_folder), True folder = DropboxFolderMetadata(data, dest_folder) folder.children = [] for item in data['contents']: if item['is_dir']: folder.children.append(DropboxFolderMetadata( item, dest_folder)) else: folder.children.append(DropboxFileMetadata(item, dest_folder)) return folder, True
async def intra_copy(self, # type: ignore dest_provider: 'DropboxProvider', src_path: WaterButlerPath, dest_path: WaterButlerPath) \ -> typing.Tuple[typing.Union[DropboxFileMetadata, DropboxFolderMetadata], bool]: dest_folder = dest_provider.folder try: if self == dest_provider: data = await self.dropbox_request( self.build_url('files', 'copy_v2'), { 'from_path': src_path.full_path.rstrip('/'), 'to_path': dest_path.full_path.rstrip('/'), }, expects=(200, 201, 409), throws=core_exceptions.IntraCopyError, ) else: from_ref_data = await self.dropbox_request( self.build_url('files', 'copy_reference', 'get'), {'path': src_path.full_path.rstrip('/')}, throws=core_exceptions.IntraCopyError, ) from_ref = from_ref_data['copy_reference'] data = await dest_provider.dropbox_request( self.build_url('files', 'copy_reference', 'save'), {'copy_reference': from_ref, 'path': dest_path.full_path.rstrip('/')}, expects=(200, 201, 409), throws=core_exceptions.IntraCopyError, ) data = data['metadata'] except pd_exceptions.DropboxNamingConflictError: await dest_provider.delete(dest_path) resp, _ = await self.intra_copy(dest_provider, src_path, dest_path) return resp, False if data['.tag'] == 'file': return DropboxFileMetadata(data, dest_folder), True folder = DropboxFolderMetadata(data, dest_folder) folder.children = [item for item in await dest_provider.metadata(dest_path)] # type: ignore return folder, True
async def intra_move(self, dest_provider, src_path, dest_path): if dest_path.full_path.lower() == src_path.full_path.lower(): # Dropbox does not support changing the casing in a file name raise exceptions.InvalidPathError('In Dropbox to change case, add or subtract other characters.') dest_folder = dest_provider.folder try: resp = await self.make_request( 'POST', self.build_url('fileops', 'move'), data={ 'root': 'auto', 'to_path': dest_path.full_path, 'from_path': src_path.full_path, }, expects=(200, ), throws=exceptions.IntraMoveError, ) except exceptions.IntraMoveError as e: if e.code != 403: raise await dest_provider.delete(dest_path) resp, _ = await self.intra_move(dest_provider, src_path, dest_path) return resp, False data = await resp.json() if not data['is_dir']: return DropboxFileMetadata(data, dest_folder), True folder = DropboxFolderMetadata(data, dest_folder) folder.children = [] for item in data['contents']: if item['is_dir']: folder.children.append(DropboxFolderMetadata(item, dest_folder)) else: folder.children.append(DropboxFileMetadata(item, dest_folder)) return folder, True
async def create_folder(self, path: WaterButlerPath, **kwargs) -> DropboxFolderMetadata: """ :param str path: The path to create a folder at """ WaterButlerPath.validate_folder(path) data = await self.dropbox_request( self.build_url('files', 'create_folder'), {'path': path.full_path.rstrip('/')}, throws=exceptions.CreateFolderError, ) return DropboxFolderMetadata(data, self.folder)
async def metadata(self, # type: ignore path: WaterButlerPath, revision: str=None, **kwargs) \ -> typing.Union[BaseDropboxMetadata, typing.List[BaseDropboxMetadata]]: full_path = path.full_path.rstrip('/') url = self.build_url('files', 'get_metadata') body = {'path': full_path} if revision: body = {'path': 'rev:' + revision} elif path.is_folder: url = self.build_url('files', 'list_folder') if path.is_folder: ret = [] # type: typing.List[BaseDropboxMetadata] has_more = True page_count = 0 while has_more: page_count += 1 data = await self.dropbox_request( url, body, throws=exceptions.MetadataError) for entry in data['entries']: if entry['.tag'] == 'folder': ret.append(DropboxFolderMetadata(entry, self.folder)) else: ret.append(DropboxFileMetadata(entry, self.folder)) if not data['has_more']: has_more = False else: url = self.build_url('files', 'list_folder', 'continue') body = {'cursor': data['cursor']} self.metrics.add('metadata.folder.pages', page_count) return ret data = await self.dropbox_request(url, body, throws=exceptions.MetadataError) # Dropbox v2 API will not indicate file/folder if path "deleted" if data['.tag'] == 'deleted': raise exceptions.MetadataError( "Could not retrieve '{}'".format(path), code=HTTPStatus.NOT_FOUND, ) # Dropbox will match a file or folder by name within the requested path if path.is_file and data['.tag'] == 'folder': raise exceptions.MetadataError( "Could not retrieve file '{}'".format(path), code=HTTPStatus.NOT_FOUND, ) return DropboxFileMetadata(data, self.folder)
def test_folder_metadata(self, root_provider_fixtures): data = DropboxFolderMetadata(root_provider_fixtures['folder_metadata'], '/Photos') assert data.name == 'newfolder' assert data.path == '/newfolder/' assert data.etag is None assert data.serialized() == { 'extra': {}, 'kind': 'folder', 'name': 'newfolder', 'path': '/newfolder/', 'provider': 'dropbox', 'materialized': '/newfolder/', 'etag': 'bbd6cc654c4a3ca1124b69fccb392ec9754e18e9094effb525192509f8e1b901' } link_url = 'http://localhost:7777/v1/resources/mucuew/providers/dropbox/newfolder/' assert data.json_api_serialized('mucuew') == { 'id': 'dropbox/newfolder/', 'type': 'files', 'attributes': { 'extra': {}, 'kind': 'folder', 'name': 'newfolder', 'path': '/newfolder/', 'provider': 'dropbox', 'materialized': '/newfolder/', 'etag': 'bbd6cc654c4a3ca1124b69fccb392ec9754e18e9094effb525192509f8e1b901', 'resource': 'mucuew', 'size': None, }, 'links': { 'move': link_url, 'upload': '{}?kind=file'.format(link_url), 'delete': link_url, 'new_folder': '{}?kind=folder'.format(link_url), } } assert data._json_api_links('mucuew') == { 'move': link_url, 'upload': '{}?kind=file'.format(link_url), 'delete': link_url, 'new_folder': '{}?kind=folder'.format(link_url), } assert data.children is None assert data._entity_url('mucuew') == link_url assert data.is_folder is True assert data.is_file is False assert data.provider == 'dropbox' assert data.materialized_path == '/newfolder/' assert data.extra == {}
async def intra_copy(self, dest_provider, src_path, dest_path): dest_folder = dest_provider.folder try: if self == dest_provider: data = await self.dropbox_request( self.build_url('files', 'copy'), { 'from_path': src_path.full_path.rstrip('/'), 'to_path': dest_path.full_path.rstrip('/'), }, expects=(200, 201, 409), throws=exceptions.IntraCopyError, ) else: from_ref_data = await self.dropbox_request( self.build_url('files', 'copy_reference', 'get'), {'path': src_path.full_path.rstrip('/')}, throws=exceptions.IntraCopyError, ) from_ref = from_ref_data['copy_reference'] data = await dest_provider.dropbox_request( self.build_url('files', 'copy_reference', 'save'), {'copy_reference': from_ref, 'path': dest_path.full_path.rstrip('/')}, expects=(200, 201, 409), throws=exceptions.IntraCopyError, ) data = data['metadata'] except DropboxNamingConflictError: await dest_provider.delete(dest_path) resp, _ = await self.intra_copy(dest_provider, src_path, dest_path) return resp, False if data['.tag'] == 'file': return DropboxFileMetadata(data, dest_folder), True folder = DropboxFolderMetadata(data, dest_folder) folder.children = [item for item in await dest_provider.metadata(dest_path)] return folder, True
async def metadata(self, path, revision=None, **kwargs): if revision: url = self.build_url('revisions', 'auto', path.full_path, rev_limit=250) else: url = self.build_url('metadata', 'auto', path.full_path) resp = await self.make_request('GET', url, expects=(200, ), throws=exceptions.MetadataError) data = await resp.json() if revision: try: data = next(v for v in (await resp.json()) if v['rev'] == revision) except StopIteration: raise exceptions.NotFoundError(str(path)) # Dropbox will match a file or folder by name within the requested path if path.is_file and data['is_dir']: raise exceptions.MetadataError( "Could not retrieve file '{}'".format(path), code=http.client.NOT_FOUND, ) if data.get('is_deleted'): raise exceptions.MetadataError( "Could not retrieve {kind} '{path}'".format( kind='folder' if data['is_dir'] else 'file', path=path, ), code=http.client.NOT_FOUND, ) if data['is_dir']: ret = [] for item in data['contents']: if item['is_dir']: ret.append(DropboxFolderMetadata(item, self.folder)) else: ret.append(DropboxFileMetadata(item, self.folder)) return ret return DropboxFileMetadata(data, self.folder)