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 validate_v1_path(self, path, **kwargs): """Take a string path from the url and attempt to map it to an entity within this article. If the entity is found, returns a FigsharePath object with the entity identifiers included. Otherwise throws a 404 Not Found. Will also assert that the entity type inferred from the path matches the type of the entity at that url. :param str path: entity path from the v1 API :rtype FigsharePath: """ if path == '/': return FigsharePath('/', _ids=('', ), folder=True, is_public=False) path_parts = self._path_split(path) if len(path_parts) != 2: raise exceptions.InvalidPathError( '{} is not a valid Figshare path.'.format(path)) file_id = path_parts[1] resp = await self.make_request( 'GET', self.build_url(False, *self.root_path_parts, 'files', file_id), expects=(200, ), ) file_json = await resp.json() return FigsharePath('/' + file_json['name'], _ids=('', file_id), folder=False, is_public=False)
def generic_path_validation(cls, path: str) -> None: """Validates a WaterButler specific path, e.g. /folder/file.txt, /folder/ :param str path: WaterButler path """ if not path: raise exceptions.InvalidPathError('Must specify path') if not path.startswith('/'): raise exceptions.InvalidPathError('Invalid path \'{}\' specified'.format(path)) if '//' in path: raise exceptions.InvalidPathError('Invalid path \'{}\' specified'.format(path)) # Do not allow path manipulation via shortcuts, e.g. '..' absolute_path = os.path.abspath(path) if not path == '/' and path.endswith('/'): absolute_path += '/' if not path == absolute_path: raise exceptions.InvalidPathError('Invalid path \'{}\' specified'.format(absolute_path))
async def validate_path(self, path, **kwargs): """Take a string path from the url and attempt to map it to an entity within this article. If the entity is found, returns a FigsharePath object with the entity identifiers included. Otherwise returns a FigsharePath with empty identifiers. :param str path: identifier path URN as passed through the v0 API :rtype FigsharePath: Quirks: * v0 may pass an identifier_path whose last part is a name and not an identifier, in the case of file/folder creation calls. * validate_path validates parent and returns a FigsharePath as accurately as possible. """ if path == '/': return FigsharePath('/', _ids=('', ), folder=True, is_public=False) path_parts = self._path_split(path) if len(path_parts) != 2: raise exceptions.InvalidPathError( '{} is not a valid Figshare path.'.format(path)) file_id = path_parts[1] resp = await self.make_request( 'GET', self.build_url(False, *self.root_path_parts, 'files', file_id), expects=( 200, 404, ), ) if resp.status == 200: file_json = await resp.json() file_name = file_json['name'] return FigsharePath('/' + file_name, _ids=('', file_id), folder=False, is_public=False) # catch for create file in article root await resp.release() return FigsharePath('/' + file_id, _ids=('', ''), folder=False, is_public=False)
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 validate_path(self, path, **kwargs): """Take a string path from the url and attempt to map it to an entity within this project. If the entity is found, returns a FigsharePath object with the entity identifiers included. Otherwise returns a FigsharePath with empty identifiers. :param str path: identifier_path URN as passed through the v0 API :rtype FigsharePath: Quirks: * v0 may pass an identifier_path whose last part is a name and not an identifier, in the case of file/folder creation calls. * validate_path validates parent and returns a FigsharePath as accurately as possible. """ if path == '/': return FigsharePath('/', _ids=('', ), folder=True, is_public=False) path_parts = self._path_split(path) if len(path_parts) not in (2, 3): raise exceptions.InvalidPathError( '{} is not a valid Figshare path.'.format(path)) article_id = path_parts[1] file_id = path_parts[2] if len(path_parts) == 3 else None articles = await self._get_all_articles() # TODO: need better way to get public/private # This call's return value is currently busted at figshare for collections. Figshare always # returns private-looking urls. is_public = False for item in articles: if '/articles/' + article_id in item['url']: article_name = item['title'] if settings.PRIVATE_IDENTIFIER not in item['url']: is_public = True article_segments = (*self.root_path_parts, 'articles', article_id) if file_id: file_response = await self.make_request( 'GET', self.build_url(is_public, *article_segments, 'files', file_id), expects=( 200, 404, ), ) if file_response.status == 200: file_response_json = await file_response.json() file_name = file_response_json['name'] return FigsharePath('/' + article_name + '/' + file_name, _ids=(self.container_id, article_id, file_id), folder=False, is_public=is_public) await file_response.release() article_response = await self.make_request( 'GET', self.build_url(is_public, *article_segments), expects=( 200, 404, ), ) if article_response.status == 200: article_json = await article_response.json() if article_json['defined_type'] in settings.FOLDER_TYPES: # Case of v0 file creation if file_id: ids = ('', article_id, '') folder = False path_urn = '/' + article_name + '/' + file_id else: ids = ('', article_id) folder = True path_urn = '/' + article_name + '/' return FigsharePath(path_urn, _ids=ids, folder=folder, is_public=is_public) else: await article_response.release() if file_id: # Catch for if neither file nor article exist raise exceptions.NotFoundError(path) # Return for v0 folder creation return FigsharePath(path, _ids=('', ''), folder=True, is_public=False)
async def validate_v1_path(self, path, **kwargs): """Take a string path from the url and attempt to map it to an entity within this project. If the entity is found, returns a FigsharePath object with the entity identifiers included. Otherwise throws a 404 Not Found. Will also assert that the entity type inferred from the path matches the type of the entity at that url. :param str path: entity path from the v1 API :rtype FigsharePath: """ if path == '/': return FigsharePath('/', _ids=('', ), folder=True, is_public=False) path_parts = self._path_split(path) if len(path_parts) not in (2, 3): raise exceptions.InvalidPathError( '{} is not a valid Figshare path.'.format(path)) article_id = path_parts[1] file_id = path_parts[2] if len(path_parts) == 3 else None articles = await self._get_all_articles() # TODO: need better way to get public/private # This call's return value is currently busted at figshare for collections. Figshare always # returns private-looking urls. is_public = False for item in articles: if '/articles/' + article_id in item['url']: article_name = item['title'] if settings.PRIVATE_IDENTIFIER not in item['url']: is_public = True article_segments = (*self.root_path_parts, 'articles', article_id) if file_id: file_response = await self.make_request( 'GET', self.build_url(is_public, *article_segments, 'files', file_id), expects=(200, ), ) file_json = await file_response.json() file_name = file_json['name'] if path[-1] == '/': raise exceptions.NotFoundError( 'File paths must not end with "/". ' '{} not found.'.format(path)) return FigsharePath('/' + article_name + '/' + file_name, _ids=(self.container_id, article_id, file_id), folder=False, is_public=is_public) article_response = await self.make_request( 'GET', self.build_url(is_public, *article_segments), expects=(200, ), ) article_json = await article_response.json() if article_json['defined_type'] in settings.FOLDER_TYPES: if not path[-1] == '/': raise exceptions.NotFoundError( 'Folder paths must end with "/". {} not found.'.format( path)) return FigsharePath('/' + article_name + '/', _ids=(self.container_id, article_id), folder=True, is_public=is_public) raise exceptions.NotFoundError( 'This article is not configured as a folder defined_type. ' '{} not found.'.format(path))
async def validate_v1_path(self, path: str, **kwargs) -> FigsharePath: """Take a string path from the url and attempt to map it to an entity within this project. If the entity is found, returns a FigsharePath object with the entity identifiers included. Otherwise throws a 404 Not Found. Will also assert that the entity type inferred from the path matches the type of the entity at that url. :param str path: entity path from the v1 API :rtype FigsharePath: """ if path == '/': # Root path should always be private api-wise since project and collection itself must # be owned by the fighsare-OSF OAuth user. return FigsharePath('/', _ids=('', ), folder=True, is_public=False) # Step 0: Preprocess the string path. path_parts = self._path_split(path) if len(path_parts) not in (2, 3): raise exceptions.InvalidPathError('{} is not a valid Figshare path.'.format(path)) article_id = path_parts[1] file_id = path_parts[2] if len(path_parts) == 3 else None # Step 1: Get a list of all articles in the project. articles = await self._get_all_articles() # Step 2: Find the article; set `article_name`, `is_public`; and prepare `article_segments`. is_public = False article_name = None for article in articles: if '/articles/' + article_id in article['url']: article_name = article['title'] is_public = article['published_date'] is not None break # Raise error earlier instead of on 404. Please note that this is different than V0. if not article_name: raise exceptions.NotFoundError('Path {} with article ID {} not found in the project\'s ' 'article list'.format(path, article_id)) article_segments = (*self.root_path_parts, 'articles', article_id) # Step 3.1: if the path is a file if file_id: file_response = await self.make_request( 'GET', self.build_url(is_public, *article_segments, 'files', file_id), expects=(200, ), ) file_json = await file_response.json() file_name = file_json['name'] if path[-1] == '/': raise exceptions.NotFoundError('File paths must not end with "/". ' '{} not found.'.format(path)) return FigsharePath('/' + article_name + '/' + file_name, _ids=(self.container_id, article_id, file_id), folder=False, is_public=is_public) # Step 3.2: if the path is a folder article_response = await self.make_request( 'GET', self.build_url(is_public, *article_segments), expects=(200, ), ) article_json = await article_response.json() if article_json['defined_type'] in pd_settings.FOLDER_TYPES: if not path[-1] == '/': raise exceptions.NotFoundError('Folder paths must end with "/". ' '{} not found.'.format(path)) return FigsharePath('/' + article_name + '/', _ids=(self.container_id, article_id), folder=True, is_public=is_public) raise exceptions.NotFoundError('This article is not configured as a folder defined_type. ' '{} not found.'.format(path))