Skip to content

Commit be163a3

Browse files
committed
update 409 Conflict to return metadata of target
* Return metadata about the conflicting object when trying to upload a file with the same name as one that already exists. This metadata includes the update url, allowing the requestor overwrite the existing file without having to make additional queries to get its metadata. If the conflicting object is a folder, we do not return metadata about it. Overwriting a folder with a single request is not a supported action at this time. * Update tests for new behavior.
1 parent 1a027eb commit be163a3

File tree

5 files changed

+28
-15
lines changed

5 files changed

+28
-15
lines changed

tests/providers/onedrive/test_provider.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import io
2+
import json
23
import pytest
34

45
import aiohttpretty
@@ -701,8 +702,8 @@ async def test_upload_exists(self, provider, file_stream, readwrite_fixtures):
701702
file_metadata, created = await provider.upload(file_stream, path, conflict='warn')
702703

703704
assert aiohttpretty.has_call(method='GET', uri=metadata_url)
704-
assert e.value.message == ('Cannot complete action: file or folder "{}" '
705-
'already exists in this location'.format(path.name))
705+
assert json.loads(e.value.message)['message'] == ('Cannot complete action: file or folder "{}" '
706+
'already exists in this location'.format(path.name))
706707

707708
@pytest.mark.aiohttpretty
708709
@pytest.mark.asyncio

tests/server/api/v1/test_create_mixin.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
from http import client
23
from unittest import mock
34

@@ -48,19 +49,19 @@ async def test_postvalidate_put_folder(self, http_request):
4849
])
4950

5051
@pytest.mark.asyncio
51-
async def test_postvalidate_put_folder_naming_conflict(self, http_request):
52+
async def test_postvalidate_put_folder_naming_conflict(self, http_request, mock_file_metadata):
5253

5354
handler = mock_handler(http_request)
5455
handler.path = WaterButlerPath('/Folder1/')
5556
handler.kind = 'folder'
5657
handler.get_query_argument = mock.Mock(return_value='child!')
57-
handler.provider.exists = MockCoroutine(return_value=True)
58+
handler.provider.exists = MockCoroutine(return_value=mock_file_metadata)
5859

5960
with pytest.raises(exceptions.NamingConflict) as exc:
6061
await handler.postvalidate_put()
6162

62-
assert exc.value.message == 'Cannot complete action: file or folder "child!" already ' \
63-
'exists in this location'
63+
assert json.loads(exc.value.message)['message'] == 'Cannot complete action: file or folder ' \
64+
'"child!" already exists in this location'
6465

6566
assert handler.target_path == WaterButlerPath('/Folder1/child!/')
6667
handler.get_query_argument.assert_called_once_with('name', default=None)
@@ -85,20 +86,21 @@ async def test_postvalidate_put_cant_duplicate_names(self, http_request):
8586
handler.provider.can_duplicate_names.assert_called_once_with()
8687

8788
@pytest.mark.asyncio
88-
async def test_postvalidate_put_cant_duplicate_names_and_naming_conflict(self, http_request):
89+
async def test_postvalidate_put_cant_duplicate_names_and_naming_conflict(self, http_request,
90+
mock_file_metadata):
8991

9092
handler = mock_handler(http_request)
9193
handler.path = WaterButlerPath('/Folder1/')
9294
handler.kind = 'folder'
9395
handler.provider.can_duplicate_names = mock.Mock(return_value=False)
9496
handler.get_query_argument = mock.Mock(return_value='child!')
95-
handler.provider.exists = MockCoroutine(side_effect=[False, True])
97+
handler.provider.exists = MockCoroutine(side_effect=[False, mock_file_metadata])
9698

9799
with pytest.raises(exceptions.NamingConflict) as exc:
98100
await handler.postvalidate_put()
99101

100-
assert exc.value.message == 'Cannot complete action: file or folder "child!" already ' \
101-
'exists in this location'
102+
assert json.loads(exc.value.message)['message'] == ('Cannot complete action: file or folder '
103+
'"child!" already exists in this location')
102104

103105
handler.provider.can_duplicate_names.assert_called_once_with()
104106
handler.get_query_argument.assert_called_once_with('name', default=None)

waterbutler/core/exceptions.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,13 @@ def __init__(self, name):
221221

222222

223223
class NamingConflict(ProviderError):
224-
def __init__(self, name):
225-
super().__init__('Cannot complete action: file or folder "{}" already exists in this '
226-
'location'.format(name), code=HTTPStatus.CONFLICT, is_user_error=True)
224+
def __init__(self, name, extant=None):
225+
message = {
226+
'message': 'Cannot complete action: file or folder "{}" already exists in this location'.format(name),
227+
'data': extant,
228+
}
229+
230+
super().__init__(message, code=HTTPStatus.CONFLICT, is_user_error=True)
227231

228232

229233
class ProviderNotFound(ProviderError):

waterbutler/core/provider.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,10 @@ async def handle_name_conflict(self,
653653
if (not exists and not exists == []) or conflict == 'replace':
654654
return path, exists # type: ignore
655655
if conflict == 'warn':
656-
raise exceptions.NamingConflict(path.name)
656+
raise exceptions.NamingConflict(
657+
path.name,
658+
extant={} if type(exists) is list else exists.serialized(),
659+
)
657660

658661
while True:
659662
path.increment_name()

waterbutler/server/api/v1/provider/create.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ async def postvalidate_put(self):
6262

6363
my_type_exists = await self.provider.exists(validated_target_path)
6464
if not isinstance(my_type_exists, bool) or my_type_exists:
65-
raise exceptions.NamingConflict(self.target_path.name)
65+
raise exceptions.NamingConflict(
66+
self.target_path.name,
67+
extant=my_type_exists.json_api_serialized(self.resource),
68+
)
6669

6770
if not self.provider.can_duplicate_names():
6871
target_flipped = self.path.child(self.childs_name, folder=(self.kind != 'folder'))

0 commit comments

Comments
 (0)