def type_validator(swagger_spec, validator, types, instance, schema): """Skip the `type` validator when a Swagger parameter value is None. Otherwise it will fail with a "None is not a valid type" failure instead of letting the downstream `required_validator` do its job. Also skip when a Swagger property value is None and the schema contains the extension field `x-nullable` set to True. In all other cases, delegate to the existing Draft4 `type` validator. :param swagger_spec: needed for access to deref() :type swagger_spec: :class:`bravado_core.spec.Spec` :param validator: Validator class used to validate the object :type validator: :class:`Swagger20Validator` or :class:`jsonschema.validators.Draft4Validator` :param types: validate types :type types: string or list :param instance: object instance value :param schema: swagger spec for the object :type schema: dict """ if (is_param_spec(swagger_spec, schema) or is_prop_nullable(swagger_spec, schema)) and instance is None: return for error in _validators.type_draft4(validator, types, instance, schema): yield error
def enum_validator(swagger_spec, validator, enums, instance, schema): """Swagger 2.0 allows enums to be validated against objects of type arrays, like query parameter (collectionFormat: multi) :param swagger_spec: needed for access to deref() :type swagger_spec: :class:`bravado_core.spec.Spec` :param validator: Validator class used to validate the object :type validator: :class: `Swagger20Validator` or :class: `jsonschema.validators.Draft4Validator` :param enums: allowed enum values :type enums: list :param instance: enum instance value :param schema: swagger spec for the object :type schema: dict """ if is_prop_nullable(swagger_spec, schema) and instance is None: return if schema.get('type') == 'array': for element in instance: for error in _DRAFT4_ENUM_VALIDATOR(validator, enums, element, schema): yield error return # Handle optional enum params with no value if is_param_spec(swagger_spec, schema): if not is_required(swagger_spec, schema) and instance is None: return for error in _DRAFT4_ENUM_VALIDATOR(validator, enums, instance, schema): yield error
def test_ref_false(minimal_swagger_dict): minimal_swagger_dict['definitions'] = { 'Pet': { 'type': 'object', }, } param_spec = {'$ref': '#/definitions/Pet'} swagger_spec = Spec.from_dict(minimal_swagger_dict) assert not is_prop_nullable(swagger_spec, param_spec)
def format_validator(swagger_spec, validator, format, instance, schema): """Skip the `format` validator when a Swagger parameter value is None. Otherwise it will fail with a "Failed validating u'format' in schema" failure instead of letting the downstream `required_validator` do its job. Also skip when a Swagger property value is None and the schema contains the extension field `x-nullable` set to True. In all other cases, delegate to the existing Draft4 `format` validator. """ if (is_param_spec(swagger_spec, schema) or is_prop_nullable(swagger_spec, schema)) and instance is None: return for error in _DRAFT4_FORMAT_VALIDATOR(validator, format, instance, schema): yield error
def handle_null_value(swagger_spec, object_schema, is_nullable=False, is_marshaling_operation=False): # type: (Spec, JSONDict, bool, bool) -> typing.Callable[[FuncType], FuncType] # TODO: remove is_nullable support once https://github.com/Yelp/bravado-core/issues/335 is addressed """ Function wrapper that performs some check to the wrapped function parameter. In case the parameter is None the decorator ensures that, according to object schema, the default value is used or that the null value is properly handled. NOTES: * the decorator is meant to be used in bravado_core.marshal and bravado_core.unmarshal modules. * the decorator could be used as wrapper of functions that accept a single value as parameter. Such value will be used for the checks mentioned above * nullable parameter is needed to ensure that x-nullable is propagated in the case it is defined as sibling in a reference object (ie. `{'x-nullable': True, '$ref': '#/definitions/something'}`) """ default_value = schema.get_default(swagger_spec, object_schema) is_nullable = is_nullable or schema.is_prop_nullable( swagger_spec, object_schema) def external_wrapper(func): # type: (FuncType) -> FuncType @wraps(func) def wrapper(value): # type: (typing.Any) -> typing.Any if value is None: value = default_value if value is None: if is_nullable: return None else: raise SwaggerMappingError( 'Spec {0} is a required value'.format( object_schema), ) elif is_marshaling_operation: return value return func(value) return wrapper return external_wrapper
def marshal_object(swagger_spec, object_spec, object_value): """Marshal a python dict to json dict. :type swagger_spec: :class:`bravado_core.spec.Spec` :type object_spec: dict :type object_value: dict :rtype: dict :raises: SwaggerMappingError """ deref = swagger_spec.deref if object_value is None: return handle_null_value(swagger_spec, object_spec) if not is_dict_like(object_value): raise SwaggerMappingError('Expected dict like type for {0}:{1}'.format( type(object_value), object_value)) object_spec = deref(object_spec) required_fields = object_spec.get('required', []) properties = collapsed_properties(object_spec, swagger_spec) result = {} for k, v in iteritems(object_value): prop_spec = get_spec_for_prop( swagger_spec, object_spec, object_value, k, properties) if not prop_spec: # Don't marshal when a spec is not available - just pass through result[k] = v continue if v is None and k not in required_fields: if not is_prop_nullable(swagger_spec, prop_spec): continue result[k] = marshal_schema_object(swagger_spec, prop_spec, v) return result
def format_validator( swagger_spec, # type: Spec validator, # type: Draft4Validator format, # type: typing.Any instance, # type: typing.Any schema, # type: JSONDict ): # type: (...) -> typing.Generator[ValidationError, None, None] """Skip the `format` validator when a Swagger parameter value is None. Otherwise it will fail with a "Failed validating u'format' in schema" failure instead of letting the downstream `required_validator` do its job. Also skip when a Swagger property value is None and the schema contains the extension field `x-nullable` set to True. In all other cases, delegate to the existing Draft4 `format` validator. """ if instance is None and (is_param_spec(swagger_spec, schema) or is_prop_nullable(swagger_spec, schema)): return for error in _DRAFT4_FORMAT_VALIDATOR(validator, format, instance, schema): yield error
def _marshaling_method_object(swagger_spec, object_schema): # type: (Spec, JSONDict) -> MarshalingMethod """ Determine the marshaling method needed for a schema of a type object. The method will be responsible for the identification of: * required and nullable value for the all properties * marshaling methods of all the properties * marshaling method of the eventual additional properties * polymorphic nature of the object (`discriminator` attribute) and list of the associated models :param swagger_spec: Spec object :param object_schema: Schema of the object type """ model_type = None # type: typing.Optional[typing.Type[Model]] object_schema = swagger_spec.deref(object_schema) if MODEL_MARKER in object_schema: model_name = object_schema[MODEL_MARKER] model_type = swagger_spec.definitions.get(model_name) if model_type is None: return partial( _raise_unknown_model, model_name, ) properties = collapsed_properties(object_schema, swagger_spec) required_properties = set(object_schema.get('required', [])) properties_to_marshaling_function = { prop_name: _get_marshaling_method( swagger_spec=swagger_spec, object_schema=prop_schema, required=prop_name in required_properties, ) for prop_name, prop_schema in iteritems(properties) } additional_properties_marshaling_function = _no_op_marshaling if object_schema.get('additionalProperties') is not False: additional_properties_schema = object_schema.get( 'additionalProperties', {}) if additional_properties_schema not in ({}, True): additional_properties_marshaling_function = _get_marshaling_method( swagger_spec=swagger_spec, object_schema=additional_properties_schema, ) discriminator_property = object_schema.get('discriminator') possible_discriminated_type_name_to_model = {} if model_type and object_schema.get('discriminator'): possible_discriminated_type_name_to_model.update({ k: v for k, v in iteritems(swagger_spec.definitions) if model_type and model_type.__name__ in v._inherits_from }) nullable_properties = { prop_name for prop_name, prop_schema in iteritems(properties) if schema.is_prop_nullable(swagger_spec, prop_schema) } return partial( _marshal_object, swagger_spec, properties_to_marshaling_function, additional_properties_marshaling_function, discriminator_property, possible_discriminated_type_name_to_model, required_properties, nullable_properties, )
def test_true(minimal_swagger_spec): prop_spec = { 'type': 'string', 'x-nullable': True, } assert is_prop_nullable(minimal_swagger_spec, prop_spec)
def test_false_explicit(minimal_swagger_spec): prop_spec = { 'type': 'string', 'x-nullable': False, } assert not is_prop_nullable(minimal_swagger_spec, prop_spec)
def test_false(minimal_swagger_spec): prop_spec = { 'type': 'string', } assert not is_prop_nullable(minimal_swagger_spec, prop_spec)
def discriminator_validator( swagger_spec, # type: Spec validator, # type: Draft4Validator discriminator_attribute, # type: typing.Any instance, # type: typing.Any schema, # type: JSONDict ): # type: (...) -> None """ Validates instance against the schema defined by the discriminator attribute. [Swagger 2.0 Schema Object](http://swagger.io/specification/#schemaObject) allows discriminator field to be defined. discriminator field defines the attribute that will be used to discriminate the object type. NOTE: discriminator_validator assumes that discriminator_attribute is not None or empty :param swagger_spec: needed for access to deref() :type swagger_spec: :class:`bravado_core.spec.Spec` :param validator: Validator class used to validate the object :type validator: :class: `Swagger20Validator` or :class: `jsonschema.validators.Draft4Validator` :param discriminator_attribute: name of the discriminator attribute :type discriminator_attribute: str :param instance: object instance value :type instance: dict :param schema: swagger spec for the object :type schema: dict """ if instance is None and is_prop_nullable(swagger_spec, schema): return try: discriminator_value = instance[discriminator_attribute] except KeyError: raise ValidationError( "'{}' is a required property".format(discriminator_attribute)) if discriminator_value not in swagger_spec.definitions: raise ValidationError( message='\'{}\' is not a recognized schema'.format( discriminator_value), ) if discriminator_value == schema[MODEL_MARKER]: return discriminated_schema = swagger_spec.definitions[ discriminator_value]._model_spec if 'allOf' not in discriminated_schema: raise ValidationError( message='discriminated schema \'{}\' must inherit from \'{}\''. format( discriminator_value, schema[MODEL_MARKER], ), ) schemas_to_remove = [ s for s in discriminated_schema['allOf'] if swagger_spec.deref(s) == schema ] if not schemas_to_remove: # Not checking against len(schemas_to_remove) > 1 because it should be prevented by swagger spec validation raise ValidationError( message='discriminated schema \'{}\' must inherit from \'{}\''. format( discriminator_value, schema[MODEL_MARKER], ), ) # Remove the current schema from the allOf list in order to avoid unbounded recursion # (the current object is already validated against schema) # WARNING: This is especially important if internally_dereference_refs is set to true # as we're modifying new_schema and new_schema is a dict (so mutable) we need to copy # it in order to have a brand new dictionary that we can modify new_schema = discriminated_schema.copy() new_schema['allOf'] = [ all_of_schema if all_of_schema not in schemas_to_remove else {} for all_of_schema in new_schema['allOf'] ] from bravado_core.validate import validate_object # Local import due to circular dependency validate_object(swagger_spec=swagger_spec, object_spec=new_schema, value=instance)