From 5db8f080ba9b1696f6bd14ba8373af751c2922b0 Mon Sep 17 00:00:00 2001 From: Addison Schiller Date: Wed, 22 Nov 2017 09:48:31 -0500 Subject: [PATCH] Reworking and refactoring --- tests/core/test_provider.py | 18 ++++ tests/providers/box/test_provider.py | 92 ++++++++++--------- tests/providers/dropbox/test_provider.py | 61 +++++++----- tests/providers/googledrive/test_provider.py | 43 +++------ tests/providers/owncloud/test_provider.py | 14 +++ waterbutler/core/provider.py | 55 +++++++++++ waterbutler/providers/box/provider.py | 11 +-- waterbutler/providers/dropbox/provider.py | 11 +-- waterbutler/providers/googledrive/provider.py | 11 +-- waterbutler/providers/owncloud/provider.py | 8 +- 10 files changed, 200 insertions(+), 124 deletions(-) diff --git a/tests/core/test_provider.py b/tests/core/test_provider.py index 123888ca0..d06386560 100644 --- a/tests/core/test_provider.py +++ b/tests/core/test_provider.py @@ -276,6 +276,15 @@ async def test_passes_on_rename(self, provider1): conflict='replace', ) + @pytest.mark.asyncio + async def test_copy_will_self_overwrite(self, provider1): + src_path = await provider1.validate_path('/source/path') + dest_path = await provider1.validate_path('/destination/') + provider1.will_self_overwrite = utils.MockCoroutine() + + with pytest.raises(exceptions.OverwriteSelfError): + await provider1.copy(provider1, src_path, dest_path) + @pytest.mark.asyncio async def test_checks_can_intra_copy(self, provider1): provider1.can_intra_copy = mock.Mock(return_value=False) @@ -393,6 +402,15 @@ async def test_passes_on_rename(self, provider1): conflict='replace', ) + @pytest.mark.asyncio + async def test_move_will_self_overwrite(self, provider1): + src_path = await provider1.validate_path('/source/path') + dest_path = await provider1.validate_path('/destination/') + provider1.will_self_overwrite = utils.MockCoroutine() + + with pytest.raises(exceptions.OverwriteSelfError): + await provider1.move(provider1, src_path, dest_path) + @pytest.mark.asyncio async def test_checks_can_intra_move(self, provider1): provider1.can_intra_move = mock.Mock(return_value=False) diff --git a/tests/providers/box/test_provider.py b/tests/providers/box/test_provider.py index 9a8a0c756..32bd3690a 100644 --- a/tests/providers/box/test_provider.py +++ b/tests/providers/box/test_provider.py @@ -165,7 +165,9 @@ async def test_validate_path(self, provider, root_provider_fixtures): provider.folder = '0' folder_id = '0' - good_url = provider.build_url('folders', folder_id, 'items', fields='id,name,type', limit=1000) + good_url = provider.build_url('folders', folder_id, 'items', + fields='id,name,type', limit=1000) + aiohttpretty.register_json_uri('GET', good_url, body=root_provider_fixtures['revalidate_metadata'], status=200) @@ -295,7 +297,7 @@ async def test_upload_checksum_mismatch(self, provider, root_provider_fixtures, aiohttpretty.register_json_uri('POST', upload_url, status=201, body=root_provider_fixtures['checksum_mismatch_metadata']) - with pytest.raises(exceptions.UploadChecksumMismatchError) as exc: + with pytest.raises(exceptions.UploadChecksumMismatchError): await provider.upload(file_stream, path) assert aiohttpretty.has_call(method='POST', uri=upload_url) @@ -360,8 +362,11 @@ async def test_delete_root(self, provider, root_provider_fixtures): url = provider.build_url('folders', root_path.identifier, 'items', fields='id,name,size,modified_at,etag,total_count', offset=(0), limit=1000) - aiohttpretty.register_json_uri('GET', url, - body=root_provider_fixtures['one_entry_folder_list_metadata']) + aiohttpretty.register_json_uri( + 'GET', + url, + body=root_provider_fixtures['one_entry_folder_list_metadata'] + ) url = provider.build_url('files', item['id'], fields='id,name,path_collection') delete_url = provider.build_url('files', path.identifier) @@ -568,6 +573,7 @@ async def test_get_revisions_free_account(self, provider, root_provider_fixtures class TestIntraCopy: + @pytest.mark.asyncio @pytest.mark.aiohttpretty async def test_intra_copy_file(self, provider, root_provider_fixtures): @@ -583,16 +589,6 @@ async def test_intra_copy_file(self, provider, root_provider_fixtures): assert result == expected - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test_intra_copy_overwrite_error(self, provider, root_provider_fixtures): - item = root_provider_fixtures['file_metadata']['entries'][0] - src_path = WaterButlerPath('/name.txt', _ids=(provider, item['id'])) - with pytest.raises(exceptions.IntraCopyError) as e: - await provider.intra_copy(provider, src_path, src_path) - - assert e.value.code == 409 - @pytest.mark.asyncio @pytest.mark.aiohttpretty async def test_intra_copy_file_replace(self, provider, root_provider_fixtures): @@ -630,7 +626,9 @@ async def test_intra_copy_folder(self, provider, intra_fixtures, root_provider_f expected_folder = BoxFolderMetadata(item, dest_path) expected_folder._children = [] for child_item in list_metadata['entries']: - child_path = dest_path.child(child_item['name'], folder=(child_item['type'] == 'folder')) + child_path = dest_path.child(child_item['name'], + folder=(child_item['type'] == 'folder')) + serialized_child = provider._serialize_item(child_item, child_path) expected_folder._children.append(serialized_child) expected = (expected_folder, True) @@ -641,7 +639,10 @@ async def test_intra_copy_folder(self, provider, intra_fixtures, root_provider_f @pytest.mark.asyncio @pytest.mark.aiohttpretty - async def test_intra_copy_folder_replace(self, provider, intra_fixtures, root_provider_fixtures): + async def test_intra_copy_folder_replace(self, + provider, + intra_fixtures, + root_provider_fixtures): item = intra_fixtures['intra_folder_metadata'] list_metadata = root_provider_fixtures['folder_list_metadata'] @@ -661,7 +662,9 @@ async def test_intra_copy_folder_replace(self, provider, intra_fixtures, root_pr expected_folder = BoxFolderMetadata(item, dest_path) expected_folder._children = [] for child_item in list_metadata['entries']: - child_path = dest_path.child(child_item['name'], folder=(child_item['type'] == 'folder')) + child_path = dest_path.child(child_item['name'], + folder=(child_item['type'] == 'folder')) + serialized_child = provider._serialize_item(child_item, child_path) expected_folder._children.append(serialized_child) expected = (expected_folder, False) @@ -689,22 +692,13 @@ async def test_intra_move_file(self, provider, root_provider_fixtures): assert result == expected - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test_intra_move_overwrite_error(self, provider, root_provider_fixtures): - item = root_provider_fixtures['file_metadata']['entries'][0] - src_path = WaterButlerPath('/name.txt', _ids=(provider, item['id'])) - with pytest.raises(exceptions.IntraCopyError) as e: - await provider.intra_move(provider, src_path, src_path) - - assert e.value.code == 409 - @pytest.mark.asyncio @pytest.mark.aiohttpretty async def test_intra_move_file_replace(self, provider, root_provider_fixtures): item = root_provider_fixtures['file_metadata']['entries'][0] src_path = WaterButlerPath('/name.txt', _ids=(provider, item['id'])) - dest_path = WaterButlerPath('/charmander/name.txt', _ids=(provider, item['id'], 'YgzZejrj834j')) + dest_path = WaterButlerPath('/charmander/name.txt', + _ids=(provider, item['id'], 'YgzZejrj834j')) file_url = provider.build_url('files', src_path.identifier) delete_url = provider.build_url('files', dest_path.identifier) @@ -736,7 +730,10 @@ async def test_intra_move_folder(self, provider, intra_fixtures, root_provider_f expected_folder = BoxFolderMetadata(item, dest_path) expected_folder._children = [] for child_item in list_metadata['entries']: - child_path = dest_path.child(child_item['name'], folder=(child_item['type'] == 'folder')) + child_path = dest_path.child( + child_item['name'], + folder=(child_item['type'] == 'folder') + ) serialized_child = provider._serialize_item(child_item, child_path) expected_folder._children.append(serialized_child) expected = (expected_folder, True) @@ -747,7 +744,10 @@ async def test_intra_move_folder(self, provider, intra_fixtures, root_provider_f @pytest.mark.asyncio @pytest.mark.aiohttpretty - async def test_intra_move_folder_replace(self, provider, intra_fixtures, root_provider_fixtures): + async def test_intra_move_folder_replace(self, + provider, + intra_fixtures, + root_provider_fixtures): item = intra_fixtures['intra_folder_metadata'] list_metadata = root_provider_fixtures['folder_list_metadata'] @@ -767,7 +767,9 @@ async def test_intra_move_folder_replace(self, provider, intra_fixtures, root_pr expected_folder = BoxFolderMetadata(item, dest_path) expected_folder._children = [] for child_item in list_metadata['entries']: - child_path = dest_path.child(child_item['name'], folder=(child_item['type'] == 'folder')) + child_path = dest_path.child(child_item['name'], + folder=(child_item['type'] == 'folder')) + serialized_child = provider._serialize_item(child_item, child_path) expected_folder._children.append(serialized_child) expected = (expected_folder, False) @@ -851,25 +853,29 @@ async def test_returns_metadata(self, provider, root_provider_fixtures): class TestOperations: - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test_can_duplicate_names(self, provider): + def test_will_self_overwrite(self, provider, other_provider): + src_path = WaterButlerPath('/50 shades of nope.txt', + _ids=(provider.folder, '12231')) + dest_path = WaterButlerPath('/50 shades of nope2223.txt', + _ids=(provider.folder, '2342sdfsd')) + + result = provider.will_self_overwrite(other_provider, src_path, dest_path) + assert result is False + + result = provider.will_self_overwrite(other_provider, src_path, src_path) + assert result is True + + def test_can_duplicate_names(self, provider): assert provider.can_duplicate_names() is False - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test_shares_storage_root(self, provider, other_provider): + def test_shares_storage_root(self, provider, other_provider): assert provider.shares_storage_root(other_provider) is False assert provider.shares_storage_root(provider) is True - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test_can_intra_move(self, provider, other_provider): + def test_can_intra_move(self, provider, other_provider): assert provider.can_intra_move(other_provider) is False assert provider.can_intra_move(provider) is True - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test_can_intra_copy(self, provider, other_provider): + def test_can_intra_copy(self, provider, other_provider): assert provider.can_intra_copy(other_provider) is False assert provider.can_intra_copy(provider) is True diff --git a/tests/providers/dropbox/test_provider.py b/tests/providers/dropbox/test_provider.py index 582f80d11..649853eda 100644 --- a/tests/providers/dropbox/test_provider.py +++ b/tests/providers/dropbox/test_provider.py @@ -290,8 +290,12 @@ async def test_folder_with_subdirectory_metadata(self, provider, root_provider_f path = await provider.validate_path('/') url = provider.build_url('files', 'list_folder') data = {'path': path.full_path} - aiohttpretty.register_json_uri('POST', url, data=data, - body=root_provider_fixtures['folder_with_subdirectory_metadata']) + aiohttpretty.register_json_uri( + 'POST', + url, + data=data, + body=root_provider_fixtures['folder_with_subdirectory_metadata'] + ) result = await provider.metadata(path) assert isinstance(result, list) @@ -308,8 +312,12 @@ async def test_folder_with_hasmore_metadata(self, provider, root_provider_fixtur data = {'path': path.full_path} aiohttpretty.register_json_uri('POST', url, data=data, body=root_provider_fixtures['folder_with_hasmore_metadata']) - aiohttpretty.register_json_uri('POST', url + '/continue', data=data, - body=root_provider_fixtures['folder_with_subdirectory_metadata']) + aiohttpretty.register_json_uri( + 'POST', + url + '/continue', + data=data, + body=root_provider_fixtures['folder_with_subdirectory_metadata'] + ) result = await provider.metadata(path) @@ -544,7 +552,9 @@ async def test_intra_copy_replace_file(self, provider, root_provider_fixtures, e { 'headers': {'Content-Type': 'application/json'}, 'data': data, - 'body': json.dumps(error_fixtures['rename_conflict_folder_metadata']).encode('utf-8'), + 'body': json.dumps( + error_fixtures['rename_conflict_folder_metadata'] + ).encode('utf-8'), 'status': 409 }, { @@ -574,8 +584,12 @@ async def test_intra_copy_file_different_provider(self, provider, other_provider url1 = provider.build_url('files', 'copy_reference', 'save') data1 = {'copy_reference': 'test', 'path': dest_path.full_path.rstrip('/')} - aiohttpretty.register_json_uri('POST', url1, data=data1, - body=intra_copy_fixtures['intra_copy_other_provider_file_metadata']) + aiohttpretty.register_json_uri( + 'POST', + url1, + data=data1, + body=intra_copy_fixtures['intra_copy_other_provider_file_metadata'] + ) result = await provider.intra_copy(other_provider, src_path, dest_path) expected = (DropboxFileMetadata( @@ -648,7 +662,9 @@ async def test_intra_move_replace_file(self, provider, root_provider_fixtures, e { 'headers': {'Content-Type': 'application/json'}, 'data': data, - 'body': json.dumps(error_fixtures['rename_conflict_file_metadata']).encode('utf-8'), + 'body': json.dumps( + error_fixtures['rename_conflict_file_metadata'] + ).encode('utf-8'), 'status': 409 }, { @@ -689,7 +705,9 @@ async def test_intra_move_replace_folder(self, provider, root_provider_fixtures, { 'headers': {'Content-Type': 'application/json'}, 'data': data, - 'body': json.dumps(error_fixtures['rename_conflict_folder_metadata']).encode('utf-8'), + 'body': json.dumps( + error_fixtures['rename_conflict_folder_metadata'] + ).encode('utf-8'), 'status': 409 }, { @@ -719,23 +737,20 @@ async def test_intra_move_casing_change(self, provider): assert e.value.code == 400 - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test_intra_overwrite_error(self, provider): - src_path = WaterButlerPath('/pfile.txt', prepend=provider.folder) - with pytest.raises(exceptions.IntraCopyError) as e: - await provider.intra_move(provider, src_path, src_path) - - assert e.value.code == 409 - - with pytest.raises(exceptions.IntraCopyError) as e: - await provider.intra_copy(provider, src_path, src_path) +class TestOperations: - assert e.value.code == 409 + def test_will_self_overwrite(self, provider, other_provider): + src_path = WaterButlerPath('/50 shades of nope.txt', + _ids=(provider.folder, '12231')) + dest_path = WaterButlerPath('/50 shades of nope2223.txt', + _ids=(provider.folder, '2342sdfsd')) + result = provider.will_self_overwrite(other_provider, src_path, dest_path) + assert result is False -class TestOperations: + result = provider.will_self_overwrite(other_provider, src_path, src_path) + assert result is True def test_can_intra_copy(self, provider): assert provider.can_intra_copy(provider) @@ -747,7 +762,7 @@ def test_can_intra_move(self, provider): assert provider.can_intra_move(provider) def test_cannot_intra_move_other(self, provider, other_provider): - assert provider.can_intra_move(other_provider) == False + assert provider.can_intra_move(other_provider) is False def test_conflict_error_handler_not_found(self, provider, error_fixtures): error_path = '/Photos/folder/file' diff --git a/tests/providers/googledrive/test_provider.py b/tests/providers/googledrive/test_provider.py index 061d11b6e..eeee379c1 100644 --- a/tests/providers/googledrive/test_provider.py +++ b/tests/providers/googledrive/test_provider.py @@ -1573,51 +1573,36 @@ async def test_intra_copy_file(self, provider, root_provider_fixtures): assert result == expected assert aiohttpretty.has_call(method='PUT', uri=delete_url) - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test_intra_copy_move_overwrite_error(self, provider, root_provider_fixtures): - item = root_provider_fixtures['docs_file_metadata'] - src_path = WaterButlerPath('/unsure.txt', _ids=('0', item['id'])) - - with pytest.raises(exceptions.IntraCopyError) as e: - await provider.intra_copy(provider, src_path, src_path) - assert e.value.code == 409 - with pytest.raises(exceptions.IntraCopyError) as e: - await provider.intra_move(provider, src_path, src_path) +class TestOperationsOrMisc: - assert e.value.code == 409 + def test_will_self_overwrite(self, provider, other_provider): + src_path = GoogleDrivePath('/root/Gear1.stl', _ids=['0', '10', '11']) + dest_path = GoogleDrivePath('/root/Gear23123.stl', _ids=['0', '10', '12']) + result = provider.will_self_overwrite(other_provider, src_path, dest_path) + assert result is False -class TestOperationsOrMisc: + result = provider.will_self_overwrite(other_provider, src_path, src_path) + assert result is True - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test_can_duplicate_names(self, provider): + def test_can_duplicate_names(self, provider): assert provider.can_duplicate_names() is True - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test_shares_storage_root(self, provider, other_provider): + def test_shares_storage_root(self, provider, other_provider): assert provider.shares_storage_root(other_provider) is True assert provider.shares_storage_root(provider) is True - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test_can_intra_move(self, provider, other_provider): + def test_can_intra_move(self, provider, other_provider): assert provider.can_intra_move(other_provider) is False assert provider.can_intra_move(provider) is True - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test__serialize_item_raw(self, provider, root_provider_fixtures): + def test__serialize_item_raw(self, provider, root_provider_fixtures): item = root_provider_fixtures['docs_file_metadata'] assert provider._serialize_item(None, item, True) == item - @pytest.mark.asyncio - @pytest.mark.aiohttpretty - async def test_can_intra_copy(self, provider, other_provider, root_provider_fixtures): + def test_can_intra_copy(self, provider, other_provider, root_provider_fixtures): item = root_provider_fixtures['list_file']['items'][0] path = WaterButlerPath('/birdie.jpg', _ids=(provider.folder['id'], item['id'])) @@ -1654,7 +1639,7 @@ async def test_revalidate_path_file_error(self, provider, root_provider_fixtures body=error_fixtures['parts_file_missing_metadata']) with pytest.raises(exceptions.MetadataError) as e: - result = await provider._resolve_path_to_ids(file_name) + await provider._resolve_path_to_ids(file_name) assert e.value.message == '{} not found'.format(str(path)) assert e.value.code == 404 diff --git a/tests/providers/owncloud/test_provider.py b/tests/providers/owncloud/test_provider.py index c4e8ac521..e9f3ffce4 100644 --- a/tests/providers/owncloud/test_provider.py +++ b/tests/providers/owncloud/test_provider.py @@ -36,6 +36,7 @@ def file_like(file_content): return io.BytesIO(file_content) + @pytest.fixture def file_stream(file_like): return streams.FileStreamReader(file_like) @@ -321,6 +322,7 @@ async def test_intra_copy_file(self, provider, file_metadata): assert metadata.name == 'dissertation.aux' assert metadata.kind == 'file' + class TestMetadata: @pytest.mark.asyncio @@ -359,6 +361,18 @@ async def test_revisions(self, provider, file_metadata): class TestOperations: + def test_will_self_overwrite(self, provider): + src_path = WaterButlerPath('/50 shades of nope.txt', + _ids=(provider.folder, '12231')) + dest_path = WaterButlerPath('/50 shades of nope2223.txt', + _ids=(provider.folder, '2342sdfsd')) + + result = provider.will_self_overwrite(provider, src_path, dest_path) + assert result is False + + result = provider.will_self_overwrite(provider, src_path, src_path) + assert result is True + def test_can_intra_copy(self, provider, provider_different_credentials): assert provider.can_intra_copy(provider) assert not provider.can_intra_copy(provider_different_credentials) diff --git a/waterbutler/core/provider.py b/waterbutler/core/provider.py index ba443e33f..789349e44 100644 --- a/waterbutler/core/provider.py +++ b/waterbutler/core/provider.py @@ -224,6 +224,18 @@ async def move(self, }) if handle_naming: + # In handle_naming so we don't run it multiple times + # This does not mean the `dest_path` is a folder, at this point it could just be the parent + # of the actual dest_path, or have a blank name + if not dest_path.is_file: + temp_path = await self.revalidate_path( + dest_path, + rename or src_path.name, + folder=src_path.is_dir + ) + if self.will_self_overwrite(dest_provider, src_path, temp_path): + raise exceptions.OverwriteSelfError(src_path) + dest_path = await dest_provider.handle_naming( src_path, dest_path, @@ -269,7 +281,29 @@ async def copy(self, 'conflict': conflict, 'got_rename': rename is not None, }) + + if not dest_path.is_file: + temp_path = await self.revalidate_path( + dest_path, + rename or src_path.name, + folder=src_path.is_dir + ) + if self.will_self_overwrite(dest_provider, src_path, temp_path): + raise exceptions.OverwriteSelfError(src_path) + if handle_naming: + # In handle_naming so we don't run it multiple times + # This does not mean the `dest_path` is a folder, at this point it could just be the parent + # of the actual dest_path, or have a blank name + if not dest_path.is_file: + temp_path = await self.revalidate_path( + dest_path, + rename or src_path.name, + folder=src_path.is_dir + ) + if self.will_self_overwrite(dest_provider, src_path, temp_path): + raise exceptions.OverwriteSelfError(src_path) + dest_path = await dest_provider.handle_naming( src_path, dest_path, @@ -393,10 +427,15 @@ async def handle_naming(self, :rtype: :class:`.WaterButlerPath` """ + + # This function can either take a file path, or a parent path to make a file path out of. + if src_path.is_dir and dest_path.is_file: # Cant copy a directory to a file raise ValueError('Destination must be a directory if the source is') + # This is confusing. `dest_path` at this point can refer to the parent or root of + # the file we want to move/copy. So even if moving/copying a file, this code will run if not dest_path.is_file: # Directories always are going to be copied into # cp /folder1/ /folder2/ -> /folder1/folder2/ @@ -410,6 +449,22 @@ async def handle_naming(self, return dest_path + def will_self_overwrite(self, + dest_provider: 'BaseProvider', + src_path: wb_path.WaterButlerPath, + dest_path: wb_path.WaterButlerPath) -> bool: + """ Return wether a move or copy operation will result in a self-overwrite. + + .. note:: + Defaults to False + + :param dest_provider: ( :class:`.BaseProvider` ) The provider to check against + :param src_path: ( :class:`.WaterButlerPath` ) The move/copy source path + :param dest_path: ( :class:`.WaterButlerPath` ) The move/copy destination path + :rtype: :class:`bool` + """ + return False + def can_intra_copy(self, other: 'BaseProvider', path: wb_path.WaterButlerPath=None) -> bool: diff --git a/waterbutler/providers/box/provider.py b/waterbutler/providers/box/provider.py index b55aa4779..a8096661b 100644 --- a/waterbutler/providers/box/provider.py +++ b/waterbutler/providers/box/provider.py @@ -182,6 +182,9 @@ def shares_storage_root(self, other: provider.BaseProvider) -> bool: Add a comparison of credentials to avoid this.""" return super().shares_storage_root(other) and self.credentials == other.credentials + def will_self_overwrite(self, dest_provider, src_path, dest_path): + return self.NAME == dest_provider.NAME and src_path.identifier == dest_path.identifier + def can_intra_move(self, other: provider.BaseProvider, path: wb_path.WaterButlerPath=None) -> bool: return self == other @@ -196,10 +199,6 @@ async def intra_copy(self, # type: ignore dest_path: wb_path.WaterButlerPath) \ -> typing.Tuple[typing.Union[BoxFileMetadata, BoxFolderMetadata], bool]: - if src_path.identifier == dest_path.identifier: - raise exceptions.IntraCopyError("Cannot overwrite a file with itself", - code=HTTPStatus.CONFLICT) - if dest_path.identifier is not None: await dest_provider.delete(dest_path) @@ -229,10 +228,6 @@ async def intra_move(self, # type: ignore src_path: wb_path.WaterButlerPath, dest_path: wb_path.WaterButlerPath) -> typing.Tuple[BaseBoxMetadata, bool]: - if src_path.identifier == dest_path.identifier: - raise exceptions.IntraCopyError("Cannot overwrite a file with itself", - code=HTTPStatus.CONFLICT) - if dest_path.identifier is not None and str(dest_path).lower() != str(src_path).lower(): await dest_provider.delete(dest_path) diff --git a/waterbutler/providers/dropbox/provider.py b/waterbutler/providers/dropbox/provider.py index 034f73774..40cdb1435 100644 --- a/waterbutler/providers/dropbox/provider.py +++ b/waterbutler/providers/dropbox/provider.py @@ -152,10 +152,6 @@ async def intra_copy(self, # type: ignore -> typing.Tuple[typing.Union[DropboxFileMetadata, DropboxFolderMetadata], bool]: dest_folder = dest_provider.folder - if dest_path.full_path == src_path.full_path: - raise exceptions.IntraCopyError("Cannot overwrite a file with itself", - code=HTTPStatus.CONFLICT) - try: if self == dest_provider: data = await self.dropbox_request( @@ -198,10 +194,6 @@ async def intra_move(self, # type: ignore src_path: WaterButlerPath, dest_path: WaterButlerPath) -> typing.Tuple[BaseDropboxMetadata, bool]: - if dest_path.full_path == src_path.full_path: - raise exceptions.IntraCopyError("Cannot overwrite a file with itself", - code=HTTPStatus.CONFLICT) - 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( @@ -380,6 +372,9 @@ async def create_folder(self, path: WaterButlerPath, **kwargs) -> DropboxFolderM ) return DropboxFolderMetadata(data, self.folder) + def will_self_overwrite(self, dest_provider, src_path, dest_path): + return self.NAME == dest_provider.NAME and dest_path.full_path == src_path.full_path + def can_intra_copy(self, dest_provider: provider.BaseProvider, path: WaterButlerPath=None) -> bool: return type(self) == type(dest_provider) diff --git a/waterbutler/providers/googledrive/provider.py b/waterbutler/providers/googledrive/provider.py index 607dc5e07..92d3b56c2 100644 --- a/waterbutler/providers/googledrive/provider.py +++ b/waterbutler/providers/googledrive/provider.py @@ -135,6 +135,9 @@ def can_duplicate_names(self) -> bool: def default_headers(self) -> dict: return {'authorization': 'Bearer {}'.format(self.token)} + def will_self_overwrite(self, dest_provider, src_path, dest_path): + return self.NAME == dest_provider.NAME and src_path.identifier == dest_path.identifier + def can_intra_move(self, other: provider.BaseProvider, path=None) -> bool: @@ -152,10 +155,6 @@ async def intra_move(self, # type: ignore dest_path: wb_path.WaterButlerPath) \ -> typing.Tuple[BaseGoogleDriveMetadata, bool]: - if src_path.identifier == dest_path.identifier: - raise exceptions.IntraCopyError("Cannot overwrite a file with itself", - code=HTTPStatus.CONFLICT) - self.metrics.add('intra_move.destination_exists', dest_path.identifier is not None) if dest_path.identifier: await dest_provider.delete(dest_path) @@ -193,10 +192,6 @@ async def intra_copy(self, dest_path: wb_path.WaterButlerPath) \ -> typing.Tuple[GoogleDriveFileMetadata, bool]: - if src_path.identifier == dest_path.identifier: - raise exceptions.IntraCopyError("Cannot overwrite a file with itself", - code=HTTPStatus.CONFLICT) - self.metrics.add('intra_copy.destination_exists', dest_path.identifier is not None) if dest_path.identifier: await dest_provider.delete(dest_path) diff --git a/waterbutler/providers/owncloud/provider.py b/waterbutler/providers/owncloud/provider.py index 81947dcf9..d183129c1 100644 --- a/waterbutler/providers/owncloud/provider.py +++ b/waterbutler/providers/owncloud/provider.py @@ -1,5 +1,4 @@ import aiohttp -from http import HTTPStatus from waterbutler.core import streams from waterbutler.core import provider @@ -267,6 +266,9 @@ async def create_folder(self, path, **kwargs): def can_duplicate_names(self): return True + def will_self_overwrite(self, dest_provider, src_path, dest_path): + return self.NAME == dest_provider.NAME and src_path.identifier == dest_path.identifier + def can_intra_copy(self, dest_provider, path=None): return self == dest_provider @@ -292,10 +294,6 @@ async def _do_dav_move_copy(self, src_path, dest_path, operation): if operation != 'MOVE' and operation != 'COPY': raise NotImplementedError("ownCloud move/copy only supports MOVE and COPY endpoints") - if src_path.full_path == dest_path.full_path: - raise exceptions.IntraCopyError("Cannot overwrite a file with itself", - code=HTTPStatus.CONFLICT) - resp = await self.make_request( operation, self._webdav_url_ + src_path.full_path,