explicit 0.2.12 → 0.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 954701c8ac0b7cd387f9dd6129739a59c624390c56c73baa507340aca73093de
4
- data.tar.gz: 5372f09b33652971b8df685721d7c8a93782054ac8bc0a91a67b1c3cbf1bbf47
3
+ metadata.gz: d263cb4a6086cfbfce9e14e8b982891f58a2037457ee07138d7cdd4d7d811e87
4
+ data.tar.gz: 31302fbc977b6f96d586e453d5d7dfa9708e9ae02423c69a2c2052f23f85cc09
5
5
  SHA512:
6
- metadata.gz: 47e20235b39777a2c42a192ca6e1a060d336b94808afdb825204e947bc511bda25cc2c8e58a3c830f768c245d8b760d159829f525adaff01e22e6c758db57e42
7
- data.tar.gz: 0d65d5a475676aa645bd757fbc38f05ea97ab64476cf113f31e28a375f2494f45dd4ddf1391cd84f3d4cffbb96e054f3e620bc6a7569558ff2fa1ac92364dc27
6
+ metadata.gz: 7683a050bc830faa883055fec094ffa5304fb2938071c8cc6cb5a4b289a98cb87ba6e965b299bef09895a2687fd3389672351b26975fa59616f2b8d136a003cb
7
+ data.tar.gz: 71a80ef0f2e7259af2d217e6ef6e8f6995ee84d30fd65dbec521eeb7afd4d901374b5a5ec09d3e5a57238990bac629dd34f9311e5af5105c98438d3e6f0098e5
data/README.md CHANGED
@@ -41,6 +41,7 @@ documented types at runtime.
41
41
  - [Changing examples file path](#changing-examples-file-path)
42
42
  - [Customizing error messages](#customizing-error-messages)
43
43
  - [Customizing error serialization](#customizing-error-serialization)
44
+ - [Raise on invalid example](#raise-on-invalid-example)
44
45
 
45
46
  # Installation
46
47
 
@@ -62,10 +63,13 @@ available:
62
63
  - `title(text)` - Adds a title to the request. Displayed in documentation.
63
64
  - `description(text)` - Adds a description to the endpoint. Displayed in
64
65
  documentation. Markdown supported.
65
- - `header(name, type)` - Adds a type to the request header.
66
+ - `header(name, type, options = {})` - Adds a type to the request header.
67
+ - The following options are available:
68
+ - `auth: :bearer` - Sets the authentication to [bearer](https://swagger.io/docs/specification/v3_0/authentication/bearer-authentication/).
69
+ - `auth: :basic` - Sets the authentication to [basic](https://swagger.io/docs/specification/v3_0/authentication/basic-authentication/).
66
70
  - `param(name, type, options = {})` - Adds a type to the request param.
67
71
  It works for params in the request body, query string and path params.
68
- - The following conveniences are available via options:
72
+ - The following options are available:
69
73
  - `optional: true` - Makes the param nilable.
70
74
  - `default: value` - Sets a default value to the param, which makes it optional.
71
75
  - `description: "text"` - Adds a documentation to the param. Markdown supported.
@@ -146,7 +150,7 @@ AuthenticatedRequest = Explicit::Request.new do
146
150
  base_url "https://my-app.com"
147
151
  base_path "/api/v1"
148
152
 
149
- header "Authorization", [:string, format: /Bearer [a-zA-Z0-9]{20}/]
153
+ header "Authorization", [:string, format: /Bearer [a-zA-Z0-9]{20}/], auth: :bearer
150
154
 
151
155
  response 403, { error: "unauthorized" }
152
156
  end
@@ -699,3 +703,10 @@ class ApplicationController < ActionController::API
699
703
  end
700
704
  end
701
705
  ```
706
+
707
+ ### Raise on invalid example
708
+
709
+ ```ruby
710
+ config.raise_on_invalid_example = true # default is false
711
+ config.raise_on_invalid_example = ::Rails.env.development? # recommended
712
+ ```
@@ -28,6 +28,14 @@ module Explicit
28
28
  @rescue_from_invalid_params
29
29
  end
30
30
 
31
+ def raise_on_invalid_example=(enabled)
32
+ @raise_on_invalid_example = enabled
33
+ end
34
+
35
+ def raise_on_invalid_example?
36
+ @raise_on_invalid_example
37
+ end
38
+
31
39
  def test_runner=(test_runner)
32
40
  @test_runner = test_runner
33
41
  end
@@ -48,4 +56,4 @@ module Explicit
48
56
  def configure(&block)
49
57
  block.call(@configuration)
50
58
  end
51
- end
59
+ end
@@ -152,7 +152,18 @@ module Explicit::Documentation::Output
152
152
  }
153
153
  end
154
154
 
155
- headers + path_params
155
+ query_params =
156
+ request.params_type.query_params_type.attributes.map do |name, type|
157
+ {
158
+ name: name.to_s,
159
+ in: "query",
160
+ required: type.required?,
161
+ schema: type.swagger_schema,
162
+ style: "simple"
163
+ }
164
+ end
165
+
166
+ headers + path_params + query_params
156
167
  end
157
168
 
158
169
  def build_request_body(request)
@@ -14,7 +14,7 @@ module Explicit::Documentation
14
14
  end
15
15
 
16
16
  engine.define_singleton_method(:documentation_builder) { builder }
17
-
17
+
18
18
  engine.routes.draw do
19
19
  get "/", to: builder.webpage, as: :explicit_documentation_webpage
20
20
  get "/swagger", to: builder.swagger, as: :explicit_documentation_swagger
@@ -6,10 +6,11 @@ class Explicit::Request
6
6
  route = request.routes.first
7
7
  method = route.method.to_s.upcase
8
8
  path = route.replace_path_params(params)
9
- url = "#{request.get_base_url}#{request.get_base_path}#{path}"
10
- curl_request = "curl -X#{method} \"#{url}\""
9
+ body_params = params.slice(*request.params_type.body_params_type.attributes.keys)
10
+ query_params = params.slice(*request.params_type.query_params_type.attributes.keys)
11
11
 
12
- body_params = params.except(*route.params)
12
+ url = "#{request.get_base_url}#{request.get_base_path}#{path}#{query_params.present? ? "?#{query_params.to_query}" : ""}"
13
+ curl_request = "curl -X#{method} \"#{url}\""
13
14
 
14
15
  curl_headers =
15
16
  if body_params.empty?
@@ -37,7 +38,7 @@ class Explicit::Request
37
38
  curl_non_file_params = non_file_params.to_query.split("&").map do |key_value|
38
39
  "-F \"#{CGI.unescape(key_value).gsub('"', '\"')}\""
39
40
  end
40
-
41
+
41
42
  curl_file_params = file_params.map do |name, _|
42
43
  "-F #{name}=\"#{body_params[name]}\""
43
44
  end
@@ -12,6 +12,10 @@ class Explicit::Request
12
12
  end
13
13
  end
14
14
 
15
+ def accepts_request_body?
16
+ method == :post || method == :put || method == :patch
17
+ end
18
+
15
19
  def replace_path_params(values)
16
20
  values.reduce(path) do |acc_path, (key, value)|
17
21
  acc_path.gsub(":#{key}", value.to_s)
@@ -24,4 +28,4 @@ class Explicit::Request
24
28
  end
25
29
  end
26
30
  end
27
- end
31
+ end
@@ -68,9 +68,13 @@ class Explicit::Request
68
68
  def description(markdown) = (@description = markdown)
69
69
  def get_description = @description
70
70
 
71
- def header(name, type)
71
+ def header(name, type, **options)
72
72
  raise ArgumentError("duplicated header #{name}") if @headers.key?(name)
73
73
 
74
+ if (auth_type = options[:auth])
75
+ type = [ :_auth_type, auth_type, type ]
76
+ end
77
+
74
78
  @headers[name] = type
75
79
  end
76
80
 
@@ -91,6 +95,10 @@ class Explicit::Request
91
95
 
92
96
  if @routes.first&.params&.include?(name)
93
97
  type = [ :_param_location, :path, type ]
98
+ elsif @routes.first&.accepts_request_body?
99
+ type = [ :_param_location, :body, type ]
100
+ else
101
+ type = [ :_param_location, :query, type ]
94
102
  end
95
103
 
96
104
  @params[name] = type
@@ -109,8 +117,15 @@ class Explicit::Request
109
117
  response = Response.new(status:, data:)
110
118
 
111
119
  case responses_type(status:).validate(data)
112
- in [:ok, _] then nil
113
- in [:error, err] then raise InvalidResponseError.new(response, err)
120
+ in [:ok, _]
121
+ nil
122
+
123
+ in [:error, err]
124
+ if ::Explicit.configuration.raise_on_invalid_example?
125
+ raise InvalidResponseError.new(response, err)
126
+ else
127
+ ::Rails.logger.error("[Explicit] Invalid response for #{gid} with status #{status}: #{err}")
128
+ end
114
129
  end
115
130
 
116
131
  @examples[response.status] << Example.new(request: self, params:, headers:, response:)
@@ -152,13 +167,13 @@ class Explicit::Request
152
167
  def requires_basic_authorization?
153
168
  authorization = headers_type.attributes["Authorization"]
154
169
 
155
- authorization&.format&.to_s&.include?("Basic")
170
+ authorization&.auth_basic? || authorization&.format&.to_s&.include?("Basic")
156
171
  end
157
172
 
158
173
  def requires_bearer_authorization?
159
174
  authorization = headers_type.attributes["Authorization"]
160
175
 
161
- authorization&.format&.to_s&.include?("Bearer")
176
+ authorization&.auth_bearer? || authorization&.format&.to_s&.include?("Bearer")
162
177
  end
163
178
 
164
179
  private
@@ -69,7 +69,7 @@ module Explicit::TestHelper
69
69
  raise <<~TXT
70
70
  Unexpected HTML response:
71
71
 
72
- #{html.text}
72
+ #{html.text.gsub(/^\s+$\n/, '')}
73
73
  TXT
74
74
  end
75
75
 
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Explicit::Type::Modifiers::AuthType
4
+ extend self
5
+
6
+ def apply(auth_type, type)
7
+ Explicit::Type.build(type).tap do |type|
8
+ type.auth_type = auth_type
9
+ end
10
+ end
11
+ end
@@ -41,9 +41,17 @@ class Explicit::Type::Record < Explicit::Type
41
41
  self.class.new(attributes: path_params)
42
42
  end
43
43
 
44
+ def query_params_type
45
+ query_params = @attributes.filter do |name, type|
46
+ type.param_location_query?
47
+ end
48
+
49
+ self.class.new(attributes: query_params)
50
+ end
51
+
44
52
  def body_params_type
45
53
  body_params = @attributes.filter do |name, type|
46
- !type.param_location_path?
54
+ type.param_location_body?
47
55
  end
48
56
 
49
57
  self.class.new(attributes: body_params)
data/lib/explicit/type.rb CHANGED
@@ -99,17 +99,36 @@ class Explicit::Type
99
99
  in [:nilable, type]
100
100
  Explicit::Type::Modifiers::Nilable.apply(type)
101
101
 
102
+ in [:_auth_type, auth_type, type]
103
+ Explicit::Type::Modifiers::AuthType.apply(auth_type, type)
104
+
102
105
  in [:_param_location, param_location, type]
103
106
  Explicit::Type::Modifiers::ParamLocation.apply(param_location, type)
104
107
  end
105
108
  end
106
109
 
107
- attr_accessor :description, :default, :nilable, :param_location
110
+ attr_accessor :description, :default, :nilable, :param_location, :auth_type
108
111
 
109
112
  def param_location_path?
110
113
  param_location == :path
111
114
  end
112
115
 
116
+ def param_location_query?
117
+ param_location == :query
118
+ end
119
+
120
+ def param_location_body?
121
+ param_location == :body
122
+ end
123
+
124
+ def auth_basic?
125
+ auth_type == :basic
126
+ end
127
+
128
+ def auth_bearer?
129
+ auth_type == :bearer
130
+ end
131
+
113
132
  def required?
114
133
  !nilable
115
134
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Explicit
4
- VERSION = "0.2.12"
4
+ VERSION = "0.2.13"
5
5
  end
data/lib/explicit.rb CHANGED
@@ -44,6 +44,7 @@ require "explicit/type/one_of"
44
44
  require "explicit/type/record"
45
45
  require "explicit/type/string"
46
46
 
47
+ require "explicit/type/modifiers/auth_type"
47
48
  require "explicit/type/modifiers/default"
48
49
  require "explicit/type/modifiers/description"
49
50
  require "explicit/type/modifiers/nilable"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: explicit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.12
4
+ version: 0.2.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luiz Vasconcellos
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-30 00:00:00.000000000 Z
11
+ date: 2025-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -111,6 +111,7 @@ files:
111
111
  - lib/explicit/type/hash.rb
112
112
  - lib/explicit/type/integer.rb
113
113
  - lib/explicit/type/literal.rb
114
+ - lib/explicit/type/modifiers/auth_type.rb
114
115
  - lib/explicit/type/modifiers/default.rb
115
116
  - lib/explicit/type/modifiers/description.rb
116
117
  - lib/explicit/type/modifiers/nilable.rb