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

Commit

Permalink
Merge pull request #65 from postatum/94732304_jsonschema_compliance
Browse files Browse the repository at this point in the history
Parse new compliant JSON schemas draft 4
  • Loading branch information
jstoiko committed Aug 16, 2015
2 parents 3f89510 + a0640cc commit cecd412
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 51 deletions.
27 changes: 15 additions & 12 deletions ramses/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,27 +97,29 @@ def generate_model_cls(schema, model_name, raml_resource, es_based=True):
base_cls = engine.ESBaseDocument if es_based else engine.BaseDocument
model_name = str(model_name)
metaclass = type(base_cls)
auth_model = schema.get('auth_model', False)
auth_model = schema.get('_auth_model', False)
if auth_model:
bases = (AuthModelMethodsMixin, base_cls)
else:
bases = (base_cls,)
attrs = {
'__tablename__': model_name.lower(),
'_public_fields': schema.get('public_fields') or [],
'_auth_fields': schema.get('auth_fields') or [],
'_nested_relationships': schema.get('nested_relationships') or [],
'_public_fields': schema.get('_public_fields') or [],
'_auth_fields': schema.get('_auth_fields') or [],
'_nested_relationships': schema.get('_nested_relationships') or [],
}
# Generate fields from properties
properties = schema.get('properties', {})
for field_name, props in properties.items():
if field_name in attrs:
continue

field_kwargs = {
'required': bool(props.get('required'))
}
field_kwargs.update(props.get('args', {}) or {})
db_settings = props.get('_db_settings')
if db_settings is None:
continue

field_kwargs = db_settings.copy()
field_kwargs['required'] = bool(field_kwargs.get('required'))

for default_attr_key in ('default', 'onupdate'):
value = field_kwargs.get(default_attr_key)
Expand All @@ -133,11 +135,12 @@ def generate_model_cls(schema, model_name, raml_resource, es_based=True):
field_kwargs[processor_key] = [
resolve_to_callable(name) for name in processors]

raml_type = (props.get('type', 'string') or 'string').lower()
if raml_type not in type_fields:
raise ValueError('Unknown type: {}'.format(raml_type))
type_name = (
field_kwargs.pop('type', 'string') or 'string').lower()
if type_name not in type_fields:
raise ValueError('Unknown type: {}'.format(type_name))

field_cls = type_fields[raml_type]
field_cls = type_fields[type_name]

