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

Commit

Permalink
If schema has no mandatory field, body can be empty
Browse files Browse the repository at this point in the history
  • Loading branch information
leplatrem committed Jul 17, 2015
1 parent 589311f commit 51751b3
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 21 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This document describes changes between each past release.
**Bug fixes**

- ``data`` is not mandatory in request body if the resource does not define
any schema (fixes mozilla-services#63)
any schema or if no field is mandatory (fixes mozilla-services/kinto#63)

2.3.1 (2015-07-15)
------------------
Expand Down
12 changes: 11 additions & 1 deletion cliquet/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,18 @@ def get_record_schema(self, resource, method):
# Simply validate that posted body is a mapping.
return colander.MappingSchema(unknown='preserve')

record_mapping = resource.mapping

try:
record_mapping.deserialize({})
is_empty_accepted = True
except colander.Invalid:
is_empty_accepted = False

record_mapping.missing = {} if is_empty_accepted else colander.required

class RecordPayload(colander.MappingSchema):
data = resource.mapping
data = record_mapping

def schema_type(self, **kw):
return colander.Mapping(unknown='raise')
Expand Down
8 changes: 1 addition & 7 deletions cliquet/schema.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import colander
from colander import SchemaNode, String

from cliquet.utils import strip_whitespace, msec_time, classproperty
from cliquet.utils import strip_whitespace, msec_time


class ResourceSchema(colander.MappingSchema):
Expand Down Expand Up @@ -61,12 +61,6 @@ def is_readonly(self, field):
"""
return field in self.get_option("readonly_fields")

@classproperty
def missing(cls):
if cls != ResourceSchema:
return colander.required
return {}

def schema_type(self, **kw):
if self.get_option("preserve_unknown") is True:
unknown = 'preserve'
Expand Down
12 changes: 11 additions & 1 deletion cliquet/tests/resource/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ def test_record_put_on_unexisting_record_is_rejected_if_write_perm(self):
class EmptySchemaTest(BaseWebTest):
collection_url = '/moistures'

def test_accept_empty_body_if_schema_is_empty(self):
def test_accept_empty_body(self):
resp = self.app.post(self.collection_url,
headers=self.headers)
self.assertIn('id', resp.json['data'])
Expand All @@ -283,6 +283,16 @@ def test_data_fields_are_ignored(self):
self.assertNotIn('icq', resp.json['data'])


class OptionalSchemaTest(EmptySchemaTest):
collection_url = '/psilos'

def test_known_fields_are_saved(self):
resp = self.app.post_json(self.collection_url,
{'data': {'edible': False}},
headers=self.headers)
self.assertIn('edible', resp.json['data'])


class InvalidRecordTest(BaseWebTest):
def setUp(self):
super(InvalidRecordTest, self).setUp()
Expand Down
10 changes: 10 additions & 0 deletions cliquet/tests/testapp/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,13 @@ class Toadstool(resource.ProtectedResource):
class Moisture(resource.ProtectedResource):
# Empty schema.
pass


class PsilocybinSchema(resource.ResourceSchema):
# Optional fields.
edible = colander.SchemaNode(colander.Boolean(), missing=True)


@resource.register()
class Psilo(resource.ProtectedResource):
mapping = PsilocybinSchema()
11 changes: 0 additions & 11 deletions cliquet/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,14 +229,3 @@ def decode_header(value, encoding='utf-8'):
if type(value) == six.binary_type:
value = value.decode(encoding)
return value


class classproperty(object):
"""Decorator to defined a class property.
Source: http://stackoverflow.com/a/5192374
"""
def __init__(self, f):
self.f = f

def __get__(self, obj, owner):
return self.f(owner)

0 comments on commit 51751b3

Please sign in to comment.