def test_colander_bound_schemas(self): dummy_request = {'x-foo': 'version_a'} a_schema = CorniceSchema.from_colander(ToBoundSchema) field = a_schema.get_attributes(request=dummy_request)[3] self.assertEqual(field.validator.choices, ['a', 'b']) other_dummy_request = {'x-foo': 'bazinga!'} b_schema = CorniceSchema.from_colander(ToBoundSchema) field = b_schema.get_attributes(request=other_dummy_request)[3] self.assertEqual(field.validator.choices, ['c', 'd'])
def test_colander_bound_schemas(self): dummy_request = {"x-foo": "version_a"} a_schema = CorniceSchema.from_colander(ToBoundSchema) field = a_schema.get_attributes(request=dummy_request)[3] self.assertEqual(field.validator.choices, ["a", "b"]) other_dummy_request = {"x-foo": "bazinga!"} b_schema = CorniceSchema.from_colander(ToBoundSchema) field = b_schema.get_attributes(request=other_dummy_request)[3] self.assertEqual(field.validator.choices, ["c", "d"])
def test_colander_schema_using_drop(self): """ remove fields from validated data if they deserialize to colander's `drop` object. """ schema = CorniceSchema.from_colander(DropSchema) class MockRequest(object): def __init__(self, body): self.headers = {} self.matchdict = {} self.body = body self.GET = {} self.POST = {} self.validated = {} self.registry = { 'cornice_deserializers': { 'application/json': extract_json_data } } dummy_request = MockRequest('{"bar": "required_data"}') setattr(dummy_request, 'errors', Errors(dummy_request)) validate_colander_schema(schema, dummy_request) self.assertNotIn('foo', dummy_request.validated)
def test_only_mapping_is_accepted(self): schema = CorniceSchema.from_colander(WrongSchema) dummy_request = get_mock_request("", {"foo": "test", "bar": "test"}) self.assertRaises(SchemaError, validate_colander_schema, schema, dummy_request) # We shouldn't accept a MappingSchema if the `typ` has # been set to something else: schema = CorniceSchema.from_colander( MappingSchema( Sequence, SchemaNode(String(), name="foo"), SchemaNode(String(), name="bar"), SchemaNode(String(), name="baz"), ) ) self.assertRaises(SchemaError, validate_colander_schema, schema, dummy_request)
def test_colander_integration(self): schema = CorniceSchema.from_colander(FooBarSchema) body_fields = schema.get_attributes(location="body") qs_fields = schema.get_attributes(location="querystring") self.assertEquals(len(body_fields), 4) self.assertEquals(len(qs_fields), 1)
def test_colander_request_is_not_bound_if_disabled(self): the_schema = CorniceSchema.from_colander(ToBoundSchema, bind_request=False) dummy_request = {'x-foo': 'version_a'} field = the_schema.get_attributes(request=dummy_request)[3] # Deferred are not resolved self.assertEqual(type(field.validator), deferred)
def test_colander_schema_using_defaults(self): """ Schema could contains default values """ schema = CorniceSchema.from_colander(DefaultSchema) dummy_request = get_mock_request("", {"bar": "test"}) validate_colander_schema(schema, dummy_request) qs_fields = schema.get_attributes(location="querystring") errors = dummy_request.errors self.assertEqual(len(errors), 0) self.assertEqual(len(qs_fields), 2) expected = {"foo": "foo", "bar": "test"} self.assertEqual(expected, dummy_request.validated) dummy_request = get_mock_request("", {"bar": "test", "foo": "test"}) validate_colander_schema(schema, dummy_request) qs_fields = schema.get_attributes(location="querystring") errors = dummy_request.errors self.assertEqual(len(errors), 0) self.assertEqual(len(qs_fields), 2) expected = {"foo": "test", "bar": "test"} self.assertEqual(expected, dummy_request.validated)
def test_colander_schema_using_defaults(self): """ Schema could contains default values """ schema = CorniceSchema.from_colander(DefaultSchema) dummy_request = MockRequest('', {'bar': 'test'}) setattr(dummy_request, 'errors', Errors(dummy_request)) validate_colander_schema(schema, dummy_request) qs_fields = schema.get_attributes(location="querystring") errors = dummy_request.errors self.assertEqual(len(errors), 0) self.assertEqual(len(qs_fields), 2) expected = {'foo': 'foo', 'bar': 'test'} self.assertEqual(expected, dummy_request.validated) dummy_request = MockRequest('', {'bar': 'test', 'foo': 'test'}) setattr(dummy_request, 'errors', Errors(dummy_request)) validate_colander_schema(schema, dummy_request) qs_fields = schema.get_attributes(location="querystring") errors = dummy_request.errors self.assertEqual(len(errors), 0) self.assertEqual(len(qs_fields), 2)
def test_imperative_colander_schema(self): # not specifying body should act the same way as specifying it schema = CorniceSchema.from_colander(imperative_schema) body_fields = schema.get_attributes(location="body") qs_fields = schema.get_attributes(location="querystring") self.assertEqual(len(body_fields), 2) self.assertEqual(len(qs_fields), 1)
def test_sequence_with_null(self): # null can be passed to a sequence field schema = CorniceSchema.from_colander(TestNoneSchema) dummy_request = get_mock_request('{"foo": "abc", "bar": null}') validate_colander_schema(schema, dummy_request) self.assertEqual(len(dummy_request.errors), 0) self.assertIsNone(dummy_request.validated['bar'])
def test_colander_inheritance(self): """ support inheritance of colander.Schema introduced in colander 0.9.9 attributes of base-classes with the same name than subclass-attributes get overwritten. """ base_schema = CorniceSchema.from_colander(TestingSchema) inherited_schema = CorniceSchema.from_colander(InheritedSchema) self.assertEquals(len(base_schema.get_attributes()), len(inherited_schema.get_attributes())) foo_filter = lambda x: x.name == "foo" base_foo = filter(foo_filter, base_schema.get_attributes())[0] inherited_foo = filter(foo_filter, inherited_schema.get_attributes())[0] self.assertTrue(base_foo.required) self.assertFalse(inherited_foo.required)
def schema(self): """Resource schema, depending on HTTP verb.""" colander_schema = self.mapping if self.request.method not in self.validate_schema_for: # No-op since payload is not validated against schema colander_schema = colander.MappingSchema(unknown='preserve') return CorniceSchema.from_colander(colander_schema)
def test_validate_colander_schema_can_preserve_unknown_fields(self): schema = CorniceSchema.from_colander(PreserveUnkownSchema) data = json.dumps({"bar": "required_data", "optional": "true"}) dummy_request = get_mock_request(data) validate_colander_schema(schema, dummy_request) self.assertDictEqual(dummy_request.validated, {"bar": "required_data", "optional": "true"}) self.assertEqual(len(dummy_request.errors), 0)
def test_schema_on_resource(self): User.schema = CorniceSchema.from_colander(validationapp.FooBarSchema) result = self.patch("/users/1", status=400).json self.assertEquals( [(e['name'], e['description']) for e in result['errors']], [ ('foo', 'foo is missing'), ('bar', 'bar is missing'), ('yeah', 'yeah is missing'), ])
def test_colander_bound_schema_rebinds_to_new_request(self): dummy_request = {'x-foo': 'version_a'} the_schema = CorniceSchema.from_colander(ToBoundSchema) field = the_schema.get_attributes(request=dummy_request)[3] self.assertEqual(field.validator.choices, ['a', 'b']) other_dummy_request = {'x-foo': 'bazinga!'} field = the_schema.get_attributes(request=other_dummy_request)[3] self.assertEqual(field.validator.choices, ['c', 'd'])
def test_only_mapping_is_accepted(self): schema = CorniceSchema.from_colander(WrongSchema) dummy_request = get_mock_request('', {'foo': 'test', 'bar': 'test'}) self.assertRaises(SchemaError, validate_colander_schema, schema, dummy_request) # We shouldn't accept a MappingSchema if the `typ` has # been set to something else: schema = CorniceSchema.from_colander( MappingSchema( Sequence, SchemaNode(String(), name='foo'), SchemaNode(String(), name='bar'), SchemaNode(String(), name='baz') ) ) self.assertRaises(SchemaError, validate_colander_schema, schema, dummy_request)
def test_extra_params_qs(self): schema = CorniceSchema.from_colander(QsSchema) dummy_request = get_mock_request("", {"foo": "test", "bar": "test"}) validate_colander_schema(schema, dummy_request) errors = dummy_request.errors self.assertEqual(len(errors), 0) expected = {"foo": "test"} self.assertEqual(expected, dummy_request.validated)
def test_colander_schema_with_root_preparer(self): """ Test the preparer run on the root node """ schema = CorniceSchema.from_colander(WithPreparerSchema) dummy_request = MockRequest('{"bar": "not_mama"}') validate_colander_schema(schema, dummy_request) self.assertEqual('mama', dummy_request.validated['bar'])
def test_extra_params_qs(self): schema = CorniceSchema.from_colander(QsSchema) dummy_request = get_mock_request('', {'foo': 'test', 'bar': 'test'}) validate_colander_schema(schema, dummy_request) errors = dummy_request.errors self.assertEqual(len(errors), 0) expected = {'foo': 'test'} self.assertEqual(expected, dummy_request.validated)
def test_schema_on_resource(self): User.schema = CorniceSchema.from_colander( validationapp.FooBarSchema) result = self.patch("/users/1", status=400).json self.assertEquals( [(e['name'], e['description']) for e in result['errors']], [ ('foo', 'foo is missing'), ('bar', 'bar is missing'), ('yeah', 'yeah is missing'), ])
def test_colander_schema_with_root_validator(self): """ Test the preparer run on the root node """ schema = CorniceSchema.from_colander(WithValidatorSchema) dummy_request = MockRequest('{"bar": "heloo"}') with self.assertRaises(Invalid) as e: validate_colander_schema(schema, dummy_request) self.assertEqual({'': 'Bubblicious'}, e.exception.asdict())
def test_extra_params_qs_strict(self): schema = CorniceSchema.from_colander(StrictQsSchema) dummy_request = get_mock_request("", {"foo": "test", "bar": "test"}) validate_colander_schema(schema, dummy_request) errors = dummy_request.errors self.assertEqual(len(errors), 1) self.assertEqual(errors[0], {"description": "bar is not allowed", "location": "querystring", "name": "bar"}) expected = {"foo": "test"} self.assertEqual(expected, dummy_request.validated)
def test_imperative_colander_schema(self): # not specifying body should act the same way as specifying it schema = CorniceSchema.from_colander(imperative_schema) body_fields = schema.get_attributes(location="body") qs_fields = schema.get_attributes(location="querystring") self.assertEqual(len(body_fields), 2) self.assertEqual(len(qs_fields), 1) dummy_request = get_mock_request('{"bar": "some data"}') validate_colander_schema(schema, dummy_request)
def test_colander_schema_using_dotted_names(self): """ Schema could be passed as string in view """ schema = CorniceSchema.from_colander("cornice.tests.schema.AccountSchema") dummy_request = get_mock_request('{"nickname": "john"}') validate_colander_schema(schema, dummy_request) self.assertIn("nickname", dummy_request.validated) self.assertNotIn("city", dummy_request.validated)
def test_extra_params_qs(self): schema = CorniceSchema.from_colander(QsSchema) dummy_request = MockRequest('', {'foo': 'test', 'bar': 'test'}) setattr(dummy_request, 'errors', Errors(dummy_request)) validate_colander_schema(schema, dummy_request) errors = dummy_request.errors self.assertEqual(len(errors), 0) expected = {'foo': 'test'} self.assertEqual(expected, dummy_request.validated)
def test_colander_integration_with_header(self): schema = CorniceSchema.from_colander(TestingSchemaWithHeader) all_fields = schema.get_attributes() body_fields = schema.get_attributes(location="body") qs_fields = schema.get_attributes(location="querystring") header_fields = schema.get_attributes(location="header") self.assertEqual(len(all_fields), 4) self.assertEqual(len(body_fields), 2) self.assertEqual(len(qs_fields), 1) self.assertEqual(len(header_fields), 1)
def test_colander_schema_default_value(self): # apply default value to field if the input for them is # missing schema = CorniceSchema.from_colander(DefaultValueSchema) dummy_request = get_mock_request('{"foo": 5}') validate_colander_schema(schema, dummy_request) self.assertIn('bar', dummy_request.validated) self.assertEqual(len(dummy_request.errors), 0) self.assertEqual(dummy_request.validated['foo'], 5) # default value should be available self.assertEqual(dummy_request.validated['bar'], 10)
def test_validate_colander_schema_can_preserve_unknown_fields(self): schema = CorniceSchema.from_colander(PreserveUnkownSchema) data = json.dumps({"bar": "required_data", "optional": "true"}) dummy_request = get_mock_request(data) validate_colander_schema(schema, dummy_request) self.assertDictEqual(dummy_request.validated, { "bar": "required_data", "optional": "true" }) self.assertEqual(len(dummy_request.errors), 0)
def get_arguments(self, conf=None): """Return a dictionary of arguments. Takes arguments from the :param conf: param and merges it with the arguments passed in the constructor. :param conf: the dictionary to use. """ if conf is None: conf = {} arguments = {} for arg in self.mandatory_arguments: # get the value from the passed conf, then from the instance, then # from the default class settings. arguments[arg] = conf.pop(arg, getattr(self, arg, None)) for arg in self.list_arguments: # rather than overwriting, extend the defined lists if # any. take care of re-creating the lists before appending # items to them, to avoid modifications to the already # existing ones value = list(getattr(self, arg, [])) if arg in conf: value.extend(to_list(conf.pop(arg))) arguments[arg] = value # schema validation handling if 'schema' in conf: arguments['schema'] = ( CorniceSchema.from_colander(conf.pop('schema'))) # Allow custom error handler arguments['error_handler'] = conf.pop('error_handler', getattr(self, 'error_handler', json_error)) # exclude some validators or filters if 'exclude' in conf: for item in to_list(conf.pop('exclude')): for container in arguments['validators'], arguments['filters']: if item in container: container.remove(item) # also include the other key,value pair we don't know anything about arguments.update(conf) # if some keys have been defined service-wide, then we need to add # them to the returned dict. if hasattr(self, 'arguments'): for key, value in self.arguments.items(): if key not in arguments: arguments[key] = value return arguments
def test_colander_schema_using_dotted_names(self): """ Schema could be passed as string in view """ schema = CorniceSchema.from_colander( 'cornice.tests.schema.AccountSchema') dummy_request = get_mock_request('{"nickname": "john"}') validate_colander_schema(schema, dummy_request) self.assertIn('nickname', dummy_request.validated) self.assertNotIn('city', dummy_request.validated)
def get_arguments(self, conf=None): """Return a dictionary of arguments. Takes arguments from the :param conf: param and merges it with the arguments passed in the constructor. :param conf: the dictionary to use. """ if conf is None: conf = {} arguments = {} for arg in self.mandatory_arguments: # get the value from the passed conf, then from the instance, then # from the default class settings. arguments[arg] = conf.pop(arg, getattr(self, arg, None)) for arg in self.list_arguments: # rather than overwriting, extend the defined lists if # any. take care of re-creating the lists before appending # items to them, to avoid modifications to the already # existing ones value = list(getattr(self, arg, [])) if arg in conf: value.extend(to_list(conf.pop(arg))) arguments[arg] = value # schema validation handling if 'schema' in conf: arguments['schema'] = (CorniceSchema.from_colander( conf.pop('schema'))) # Allow custom error handler arguments['error_handler'] = conf.pop( 'error_handler', getattr(self, 'error_handler', json_error)) # exclude some validators or filters if 'exclude' in conf: for item in to_list(conf.pop('exclude')): for container in arguments['validators'], arguments['filters']: if item in container: container.remove(item) # also include the other key,value pair we don't know anything about arguments.update(conf) # if some keys have been defined service-wide, then we need to add # them to the returned dict. if hasattr(self, 'arguments'): for key, value in self.arguments.items(): if key not in arguments: arguments[key] = value return arguments
def test_colander_schema_using_dotted_names(self): """ Schema could be passed as string in view """ schema = CorniceSchema.from_colander( 'cornice.tests.schema.AccountSchema') dummy_request = MockRequest('{"nickname": "john"}') setattr(dummy_request, 'errors', Errors(dummy_request)) validate_colander_schema(schema, dummy_request) self.assertIn('nickname', dummy_request.validated) self.assertNotIn('city', dummy_request.validated)
def test_colander_schema_using_drop(self): """ remove fields from validated data if they deserialize to colander's `drop` object. """ schema = CorniceSchema.from_colander(DropSchema) dummy_request = get_mock_request('{"bar": "required_data"}') validate_colander_schema(schema, dummy_request) self.assertNotIn('foo', dummy_request.validated) self.assertIn('bar', dummy_request.validated) self.assertEqual(len(dummy_request.errors), 0)
def test_extra_params_qs_strict(self): schema = CorniceSchema.from_colander(StrictQsSchema) dummy_request = get_mock_request('', {'foo': 'test', 'bar': 'test'}) validate_colander_schema(schema, dummy_request) errors = dummy_request.errors self.assertEqual(len(errors), 1) self.assertEqual(errors[0], {'description': 'bar is not allowed', 'location': 'querystring', 'name': 'bar'}) expected = {'foo': 'test'} self.assertEqual(expected, dummy_request.validated)
def test_colander_schema_using_drop(self): """ remove fields from validated data if they deserialize to colander's `drop` object. """ schema = CorniceSchema.from_colander(DropSchema) dummy_request = MockRequest('{"bar": "required_data"}') setattr(dummy_request, 'errors', Errors(dummy_request)) validate_colander_schema(schema, dummy_request) self.assertNotIn('foo', dummy_request.validated) self.assertIn('bar', dummy_request.validated)
def schema(self): """Resource schema, depending on HTTP verb. :returns: a :class:`~cornice:cornice.schemas.CorniceSchema` object built from this resource :attr:`mapping <.BaseResource.mapping>`. """ colander_schema = self.mapping if self.request.method not in self.validate_schema_for: # No-op since payload is not validated against schema colander_schema = colander.MappingSchema(unknown='preserve') return CorniceSchema.from_colander(colander_schema, bind_request=False)
def test_extra_params_qs_strict(self): schema = CorniceSchema.from_colander(StrictQsSchema) dummy_request = MockRequest('', {'foo': 'test', 'bar': 'test'}) setattr(dummy_request, 'errors', Errors(dummy_request)) validate_colander_schema(schema, dummy_request) errors = dummy_request.errors self.assertEqual(len(errors), 1) self.assertEqual(errors[0], {'description': 'bar is not allowed', 'location': 'querystring', 'name': 'bar'}) expected = {'foo': 'test'} self.assertEqual(expected, dummy_request.validated)
def validator_from_model(model): validator = Schema(Mapping()) for key, column in model.__table__.columns.items(): if key in _excludes: # skip things like ID that won't be in the body continue missing = colander.required if column.nullable: missing = colander.drop validator.add( SchemaNode(sqla_to_colander_type(column.type), name=key, missing=missing, **sqla_kwargs(column.type))) return CorniceSchema.from_colander(validator)
def test_colander_strict_schema(self): schema = CorniceSchema.from_colander(StrictSchema) dummy_request = MockRequest('''{"bar": "required_data", "foo": "optional_data", "other": "not_wanted_data"}''') setattr(dummy_request, 'errors', Errors(dummy_request)) validate_colander_schema(schema, dummy_request) errors = dummy_request.errors self.assertEqual(len(errors), 1) self.assertEqual(errors[0], {'description': 'other is not allowed', 'location': 'body', 'name': 'other'}) self.assertIn('foo', dummy_request.validated) self.assertIn('bar', dummy_request.validated)
def test_colander_nested_schema(self): schema = CorniceSchema.from_colander(NestedSchema) dummy_request = MockRequest('{"ham": {"bar": "POST"}}', {'egg.bar': 'GET'}) setattr(dummy_request, 'errors', Errors(dummy_request)) validate_colander_schema(schema, dummy_request) qs_fields = schema.get_attributes(location="querystring") errors = dummy_request.errors self.assertEqual(len(errors), 0, errors) self.assertEqual(len(qs_fields), 1) expected = {'egg': {'bar': 'GET'}, 'ham': {'bar': 'POST'}, } self.assertEqual(expected, dummy_request.validated)
def test_colander_schema_using_drop(self): """ remove fields from validated data if they deserialize to colander's `drop` object. """ schema = CorniceSchema.from_colander(DropSchema) class MockRequest(object): def __init__(self, body): self.headers = {} self.matchdict = {} self.body = body self.GET = {} self.POST = {} self.validated = {} dummy_request = MockRequest('{"bar": "required_data"}') setattr(dummy_request, 'errors', Errors(dummy_request)) validate_colander_schema(schema, dummy_request) self.assertNotIn('foo', dummy_request.validated)
def test_get_from_colander(self): schema = CorniceSchema.from_colander(FooBarSchema) attrs = schema.as_dict() self.assertEqual(len(attrs), 6)
def api(self, **kw): """Decorates a function to make it a service. Options can be passed to the decorator. The methods get, post, put and delete are aliases to this one, specifying the "request_method" argument for convenience. :param request_method: the request method. Should be one of GET, POST, PUT, DELETE, OPTIONS, HEAD, TRACE or CONNECT :param decorators: A sequence of decorators which should be applied to the view callable before it's returned. Will be applied in order received, i.e. the last decorator in the sequence will be the outermost wrapper. All the constructor options, minus name and path, can be overwritten in here. """ view_wrapper = self.get_view_wrapper(kw) method = kw.get('request_method', 'GET') # default is GET api_kw = self.kw.copy() api_kw.update(kw) # sanitize the keyword arguments if 'renderer' not in api_kw: api_kw['renderer'] = self.renderer if 'validator' in api_kw: msg = "'validator' is deprecated, please use 'validators'" warnings.warn(msg, DeprecationWarning) api_kw['validators'] = api_kw.pop('validator') validators = [] validators.extend(to_list(api_kw.get('validators', []))) validators.extend(DEFAULT_VALIDATORS) filters = [] filters.extend(to_list(api_kw.get('filters', []))) filters.extend(DEFAULT_FILTERS) # excluded validators/filters for item in to_list(api_kw.pop('exclude', [])): for items in validators, filters: if item in items: items.remove(item) if 'schema' in api_kw: schema = CorniceSchema.from_colander(api_kw.pop('schema')) validators.append(validate_colander_schema(schema)) self.schemas[method] = schema api_kw['filters'] = filters api_kw['validators'] = validators def _api(func): _api_kw = api_kw.copy() self.definitions.append(_api_kw) def callback(context, name, ob): config = context.config.with_package(info.module) self._define(config, method) config.add_apidoc((self.route_pattern, method), func, self, **_api_kw) view_kw = _api_kw.copy() for arg in _CORNICE_ARGS: view_kw.pop(arg, None) # method decorators if 'attr' in view_kw: @functools.wraps(getattr(ob, kw['attr'])) def view(request): meth = getattr(ob(request), kw['attr']) return meth() del view_kw['attr'] view = functools.partial(call_service, view, _api_kw) else: view = functools.partial(call_service, ob, _api_kw) # set the module of the partial function setattr(view, '__module__', getattr(ob, '__module__')) # handle accept headers as custom predicates if needed if 'accept' in view_kw: for accept in to_list(view_kw.pop('accept', ())): _view_kw = view_kw.copy() predicates = view_kw.get('custom_predicates', []) if callable(accept): predicates.append( functools.partial(match_accept_header, accept)) _view_kw['custom_predicates'] = predicates else: _view_kw['accept'] = accept config.add_view(view=view, route_name=self.route_name, **_view_kw) else: config.add_view(view=view, route_name=self.route_name, **view_kw) func = view_wrapper(func) info = venusian.attach(func, callback, category='pyramid') if info.scope == 'class': # if the decorator was attached to a method in a class, or # otherwise executed at class scope, we need to set an # 'attr' into the settings if one isn't already in there if 'attr' not in kw: kw['attr'] = func.__name__ kw['_info'] = info.codeinfo # fbo "action_method" return func return _api
def schema(self): if self.request.method in ('POST', 'PUT'): return CorniceSchema.from_colander(self.model.schema) raise AttributeError
def _make_schema(self): from ichnaea.service.search.schema import SearchSchema return CorniceSchema.from_colander(SearchSchema)
def _make_schema(self): from ichnaea.service.submit.schema import SubmitSchema return CorniceSchema.from_colander(SubmitSchema)
def test_colander_request_is_bound_by_default(self): the_schema = CorniceSchema.from_colander(ToBoundSchema) dummy_request = {'x-foo': 'version_a'} field = the_schema.get_attributes(request=dummy_request)[3] # Deferred are resolved self.assertNotEqual(type(field.validator), deferred)