Class: Explicit::Request
- Inherits:
-
Object
- Object
- Explicit::Request
- Defined in:
- lib/explicit/request/route.rb,
lib/explicit/request/example.rb,
lib/explicit/request/response.rb,
lib/explicit/request.rb
Defined Under Namespace
Classes: InvalidParamsError, InvalidResponseError
Constant Summary collapse
- Route =
::Data.define(:method, :path) do def to_s "#{method.to_s.upcase} #{path}" end def params path.split("/").filter_map do |part| part[1..-1].to_sym if part.start_with?(":") end end def accepts_request_body? method == :post || method == :put || method == :patch end def replace_path_params(values) values.reduce(path) do |acc_path, (key, value)| acc_path.gsub(":#{key}", value.to_s) end end def path_with_curly_syntax params.reduce(path) do |acc_path, param| acc_path.gsub(":#{param}", "{#{param}}") end end end
- Example =
::Data.define(:request, :params, :headers, :response) do def to_curl_lines route = request.routes.first method = route.method.to_s.upcase path = route.replace_path_params(params) body_params = params.slice(*request.params_type.body_params_type.attributes.keys) query_params = params.slice(*request.params_type.query_params_type.attributes.keys) url = "#{request.get_base_url}#{request.get_base_path}#{path}#{query_params.present? ? "?#{query_params.to_query}" : ""}" curl_request = "curl -X#{method} \"#{url}\"" curl_headers = if body_params.empty? [] elsif request.accepts_file_upload? ['-H "Content-Type: multipart/form-data"'] else ['-H "Content-Type: application/json"'] end headers.each do |key, value| curl_headers << "-H \"#{key}: #{value}\"" end curl_body = if body_params.empty? [] elsif request.accepts_file_upload? file_params = request.params_type.attributes.filter do |name, type| type.is_a?(Explicit::Type::File) end.to_h non_file_params = body_params.except(*file_params.keys) curl_non_file_params = non_file_params.to_query.split("&").map do |key_value| "-F \"#{CGI.unescape(key_value).gsub('"', '\"')}\"" end curl_file_params = file_params.map do |name, _| "-F #{name}=\"#{body_params[name]}\"" end curl_non_file_params.concat(curl_file_params) else # https://stackoverflow.com/questions/34847981/curl-with-multiline-of-json ["-d @- << EOF\n#{JSON.pretty_generate(body_params)}\nEOF"] end [curl_request].concat(curl_headers).concat(curl_body) end end
- Response =
::Data.define(:status, :data) do def dig(...) = data.dig(...) end
Instance Attribute Summary collapse
-
#examples ⇒ Object
readonly
Returns the value of attribute examples.
-
#headers ⇒ Object
readonly
Returns the value of attribute headers.
-
#params ⇒ Object
readonly
Returns the value of attribute params.
-
#responses ⇒ Object
readonly
Returns the value of attribute responses.
-
#routes ⇒ Object
readonly
Returns the value of attribute routes.
Instance Method Summary collapse
- #accepts_file_upload? ⇒ Boolean
- #base_path(prefix) ⇒ Object
- #base_url(url) ⇒ Object
- #custom_authorization_format? ⇒ Boolean
- #delete(path) ⇒ Object
- #description(markdown) ⇒ Object
- #example(params:, response:, headers: {}) ⇒ Object (also: #add_example)
- #get(path) ⇒ Object
- #get_base_path ⇒ Object
- #get_base_url ⇒ Object
- #get_description ⇒ Object
- #get_title ⇒ Object
- #gid ⇒ Object
- #head(path) ⇒ Object
- #header(name, type, **options) ⇒ Object
- #headers_type ⇒ Object
-
#initialize(&block) ⇒ Request
constructor
A new instance of Request.
- #new(&block) ⇒ Object
- #options(path) ⇒ Object
- #param(name, type, **options) ⇒ Object
- #params_type ⇒ Object
- #patch(path) ⇒ Object
- #post(path) ⇒ Object
- #put(path) ⇒ Object
- #requires_basic_authorization? ⇒ Boolean
- #requires_bearer_authorization? ⇒ Boolean
- #response(status, typespec) ⇒ Object
- #responses_type(status:) ⇒ Object
- #title(text) ⇒ Object
- #validate!(values) ⇒ Object
Constructor Details
#initialize(&block) ⇒ Request
Returns a new instance of Request.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/explicit/request.rb', line 6 def initialize(&block) @routes = [] @headers = {} @params = {} @responses = Hash.new { |hash, key| hash[key] = [] } @examples = Hash.new { |hash, key| hash[key] = [] } instance_eval(&block) define_missing_path_params! if Explicit.configuration.rescue_from_invalid_params? && @params.any? @responses[422] << { error: "invalid_params", params: [ :description, "An object containing error messages for all invalid params", [ :hash, :string, :string ] ] } end end |
Instance Attribute Details
#examples ⇒ Object (readonly)
Returns the value of attribute examples.
4 5 6 |
# File 'lib/explicit/request.rb', line 4 def examples @examples end |
#headers ⇒ Object (readonly)
Returns the value of attribute headers.
4 5 6 |
# File 'lib/explicit/request.rb', line 4 def headers @headers end |
#params ⇒ Object (readonly)
Returns the value of attribute params.
4 5 6 |
# File 'lib/explicit/request.rb', line 4 def params @params end |
#responses ⇒ Object (readonly)
Returns the value of attribute responses.
4 5 6 |
# File 'lib/explicit/request.rb', line 4 def responses @responses end |
#routes ⇒ Object (readonly)
Returns the value of attribute routes.
4 5 6 |
# File 'lib/explicit/request.rb', line 4 def routes @routes end |
Instance Method Details
#accepts_file_upload? ⇒ Boolean
179 180 181 182 183 |
# File 'lib/explicit/request.rb', line 179 def accepts_file_upload? params_type.attributes.any? do |name, type| type.is_a?(Explicit::Type::File) end end |
#base_path(prefix) ⇒ Object
62 |
# File 'lib/explicit/request.rb', line 62 def base_path(prefix) = (@base_path = prefix) |
#base_url(url) ⇒ Object
59 |
# File 'lib/explicit/request.rb', line 59 def base_url(url) = (@base_url = url) |
#custom_authorization_format? ⇒ Boolean
197 198 199 |
# File 'lib/explicit/request.rb', line 197 def @headers.key?("Authorization") && ! && ! end |
#delete(path) ⇒ Object
55 |
# File 'lib/explicit/request.rb', line 55 def delete(path) = @routes << Route.new(method: :delete, path:) |
#description(markdown) ⇒ Object
68 |
# File 'lib/explicit/request.rb', line 68 def description(markdown) = (@description = markdown) |
#example(params:, response:, headers: {}) ⇒ Object Also known as: add_example
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/explicit/request.rb', line 144 def example(params:, response:, headers: {}) raise ArgumentError.new("missing :status in response") if !response.key?(:status) raise ArgumentError.new("missing :data in response") if !response.key?(:data) status, data = response.values_at(:status, :data) response = Response.new(status:, data:) case responses_type(status:).validate(data) in [:ok, _] nil in [:error, err] if ::Explicit.configuration.raise_on_invalid_example? raise InvalidResponseError.new(response, err) else ::Rails.logger.error("[Explicit] Invalid response for #{gid} with status #{status}: #{err}") end end @examples[response.status] << Example.new(request: self, params:, headers:, response:) end |
#get(path) ⇒ Object
51 |
# File 'lib/explicit/request.rb', line 51 def get(path) = @routes << Route.new(method: :get, path:) |
#get_base_path ⇒ Object
63 |
# File 'lib/explicit/request.rb', line 63 def get_base_path = @base_path |
#get_base_url ⇒ Object
60 |
# File 'lib/explicit/request.rb', line 60 def get_base_url = @base_url |
#get_description ⇒ Object
69 |
# File 'lib/explicit/request.rb', line 69 def get_description = @description |
#get_title ⇒ Object
66 |
# File 'lib/explicit/request.rb', line 66 def get_title = @title || @routes.first.to_s |
#gid ⇒ Object
175 176 177 |
# File 'lib/explicit/request.rb', line 175 def gid routes.first.to_s end |
#head(path) ⇒ Object
52 |
# File 'lib/explicit/request.rb', line 52 def head(path) = @routes << Route.new(method: :head, path:) |
#header(name, type, **options) ⇒ Object
104 105 106 107 108 109 110 111 112 |
# File 'lib/explicit/request.rb', line 104 def header(name, type, **) raise ArgumentError("duplicated header #{name}") if @headers.key?(name) if (auth_type = [:auth]) type = [ :_auth_type, auth_type, type ] end @headers[name] = type end |
#headers_type ⇒ Object
185 186 187 |
# File 'lib/explicit/request.rb', line 185 def headers_type @headers_type ||= Explicit::Type.build(@headers) end |
#new(&block) ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/explicit/request.rb', line 29 def new(&block) this = self self.class.new do instance_variable_set(:@base_url, this.get_base_url) instance_variable_set(:@base_path, this.get_base_path) instance_variable_set(:@routes, this.routes.dup) instance_variable_set(:@headers, this.headers.dup) instance_variable_set(:@params, this.params.dup) this.responses.each do |status, types| @responses[status] = types.dup end this.examples.each do |status, examples| @examples[status] = examples.dup end instance_eval(&block) end end |
#options(path) ⇒ Object
56 |
# File 'lib/explicit/request.rb', line 56 def (path) = @routes << Route.new(method: :options, path:) |
#param(name, type, **options) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/explicit/request.rb', line 114 def param(name, type, **) raise ArgumentError("duplicated param #{name}") if @params.key?(name) if [:optional] type = [ :nilable, type ] end if (defaultval = [:default]) type = [ :default, defaultval, type ] end if (description = [:description]) type = [ :description, description, type ] end if @routes.first&.params&.include?(name) type = [ :_param_location, :path, type ] elsif @routes.first&.accepts_request_body? type = [ :_param_location, :body, type ] else type = [ :_param_location, :query, type ] end @params[name] = type end |
#params_type ⇒ Object
189 190 191 |
# File 'lib/explicit/request.rb', line 189 def params_type @params_type ||= Explicit::Type.build(@params) end |
#patch(path) ⇒ Object
57 |
# File 'lib/explicit/request.rb', line 57 def patch(path) = @routes << Route.new(method: :patch, path:) |
#post(path) ⇒ Object
53 |
# File 'lib/explicit/request.rb', line 53 def post(path) = @routes << Route.new(method: :post, path:) |
#put(path) ⇒ Object
54 |
# File 'lib/explicit/request.rb', line 54 def put(path) = @routes << Route.new(method: :put, path:) |
#requires_basic_authorization? ⇒ Boolean
201 202 203 204 205 |
# File 'lib/explicit/request.rb', line 201 def = headers_type.attributes["Authorization"] &.auth_basic? || &.format&.to_s&.include?("Basic") end |
#requires_bearer_authorization? ⇒ Boolean
207 208 209 210 211 |
# File 'lib/explicit/request.rb', line 207 def = headers_type.attributes["Authorization"] &.auth_bearer? || &.format&.to_s&.include?("Bearer") end |
#response(status, typespec) ⇒ Object
140 141 142 |
# File 'lib/explicit/request.rb', line 140 def response(status, typespec) @responses[status] << typespec end |
#responses_type(status:) ⇒ Object
193 194 195 |
# File 'lib/explicit/request.rb', line 193 def responses_type(status:) Explicit::Type.build([ :one_of, *@responses[status] ]) end |
#title(text) ⇒ Object
65 |
# File 'lib/explicit/request.rb', line 65 def title(text) = (@title = text) |
#validate!(values) ⇒ Object
168 169 170 171 172 173 |
# File 'lib/explicit/request.rb', line 168 def validate!(values) case params_type.validate(values) in [:ok, validated_data] then validated_data in [:error, err] then raise InvalidParamsError.new(err) end end |