Skip to content
This repository was archived by the owner on Mar 28, 2019. It is now read-only.

Commit 70a2dd0

Browse files
committed
Merge pull request #675 from mozilla-services/post-if-none-match
Do not always return 412 errors when request header `If-None-Match: *`` is sent on ``POST /collection`` (fixes #673)
2 parents dfea05a + 0b51b8c commit 70a2dd0

File tree

2 files changed

+32
-6
lines changed

2 files changed

+32
-6
lines changed

cliquet/resource/__init__.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -276,17 +276,19 @@ def collection_post(self):
276276
Add custom behaviour by overriding
277277
:meth:`cliquet.resource.UserResource.process_record`
278278
"""
279-
self._raise_412_if_modified()
279+
existing = None
280280
new_record = self.request.validated['data']
281-
282-
# Since ``id`` does not belong to schema, it can only be found in body.
283281
try:
284282
id_field = self.model.id_field
283+
# Since ``id`` does not belong to schema, look up in body.
285284
new_record[id_field] = _id = self.request.json['data'][id_field]
286285
self._raise_400_if_invalid_id(_id)
287-
except (KeyError, ValueError):
286+
existing = self._get_record_or_404(_id)
287+
except (HTTPNotFound, KeyError, ValueError):
288288
pass
289289

290+
self._raise_412_if_modified(record=existing)
291+
290292
new_record = self.process_record(new_record)
291293
try:
292294
unique_fields = self.mapping.get_option('unique_fields')
@@ -753,7 +755,7 @@ def _raise_412_if_modified(self, record=None):
753755

754756
if_match = decode_header(if_match) if if_match else None
755757

756-
if if_none_match and decode_header(if_none_match) == '*':
758+
if record and if_none_match and decode_header(if_none_match) == '*':
757759
modified_since = -1 # Always raise.
758760
elif if_match:
759761
try:

cliquet/tests/resource/test_preconditions.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ def test_412_on_single_record_has_last_modified_timestamp(self):
129129
self.assertIsNotNone(error.headers.get('Last-Modified'))
130130

131131
def test_create_returns_412_if_changed_meanwhile(self):
132+
self.resource.request.validated = {'data': {'field': 'new'}}
132133
self.assertRaises(httpexceptions.HTTPPreconditionFailed,
133134
self.resource.collection_post)
134135

@@ -159,16 +160,39 @@ def test_put_returns_412_if_deleted_meanwhile(self):
159160
self.assertRaises(httpexceptions.HTTPPreconditionFailed,
160161
self.resource.put)
161162

162-
def test_if_none_match_star_fails_if_record_exists(self):
163+
def test_put_if_none_match_star_fails_if_record_exists(self):
163164
self.resource.request.headers.pop('If-Match')
164165
self.resource.request.headers['If-None-Match'] = '*'
165166
self.resource.record_id = self.stored['id']
166167
self.assertRaises(httpexceptions.HTTPPreconditionFailed,
167168
self.resource.put)
169+
170+
def test_put_if_none_match_star_succeeds_if_record_does_not_exist(self):
171+
self.resource.request.headers.pop('If-Match')
172+
self.resource.request.headers['If-None-Match'] = '*'
168173
self.resource.request.validated = {'data': {'field': 'new'}}
169174
self.resource.record_id = self.resource.model.id_generator()
170175
self.resource.put() # not raising.
171176

177+
def test_post_if_none_match_star_fails_if_record_exists(self):
178+
self.resource.request.headers.pop('If-Match')
179+
self.resource.request.headers['If-None-Match'] = '*'
180+
self.resource.request.json = self.resource.request.validated = {
181+
'data': {
182+
'id': self.stored['id'],
183+
'field': 'new'}}
184+
self.assertRaises(httpexceptions.HTTPPreconditionFailed,
185+
self.resource.collection_post)
186+
187+
def test_post_if_none_match_star_succeeds_if_record_does_not_exist(self):
188+
self.resource.request.headers.pop('If-Match')
189+
self.resource.request.headers['If-None-Match'] = '*'
190+
self.resource.request.validated = {
191+
'data': {
192+
'id': self.resource.model.id_generator(),
193+
'field': 'new'}}
194+
self.resource.collection_post() # not raising.
195+
172196
def test_patch_returns_412_if_changed_meanwhile(self):
173197
self.resource.record_id = self.stored['id']
174198
self.assertRaises(httpexceptions.HTTPPreconditionFailed,

0 commit comments

Comments
 (0)