Class: Praxis::ResponseDefinition
- Inherits:
-
Object
- Object
- Praxis::ResponseDefinition
- Defined in:
- lib/praxis/response_definition.rb
Overview
Response spec DSL container
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
Instance Method Summary collapse
- #_describe_header(data) ⇒ Object
- #describe(context: nil) ⇒ Object
- #description(text = nil) ⇒ Object
- #example(context = nil) ⇒ Object
- #header(name, value, description: nil) ⇒ Object
- #headers ⇒ Object
-
#initialize(response_name, **spec, &block) ⇒ ResponseDefinition
constructor
A new instance of ResponseDefinition.
- #location(loc = nil, description: nil) ⇒ Object
- #media_type(media_type = nil) ⇒ Object
- #parts(proc = nil, like: nil, **args, &block) ⇒ Object
- #status(code = nil) ⇒ Object
- #validate(response, validate_body: false) ⇒ Object
-
#validate_body!(response) ⇒ Object
Validates response body.
-
#validate_content_type!(response) ⇒ Object
Validates Content-Type header and response media type.
-
#validate_headers!(response) ⇒ Object
Validates Headers.
- #validate_parts!(response) ⇒ Object
-
#validate_status!(response) ⇒ Object
Validates Status code.
Constructor Details
#initialize(response_name, **spec, &block) ⇒ ResponseDefinition
Returns a new instance of ResponseDefinition.
9 10 11 12 13 14 15 16 17 |
# 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 |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
7 8 9 |
# File 'lib/praxis/response_definition.rb', line 7 def name @name end |
Instance Method Details
#_describe_header(data) ⇒ Object
138 139 140 141 142 |
# 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 |
#describe(context: nil) ⇒ Object
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# 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 |
#description(text = nil) ⇒ Object
19 20 21 22 23 |
# File 'lib/praxis/response_definition.rb', line 19 def description(text = nil) return @spec[:description] if text.nil? @spec[:description] = text end |
#example(context = nil) ⇒ Object
78 79 80 81 82 83 84 85 |
# 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 |
#header(name, value, description: nil) ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# 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 |
#headers ⇒ Object
54 55 56 |
# File 'lib/praxis/response_definition.rb', line 54 def headers @spec[:headers] end |
#location(loc = nil, description: nil) ⇒ Object
48 49 50 51 52 |
# 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 |
#media_type(media_type = nil) ⇒ Object
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# 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 |
#parts(proc = nil, like: nil, **args, &block) ⇒ Object
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# 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 |
#status(code = nil) ⇒ Object
25 26 27 28 29 |
# File 'lib/praxis/response_definition.rb', line 25 def status(code = nil) return @spec[:status] if code.nil? @spec[:status] = code end |
#validate(response, validate_body: false) ⇒ Object
144 145 146 147 148 149 150 151 |
# 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 |
#validate_body!(response) ⇒ Object
Validates response body
235 236 237 238 239 240 241 242 243 244 245 |
# 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? = "Invalid response body for #{media_type.identifier}." \ "Errors: #{errors.inspect}" raise Exceptions::Validation.new(, errors: errors) end |
#validate_content_type!(response) ⇒ Object
Validates Content-Type header and response media type
219 220 221 222 223 224 225 226 227 228 |
# 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 |
#validate_headers!(response) ⇒ Object
Validates Headers
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# 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 |
#validate_parts!(response) ⇒ Object
247 248 249 250 251 252 253 |
# 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 |
#validate_status!(response) ⇒ Object
Validates Status code
174 175 176 177 178 179 |
# 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 |