class Praxis::ResponseDefinition
Response
spec DSL container
Attributes
Public Class Methods
Source
# File lib/praxis/response_definition.rb, line 9 def initialize(response_name, **spec, &block) raise Exceptions::InvalidConfiguration, 'Response name is required for a response specification' unless response_name @spec = { headers: {} } @name = response_name instance_exec(**spec, &block) if block_given? raise Exceptions::InvalidConfiguration, 'Status code is required for a response specification' if status.nil? end
Public Instance Methods
Source
# File lib/praxis/response_definition.rb, line 138 def _describe_header(data) data_type = data[:value].is_a?(Regexp) ? :regexp : :string data_value = data[:value].is_a?(Regexp) ? data[:value].inspect : data[:value] { value: data_value, type: data_type } end
Source
# File lib/praxis/response_definition.rb, line 87 def describe(context: nil) content = { description: description, status: status, headers: {} } headers&.each do |name, value| content[:headers][name] = _describe_header(value) end content[:location] = content[:headers]['Location'] if media_type payload = media_type.describe(true) if (example_payload = example(context)) payload[:examples] = {} rendered_payload = example_payload.dump # FIXME: remove load when when MediaTypeCommon.identifier returns a MediaTypeIdentifier identifier = MediaTypeIdentifier.load(media_type.identifier) default_handlers = ApiDefinition.instance.info.produces handlers = Praxis::Application.instance.handlers.select do |k, _v| default_handlers.include?(k) end if identifier && (handler = handlers[identifier.handler_name]) payload[:examples][identifier.handler_name] = { content_type: identifier.to_s, body: handler.generate(rendered_payload) } else handlers.each do |name, handler_class| content_type = identifier ? identifier + name : "application/#{name}" payload[:examples][name] = { content_type: content_type.to_s, body: handler_class.generate(rendered_payload) } end end end content[:payload] = { type: payload } end content[:parts_like] = parts.describe unless parts.nil? content end
Source
# File lib/praxis/response_definition.rb, line 19 def description(text = nil) return @spec[:description] if text.nil? @spec[:description] = text end
Source
# File lib/praxis/response_definition.rb, line 78 def example(context = nil) return nil if media_type.nil? return nil if media_type.is_a?(SimpleMediaType) context = "#{media_type.name}-#{name}" if context.nil? media_type.example(context) end
Source
# File lib/praxis/response_definition.rb, line 58 def header(name, value, description: nil) the_type, args = case value when nil, String [String, {}] when Regexp # A regexp means it's gonna be a String typed, attached to a regexp [String, { regexp: value }] else raise Exceptions::InvalidConfiguration, 'A header definition for a response can only take String, Regexp or nil values (to match anything).' \ "Received the following value for header name #{name}: #{value}" end info = { value: value, attribute: Attributor::Attribute.new(the_type, **args) } info[:description] = description if description @spec[:headers][name] = info end
Source
# File lib/praxis/response_definition.rb, line 48 def location(loc = nil, description: nil) return headers.dig('Location', :value) if loc.nil? header('Location', loc, description: description) end
Source
# File lib/praxis/response_definition.rb, line 31 def media_type(media_type = nil) return @spec[:media_type] if media_type.nil? @spec[:media_type] = case media_type when String SimpleMediaType.new(media_type) when Class raise Exceptions::InvalidConfiguration, 'Invalid media_type specification. media_type must be a Praxis::MediaType' unless media_type < Praxis::Types::MediaTypeCommon media_type when SimpleMediaType media_type else raise Exceptions::InvalidConfiguration, 'Invalid media_type specification. media_type must be a String, MediaType or SimpleMediaType' end end
Source
# File lib/praxis/response_definition.rb, line 153 def parts(proc = nil, like: nil, **args, &block) a_proc = proc || block if like.nil? && !a_proc raise ArgumentError, "Parts definition for response #{name} needs a :like argument or a block/proc" unless args.empty? return @parts end raise ArgumentError, "Parts definition for response #{name} does not allow :like and a block simultaneously" if like && a_proc if like template = ApiDefinition.instance.response(like) @parts = template.compile(nil, **args) else # block @parts = Praxis::ResponseDefinition.new('anonymous', **args, &a_proc) end end
Source
# File lib/praxis/response_definition.rb, line 25 def status(code = nil) return @spec[:status] if code.nil? @spec[:status] = code end
Source
# File lib/praxis/response_definition.rb, line 144 def validate(response, validate_body: false) validate_status!(response) validate_headers!(response) validate_content_type!(response) validate_parts!(response) validate_body!(response) if validate_body end
Source
# File lib/praxis/response_definition.rb, line 235 def validate_body!(response) return unless media_type return if media_type.is_a? SimpleMediaType errors = media_type.validate(media_type.load(response.body)) return unless errors.any? message = "Invalid response body for #{media_type.identifier}." \ "Errors: #{errors.inspect}" raise Exceptions::Validation.new(message, errors: errors) end
Validates response body
@param [Object] response
@raise [Exceptions::Validation] When there is a missing required header..
Source
# File lib/praxis/response_definition.rb, line 219 def validate_content_type!(response) return unless media_type response_content_type = response.content_type expected_content_type = Praxis::MediaTypeIdentifier.load(media_type.identifier) return if expected_content_type.match(response_content_type) raise Exceptions::Validation, "Bad Content-Type header. #{response_content_type}" \ " is incompatible with #{expected_content_type} as described in response: #{name}" end
Validates Content-Type header and response media type
@param [Object] response
@raise [Exceptions::Validation] When there is a missing required header
Source
# File lib/praxis/response_definition.rb, line 185 def validate_headers!(response) return unless headers headers.each do |name, value| raise Exceptions::Validation, 'Symbols are not supported in headers' if name.is_a? Symbol raise Exceptions::Validation, "Header #{name.inspect} was required but is missing" unless response.headers.key?(name) errors = value[:attribute].validate(response.headers[name]) raise Exceptions::Validation, "Header #{name.inspect}, with value #{value.inspect} does not match #{response.headers[name]}." unless errors.empty? # case value # when String # if response.headers[name] != value # raise Exceptions::Validation.new( # "Header #{name.inspect}, with value #{value.inspect} does not match #{response.headers[name]}." # ) # end # when Regexp # if response.headers[name] !~ value # raise Exceptions::Validation.new( # "Header #{name.inspect}, with value #{value.inspect} does not match #{response.headers[name].inspect}." # ) # end # end end end
Validates Headers
@raise [Exceptions::Validation] When there is a missing required header..
Source
# File lib/praxis/response_definition.rb, line 247 def validate_parts!(response) return unless parts response.body.each do |part| parts.validate(part) end end
Source
# File lib/praxis/response_definition.rb, line 174 def validate_status!(response) return unless status # Validate status code if defined in the spec raise Exceptions::Validation, format('Invalid response code detected. Response %<name>s dictates status of %<status>s but this response is returning %<response>s.', name: name, status: status.inspect, response: response.status.inspect) if response.status != status end
Validates Status code
@raise [Exceptions::Validation] When response returns an unexpected status.