if field_cls is engine.Relationship:
prepare_relationship(
Expand Down
24 changes: 16 additions & 8 deletions ramses/scaffolds/ramses_starter/items.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
{
"type": "object",
"title": "Item schema",
"$schema": "http://json-schema.org/draft-03/schema",
"$schema": "http://json-schema.org/draft-04/schema",
"required": ["id", "name"],
"properties": {
"id": {
"required": true,
"type": "id_field",
"args": {
"type": ["integer", "null"],
"_db_settings": {
"required": true,
"type": "id_field",
"primary_key": true
}
},
"name": {
"required": true,
"type": "string"
"type": "string",
"_db_settings": {
"type": "string",
"required": true
}
},
"description": {
"required": false,
"type": "text"
"type": ["string", "null"],
"_db_settings": {
"type": "text",
"required": false
}
}
}
}
13 changes: 8 additions & 5 deletions ramses/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,10 @@ def attr_subresource(raml_resource, route_name):
return False
schema = resource_schema(static_parent) or {}
properties = schema.get('properties', {})
return (route_name in properties and
properties[route_name]['type'] in ('dict', 'list'))
if route_name in properties:
db_settings = properties[route_name].get('_db_settings', {})
return db_settings.get('type') in ('dict', 'list')
return False


def singular_subresource(raml_resource, route_name):
Expand All @@ -238,9 +240,10 @@ def singular_subresource(raml_resource, route_name):
properties = schema.get('properties', {})
if route_name not in properties:
return False
is_obj = properties[route_name]['type'] == 'relationship'
args = properties[route_name].get('args', {})
single_obj = not args.get('uselist', True)

db_settings = properties[route_name].get('_db_settings', {})
is_obj = db_settings.get('type') == 'relationship'
single_obj = not db_settings.get('uselist', True)
return is_obj and single_obj


Expand Down
57 changes: 37 additions & 20 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,10 @@ class TestGenerateModelCls(object):
def _test_schema(self):
return {
'properties': {},
'auth_model': False,
'public_fields': ['public_field1'],
'auth_fields': ['auth_field1'],
'nested_relationships': ['nested_field1']
'_auth_model': False,
'_public_fields': ['public_field1'],
'_auth_fields': ['auth_field1'],
'_nested_relationships': ['nested_field1']
}

@patch('ramses.models.resolve_to_callable')
Expand All @@ -138,9 +138,9 @@ def test_simple_case(self, mock_res, mock_reg):
models.engine.FloatField.reset_mock()
schema = self._test_schema()
schema['properties']['progress'] = {
"required": True,
"type": "float",
"args": {
"_db_settings": {
"type": "float",
"required": True,
"default": 0,
"before_validation": ["zoo"],
"after_validation": ["foo"],
Expand Down Expand Up @@ -175,8 +175,10 @@ def test_callable_default(self, mock_res, mock_reg):
models.engine.FloatField.reset_mock()
schema = self._test_schema()
schema['properties']['progress'] = {
"type": "float",
"args": {"default": "{{foobar}}"}
"_db_settings": {
"type": "float",
"default": "{{foobar}}",
}
}
mock_res.return_value = 1
model_cls, auth_model = models.generate_model_cls(
Expand All @@ -189,8 +191,8 @@ def test_auth_model(self, mock_reg):
from nefertari.authentication.models import AuthModelMethodsMixin
from ramses import models
schema = self._test_schema()
schema['properties']['progress'] = {}
schema['auth_model'] = True
schema['properties']['progress'] = {'_db_settings': {}}
schema['_auth_model'] = True
mock_reg.mget.return_value = {'foo': 'bar'}

model_cls, auth_model = models.generate_model_cls(
Expand All @@ -202,7 +204,7 @@ def test_db_based_model(self, mock_reg):
from nefertari.authentication.models import AuthModelMethodsMixin
from ramses import models
schema = self._test_schema()
schema['properties']['progress'] = {}
schema['properties']['progress'] = {'_db_settings': {}}
mock_reg.mget.return_value = {'foo': 'bar'}

model_cls, auth_model = models.generate_model_cls(
Expand All @@ -212,10 +214,22 @@ def test_db_based_model(self, mock_reg):
assert not issubclass(model_cls, models.engine.ESBaseDocument)
assert not issubclass(model_cls, AuthModelMethodsMixin)

def test_no_db_settings(self, mock_reg):
from ramses import models
schema = self._test_schema()
schema['properties']['progress'] = {'type': 'pickle'}
mock_reg.mget.return_value = {'foo': 'bar'}

model_cls, auth_model = models.generate_model_cls(
schema=schema, model_name='Story', raml_resource=None,
es_based=False)
assert not models.engine.PickleField.called

def test_unknown_field_type(self, mock_reg):
from ramses import models
schema = self._test_schema()
schema['properties']['progress'] = {'type': 'foobar'}
schema['properties']['progress'] = {
'_db_settings': {'type': 'foobar'}}
mock_reg.mget.return_value = {'foo': 'bar'}

with pytest.raises(ValueError) as ex:
Expand All @@ -228,8 +242,10 @@ def test_relationship_field(self, mock_prep, mock_reg):
from ramses import models
schema = self._test_schema()
schema['properties']['progress'] = {
'type': 'relationship',
'args': {'document': 'FooBar'}
'_db_settings': {
'type': 'relationship',
'document': 'FooBar',
}
}
mock_reg.mget.return_value = {'foo': 'bar'}
models.generate_model_cls(
Expand All @@ -240,8 +256,8 @@ def test_foreignkey_field(self, mock_reg):
from ramses import models
schema = self._test_schema()
schema['properties']['progress'] = {
"type": "foreign_key",
"args": {
"_db_settings": {
"type": "foreign_key",
"ref_column_type": "string"
}
}
Expand All @@ -255,8 +271,8 @@ def test_list_field(self, mock_reg):
from ramses import models
schema = self._test_schema()
schema['properties']['progress'] = {
"type": "list",
"args": {
"_db_settings": {
"type": "list",
"item_type": "integer"
}
}
Expand All @@ -269,7 +285,8 @@ def test_list_field(self, mock_reg):
def test_duplicate_field_name(self, mock_reg):
from ramses import models
schema = self._test_schema()
schema['properties']['_public_fields'] = {'type': 'interval'}
schema['properties']['_public_fields'] = {
'_db_settings': {'type': 'interval'}}
mock_reg.mget.return_value = {'foo': 'bar'}
models.generate_model_cls(
schema=schema, model_name='Story', raml_resource=1)
Expand Down
22 changes: 16 additions & 6 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,9 @@ def test_attr_subresource_not_attr(self, mock_schema, mock_par):
mock_schema.return_value = {
'properties': {
'route_name': {
'type': 'string'
'_db_settings': {
'type': 'string'
}
}
}
}
Expand All @@ -267,10 +269,14 @@ def test_attr_subresource_dict(self, mock_schema, mock_par):
mock_schema.return_value = {
'properties': {
'route_name': {
'type': 'dict'
'_db_settings': {
'type': 'dict'
}
},
'route_name2': {
'type': 'list'
'_db_settings': {
'type': 'list'
}
}
}
}
Expand Down Expand Up @@ -304,7 +310,9 @@ def test_singular_subresource_not_attr(self, mock_schema, mock_par):
mock_schema.return_value = {
'properties': {
'route_name': {
'type': 'string'
'_db_settings': {
'type': 'string'
}
}
}
}
Expand All @@ -320,8 +328,10 @@ def test_singular_subresource_dict(self, mock_schema, mock_par):
mock_schema.return_value = {
'properties': {
'route_name': {
'type': 'relationship',
'args': {'uselist': False}
'_db_settings': {
'type': 'relationship',
'uselist': False
}
},
}
}
Expand Down

0 comments on commit cecd412

Please sign in to comment.