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
Example #2
0
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
Example #3
0
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)
Example #4
0
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
Example #5
0
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
Example #6
0
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
Example #8
0
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,
    )
Example #9
0
def test_true(minimal_swagger_spec):
    prop_spec = {
        'type': 'string',
        'x-nullable': True,
    }
    assert is_prop_nullable(minimal_swagger_spec, prop_spec)
Example #10
0
def test_false_explicit(minimal_swagger_spec):
    prop_spec = {
        'type': 'string',
        'x-nullable': False,
    }
    assert not is_prop_nullable(minimal_swagger_spec, prop_spec)
Example #11
0
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)