def _validate_collection(self, value, custom_formatters=None): if self.items is None: raise UndefinedItemsSchema(self.type) if self.min_items is not None: if self.min_items < 0: raise OpenAPISchemaError("Schema for collection invalid:" " minItems must be non-negative") if len(value) < self.min_items: raise InvalidSchemaValue( "Value must contain at least {type} item(s)," " {value} found", len(value), self.min_items) if self.max_items is not None: if self.max_items < 0: raise OpenAPISchemaError("Schema for collection invalid:" " maxItems must be non-negative") if len(value) > self.max_items: raise InvalidSchemaValue( "Value must contain at most {value} item(s)," " {type} found", len(value), self.max_items) if self.unique_items and len(set(value)) != len(value): raise OpenAPISchemaError("Value may not contain duplicate items") f = functools.partial(self.items.validate, custom_formatters=custom_formatters) return list(map(f, value))
def cast(self, value, custom_formatters=None, strict=True, require_all_props=False): """Cast value to schema type""" if value is None: if not self.nullable: raise InvalidSchemaValue("Null value for non-nullable schema", value, self.type) return self.default if self.enum and value not in self.enum: raise InvalidSchemaValue( "Value {value} not in enum choices: {type}", value, self.enum) cast_mapping = self.get_cast_mapping( custom_formatters=custom_formatters, strict=strict, require_all_props=require_all_props) if self.type is not SchemaType.STRING and value == '': return None cast_callable = cast_mapping[self.type] try: return cast_callable(value) except UnmarshallerStrictTypeError: raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type) except ValueError: raise InvalidSchemaValue( "Failed to cast value {value} to type {type}", value, self.type)
def _validate_number(self, value, custom_formatters=None): if self.minimum is not None: if self.exclusive_minimum and value <= self.minimum: raise InvalidSchemaValue( "Value {value} is not less than or equal to {type}", value, self.minimum) elif value < self.minimum: raise InvalidSchemaValue( "Value {value} is not less than {type}", value, self.minimum) if self.maximum is not None: if self.exclusive_maximum and value >= self.maximum: raise InvalidSchemaValue( "Value {value} is not greater than or equal to {type}", value, self.maximum) elif value > self.maximum: raise InvalidSchemaValue( "Value {value} is not greater than {type}", value, self.maximum) if self.multiple_of is not None and value % self.multiple_of: raise InvalidSchemaValue( "Value {value} is not a multiple of {type}", value, self.multiple_of)
def unmarshal(self, value, custom_formatters=None, strict=True): """Unmarshal parameter from the value.""" if self.deprecated: warnings.warn("The schema is deprecated", DeprecationWarning) if value is None: if not self.nullable: raise InvalidSchemaValue("Null value for non-nullable schema", value, self.type) return self.default if self.enum and value not in self.enum: raise InvalidSchemaValue( "Value {value} not in enum choices: {type}", value, self.enum) unmarshal_mapping = self.get_unmarshal_mapping( custom_formatters=custom_formatters, strict=strict) if self.type is not SchemaType.STRING and value == '': return None unmarshal_callable = unmarshal_mapping[self.type] try: unmarshalled = unmarshal_callable(value) except UnmarshallerStrictTypeError: raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type) except ValueError: raise InvalidSchemaValue( "Failed to unmarshal value {value} to type {type}", value, self.type) return unmarshalled
def unmarshal(self, value, custom_formatters=None): """Unmarshal parameter from the value.""" if self.deprecated: warnings.warn("The schema is deprecated", DeprecationWarning) casted = self.cast(value, custom_formatters=custom_formatters) if casted is None and not self.required: return None if self.enum and casted not in self.enum: raise InvalidSchemaValue( "Value {value} not in enum choices: {type}", value, self.enum) valid_casted_types_by_original_type = { str: (string_types, bytes), bytes: string_types, int: int, float: float, dict: Model, string_types: bytes, } is_string_and_format_datetime = self.format in ("date", "date-time") and isinstance(casted, (date, datetime)) if not (value is None \ or isinstance(casted, type(value)) \ or is_string_and_format_datetime \ or isinstance(casted, valid_casted_types_by_original_type[type(value)])): raise InvalidSchemaValue("Input {value} not valid for type {type}", value, self.type) return casted
def _validate_number(self, value): if self.minimum is not None: if value < self.minimum: 'The :attribute value :input is not between :min - :max.', raise InvalidSchemaValue( "The attribute value {0} is less than {1}".format( value, self.minimum)) if self.maximum is not None: if value > self.maximum: raise InvalidSchemaValue( "The attribute value {0} is greater than {1}".format( value, self.maximum)) return
def _validate_object(self, value, custom_formatters=None): properties = value.__dict__ if self.one_of: valid_one_of_schema = None for one_of_schema in self.one_of: try: self._validate_properties( properties, one_of_schema, custom_formatters=custom_formatters) except OpenAPISchemaError: pass else: if valid_one_of_schema is not None: raise MultipleOneOfSchema(self.type) valid_one_of_schema = True if valid_one_of_schema is None: raise NoOneOfSchema(self.type) else: self._validate_properties(properties, custom_formatters=custom_formatters) if self.min_properties is not None: if self.min_properties < 0: raise OpenAPISchemaError( "Schema for object invalid:" " minProperties must be non-negative" ) if len(properties) < self.min_properties: raise InvalidSchemaValue( "Value must contain at least {type} properties," " {value} found", len(properties), self.min_properties ) if self.max_properties is not None: if self.max_properties < 0: raise OpenAPISchemaError( "Schema for object invalid:" " maxProperties must be non-negative" ) if len(properties) > self.max_properties: raise InvalidSchemaValue( "Value must contain at most {type} properties," " {value} found", len(properties), self.max_properties ) return True
def validate(self, value): if value is None: if not self.nullable: raise InvalidSchemaValue("Null value for non-nullable schema") return self.default validator = self.VALIDATOR_CALLABLE_GETTER[self.type] if not validator(value): raise InvalidSchemaValue( "Value of {0} not valid type of {1}".format( value, self.type.value)) return value
def _validate_string(self, value, custom_formatters=None): try: schema_format = SchemaFormat(self.format) except ValueError: msg = "Unsupported {0} format validation".format(self.format) if custom_formatters is not None: formatstring = custom_formatters.get(self.format) if formatstring is None: raise OpenAPISchemaError(msg) else: raise OpenAPISchemaError(msg) else: formatstring =\ self.STRING_FORMAT_CALLABLE_GETTER[schema_format] if not formatstring.validate(value): raise InvalidSchemaValue( "Value {value} not valid format {type}", value, self.format) if self.min_length is not None: if self.min_length < 0: raise OpenAPISchemaError( "Schema for string invalid:" " minLength must be non-negative" ) if len(value) < self.min_length: raise InvalidSchemaValue( "Value is shorter ({value}) than the minimum length of {type}", len(value), self.min_length ) if self.max_length is not None: if self.max_length < 0: raise OpenAPISchemaError( "Schema for string invalid:" " maxLength must be non-negative" ) if len(value) > self.max_length: raise InvalidSchemaValue( "Value is longer ({value}) than the maximum length of {type}", len(value), self.max_length ) if self.pattern is not None and not self.pattern.search(value): raise InvalidSchemaValue( "Value {value} does not match the pattern {type}", value, self.pattern.pattern ) return True
def __call__(self, value, type_format=SchemaFormat.NONE, strict=True, require_all_props=False): value = super(PrimitiveTypeUnmarshaller, self).__call__(value, type_format=type_format, strict=strict, require_all_props=require_all_props) try: schema_format = SchemaFormat(type_format) except ValueError: formatter = self.custom_formatters.get(type_format) else: formatters = self.get_formatters() formatter = formatters.get(schema_format) if formatter is None: raise InvalidSchemaValue( "Unsupported format {type} unmarshalling " "for value {value}", value, type_format) try: return formatter(value) except ValueError as exc: raise InvalidCustomFormatSchemaValue( "Failed to format value {value} to format {type}: {exception}", value, type_format, exc)
def _unmarshal_object(self, value, model_factory=None, custom_formatters=None): if not isinstance(value, (dict, )): raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type) model_factory = model_factory or ModelFactory() if self.one_of: properties = None for one_of_schema in self.one_of: try: found_props = self._unmarshal_properties( value, one_of_schema, custom_formatters=custom_formatters) except OpenAPISchemaError: pass else: if properties is not None: raise MultipleOneOfSchema(self.type) properties = found_props if properties is None: raise NoOneOfSchema(self.type) else: properties = self._unmarshal_properties( value, custom_formatters=custom_formatters) return model_factory.create(properties, name=self.model)
def _unmarshal_object(self, value): if not isinstance(value, (dict, )): raise InvalidSchemaValue( "Value of {0} not an object".format(value)) if self.one_of: properties = None for one_of_schema in self.one_of: try: found_props = self._unmarshal_properties( value, one_of_schema) except OpenAPISchemaError: pass else: if properties is not None: raise MultipleOneOfSchema( "Exactly one schema should be valid," "multiple found") properties = found_props if properties is None: raise NoOneOfSchema( "Exactly one valid schema should be valid, None found.") else: properties = self._unmarshal_properties(value) return ModelFactory().create(properties, name=self.model)
def _unmarshal_object(self, value, model_factory=None, custom_formatters=None, strict=True): if not isinstance(value, (dict, )): raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type) model_factory = model_factory or ModelFactory() if self.one_of: properties = None for one_of_schema in self.one_of: try: unmarshalled = self._unmarshal_properties( value, one_of_schema, custom_formatters=custom_formatters) except OpenAPISchemaError: pass else: if properties is not None: log.warning("multiple valid oneOf schemas found") continue properties = unmarshalled if properties is None: log.warning("valid oneOf schema not found") else: properties = self._unmarshal_properties( value, custom_formatters=custom_formatters) return model_factory.create(properties, name=self.model)
def _unmarshal_string(self, value): formatter = self.FORMAT_CALLABLE_GETTER[self.format] try: return formatter(value) except ValueError: raise InvalidSchemaValue( "Failed to format value of {0} to {1}".format( value, self.format))
def unmarshal(self, value): """Unmarshal parameter from the value.""" if self.deprecated: warnings.warn("The schema is deprecated", DeprecationWarning) if self.in_request_body and self.read_only: raise InvalidSchemaValue("Value is readonly!".format(value)) casted = self.cast(value) if casted is None and not self.required: return None if self.enum and casted not in self.enum: raise InvalidSchemaValue( "Value of {0} not in enum choices: {1}".format( value, self.enum)) return casted
def validate(self, value, resolver=None): validator = self.get_validator(resolver=resolver) try: return validator.validate(value) except ValidationError: # TODO: pass validation errors raise InvalidSchemaValue("Value not valid for schema", value, self.type)
def validate(self, value, resolver=None): validator = self.get_validator(resolver=resolver) try: return validator.validate(value) except ValidationError: errors_iter = validator.iter_errors(value) raise InvalidSchemaValue(value, self.type, schema_errors_iter=errors_iter)
def cast(self, value, custom_formatters=None): """Cast value to schema type""" if value is None: if not self.nullable: raise InvalidSchemaValue("Null value for non-nullable schema", value, self.type) return self.default cast_mapping = self.get_cast_mapping(custom_formatters=custom_formatters) if self.type is not SchemaType.STRING and value == '': return None cast_callable = cast_mapping[self.type] try: return cast_callable(value) except ValueError: raise InvalidSchemaValue( "Failed to cast value {value} to type {type}", value, self.type)
def cast(self, value): """Cast value to schema type""" if value is None: if not self.nullable: raise InvalidSchemaValue("Null value for non-nullable schema") return self.default cast_mapping = self.get_cast_mapping() if self.type is not SchemaType.STRING and value == '': return None cast_callable = cast_mapping[self.type] try: return cast_callable(value) except ValueError: raise InvalidSchemaValue( "Failed to cast value of {0} to {1}".format(value, self.type))
def validate(self, value, custom_formatters=None): if value is None: if not self.nullable: raise InvalidSchemaValue("Null value for non-nullable schema of type {type}", value, self.type) return # type validation type_validator_callable = self.TYPE_VALIDATOR_CALLABLE_GETTER[ self.type] if not type_validator_callable(value): raise InvalidSchemaValue( "Value {value} not valid type {type}", value, self.type.value) # structure validation validator_mapping = self.get_validator_mapping() validator_callable = validator_mapping[self.type] validator_callable(value, custom_formatters=custom_formatters) return value
def test_get_pets_invalid_response(self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets' query_params = { 'limit': '20', } request = MockRequest( host_url, 'GET', '/pets', path_pattern=path_pattern, args=query_params, ) parameters = request.get_parameters(spec) body = request.get_body(spec) assert parameters == { 'query': { 'limit': 20, 'page': 1, 'search': '', } } assert body is None data_json = { 'data': [ { 'id': 1, 'name': { 'first_name': 'Cat', }, } ], } data = json.dumps(data_json) response = MockResponse(data) response_result = response_validator.validate(request, response) assert response_result.errors == [ InvalidMediaTypeValue( original_exception=InvalidSchemaProperty( property_name='data', original_exception=InvalidSchemaProperty( property_name='name', original_exception=InvalidSchemaValue( msg="Value {value} is not of type {type}", type=SchemaType.STRING, value={'first_name': 'Cat'}, ), ), ), ), ] assert response_result.data is None
def validate(self, value): if value is None: if not self.nullable: raise InvalidSchemaValue("Null value for non-nullable schema") return # type validation type_validator_callable = self.TYPE_VALIDATOR_CALLABLE_GETTER[ self.type] if not type_validator_callable(value): raise InvalidSchemaValue( "Value of {0} not valid type of {1}".format( value, self.type.value)) # structure validation validator_mapping = self.get_validator_mapping() validator_callable = validator_mapping[self.type] validator_callable(value) return value
def _unmarshal_string(self, value, custom_formatters=None): try: schema_format = SchemaFormat(self.format) except ValueError: msg = "Unsupported format {type} unmarshalling for value {value}" if custom_formatters is not None: formatstring = custom_formatters.get(self.format) if formatstring is None: raise InvalidSchemaValue(msg, value, self.format) else: raise InvalidSchemaValue(msg, value, self.format) else: formatstring = self.STRING_FORMAT_CALLABLE_GETTER[schema_format] try: return formatstring.unmarshal(value) except ValueError as exc: raise InvalidCustomFormatSchemaValue( "Failed to format value {value} to format {type}: {exception}", value, self.format, exc)
def _unmarshal_collection(self, value, custom_formatters=None, strict=True): if not isinstance(value, (list, tuple)): raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type) if self.items is None: raise UndefinedItemsSchema(self.type) f = functools.partial( self.items.unmarshal, custom_formatters=custom_formatters, strict=strict, ) return list(map(f, value))
def _unmarshal_number(self, value, custom_formatters=None, strict=True): if strict and not isinstance(value, (float, ) + integer_types): raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type) try: schema_format = SchemaFormat(self.format) except ValueError: msg = "Unsupported format {type} unmarshalling for value {value}" if custom_formatters is not None: formatnumber = custom_formatters.get(self.format) if formatnumber is None: raise InvalidSchemaValue(msg, value, self.format) else: raise InvalidSchemaValue(msg, value, self.format) else: formatnumber = self.NUMBER_FORMAT_CALLABLE_GETTER[schema_format] try: return formatnumber.unmarshal(value) except ValueError as exc: raise InvalidCustomFormatSchemaValue( "Failed to format value {value} to format {type}: {exception}", value, self.format, exc)
def cast(self, value): """Cast value from string to schema type""" if value is None: return value cast_mapping = self.get_cast_mapping() cast_callable = cast_mapping[self.type] try: return cast_callable(value) except ValueError: raise InvalidSchemaValue( "Failed to cast value {value} to type {type}", value, self.type)
def unmarshal(self, value, custom_formatters=None): """Unmarshal parameter from the value.""" if self.deprecated: warnings.warn("The schema is deprecated", DeprecationWarning) casted = self.cast(value, custom_formatters=custom_formatters) if casted is None and not self.required: return None if self.enum and casted not in self.enum: raise InvalidSchemaValue( "Value {value} not in enum choices: {type}", value, self.enum) return casted
def test_get_pets_invalid_response(self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets' query_params = { 'limit': '20', } request = MockRequest( host_url, 'GET', '/pets', path_pattern=path_pattern, args=query_params, ) parameters = validate_parameters(spec, request) body = validate_body(spec, request) assert parameters == RequestParameters(query={ 'limit': 20, 'page': 1, 'search': '', }) assert body is None data_json = { 'data': [{ 'id': 1, 'name': { 'first_name': 'Cat', }, }], } data = json.dumps(data_json) response = MockResponse(data) response_result = response_validator.validate(request, response) original_exc = response_result.errors[0].original_exception assert response_result.errors == [ InvalidMediaTypeValue(original_exception=InvalidSchemaValue( type=SchemaType.OBJECT, value=data_json, schema_errors=original_exc.schema_errors, schema_errors_iter=original_exc._schema_errors_iter, ), ), ] assert response_result.data is None
def _unmarshal_string(self, value): try: schema_format = SchemaFormat(self.format) except ValueError: # @todo: implement custom format unmarshalling support raise OpenAPISchemaError( "Unsupported {0} format unmarshalling".format(self.format)) else: formatter = self.STRING_FORMAT_CAST_CALLABLE_GETTER[schema_format] try: return formatter(value) except ValueError: raise InvalidSchemaValue( "Failed to format value of {0} to {1}".format( value, self.format))
def _validate_string(self, value): try: schema_format = SchemaFormat(self.format) except ValueError: # @todo: implement custom format validation support raise OpenAPISchemaError( "Unsupported {0} format validation".format(self.format)) else: format_validator_callable =\ self.STRING_FORMAT_VALIDATOR_CALLABLE_GETTER[schema_format] if not format_validator_callable(value): raise InvalidSchemaValue( "Value of {0} not valid format of {1}".format( value, self.format)) return True