explicit 0.2.8 → 0.2.10

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: 6d45270c6ac48802f9d370fb8afe70ec879f761499f589b5a875671161fbebaf
4
- data.tar.gz: 2f2f57d5147f9f2a676c98727c63de82f7ed61477ad4d3b7bd0399ba4ea27c67
3
+ metadata.gz: 1402d523d25704f0f739033b55f4b14fbc197608e8ce8ba383ba1f6d15a86893
4
+ data.tar.gz: 30a8117792bf24f897bd1d13ae193dcc9829a6bd3e0355d49470618226269ccc
5
5
  SHA512:
6
- metadata.gz: e91a474e286da7b34fa36de63fa9885e1c89b765a4cf3aa2a0d9de8b936841247014f2ae6b326bc093cecb94fb05ae2cfc3824fe61f1be159554d98ac7df7d36
7
- data.tar.gz: 40ab134356ea61d933c81379aa84977c70cb3d00cebfc3b62d1e3e26e6b698ba8f19993a22900ed6be07ec8e356272812de5a84bc87184014a29a226d055367b
6
+ metadata.gz: b61da4ef8d7ea544b1cb777c18939c928d9fa8b78e014aba28c96573752b1339e00d5313d1cff7857eb15a1aef4184ccf1a76f324c1a2152d44070d8b98836f9
7
+ data.tar.gz: 0b6ab64beb50c209f31c73f5ff20eca41e8d97cfb448f61da5ff9eaf848e008cd1de2bc4b605d04d07f5c0e80b8d3d6c334d08256475c33a2570a5cfe4796227
data/README.md CHANGED
@@ -16,6 +16,7 @@ documented types at runtime.
16
16
  - [Adding request examples](#adding-request-examples)
17
17
  7. Types
18
18
  - [Agreement](#agreement)
19
+ - [Any](#any)
19
20
  - [Array](#array)
20
21
  - [BigDecimal](#big_decimal)
21
22
  - [Boolean](#boolean)
@@ -404,6 +405,16 @@ A boolean that must always be true. Useful for terms of use or agreement
404
405
  acceptances. The following values are accepted: `true`, `"true"`, `"on"`, `"1"`
405
406
  and `1`.
406
407
 
408
+ ### Any
409
+
410
+ ```ruby
411
+ :any
412
+ ```
413
+
414
+ Allows all values, including null. Useful when documenting a proxy that
415
+ responds with whatever value the other service returned.
416
+
417
+
407
418
  ### Array
408
419
 
409
420
  ```ruby
@@ -2,6 +2,7 @@
2
2
  <html>
3
3
  <head>
4
4
  <title><%= page_title || "API Documentation" %></title>
5
+ <meta charset="UTF-8">
5
6
 
6
7
  <% if favicon_url %>
7
8
  <link rel="icon" href="<%= favicon_url %>" />
@@ -50,7 +51,7 @@
50
51
  Version <%= version %>
51
52
  </div>
52
53
  <div class="p-1 w-1/2">
53
- <%= link_to url_helpers.explicit_documentation_swagger_path, target: "_blank", class: "flex items-center justify-center gap-1 text-neutral-900" do %>
54
+ <%= link_to "https://petstore.swagger.io/?url=#{url_helpers.explicit_documentation_swagger_url(host: request.host)}", target: "_blank", class: "flex items-center justify-center gap-1 text-neutral-900" do %>
54
55
  <span>Swagger</span>
55
56
 
56
57
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4">
@@ -12,6 +12,20 @@ module Explicit::Documentation::Output
12
12
  end
13
13
 
14
14
  def swagger_document
15
+ paths = build_paths_from_requests
16
+
17
+ securitySchemes = {}.tap do |hash|
18
+ requests = paths.flat_map { |path, methods| methods.values }
19
+
20
+ if requests.filter { _1.dig(:security, 0, :basicAuth) }.any?
21
+ hash[:basicAuth] = { type: "http", scheme: "basic" }
22
+ end
23
+
24
+ if requests.filter { _1.dig(:security, 0, :bearerAuth) }.any?
25
+ hash[:bearerAuth] = { type: "http", scheme: "bearer" }
26
+ end
27
+ end
28
+
15
29
  {
16
30
  openapi: "3.0.1",
17
31
  info: {
@@ -24,7 +38,8 @@ module Explicit::Documentation::Output
24
38
  }
25
39
  ],
26
40
  tags: build_tags_from_sections,
27
- paths: build_paths_from_requests
41
+ paths: build_paths_from_requests,
42
+ components: { securitySchemes: }
28
43
  }
29
44
  end
30
45
 
@@ -86,13 +101,23 @@ module Explicit::Documentation::Output
86
101
  request = page.request
87
102
  route = request.routes.first
88
103
 
104
+ security =
105
+ if request.requires_basic_authorization?
106
+ [ { basicAuth: [] } ]
107
+ elsif request.requires_bearer_authorization?
108
+ [ { bearerAuth: [] } ]
109
+ else
110
+ nil
111
+ end
112
+
89
113
  paths[route.path_with_curly_syntax][route.method.to_s] = {
90
114
  tags: [ section.name ],
91
115
  summary: request.get_title,
92
116
  description: request.get_description,
93
117
  parameters: build_parameters(request),
94
118
  requestBody: build_request_body(request),
95
- responses: build_responses(request)
119
+ responses: build_responses(request),
120
+ security:
96
121
  }.compact_blank
97
122
  end
98
123
  end
@@ -102,7 +127,11 @@ module Explicit::Documentation::Output
102
127
 
103
128
  def build_parameters(request)
104
129
  headers =
105
- request.headers_type.attributes.map do |name, type|
130
+ request.headers_type.attributes.filter_map do |name, type|
131
+ # NOTE: skip Authorization header because swagger prefers the `security` directive for basic and bearer
132
+ # authorization schemas. If not basic or bearer, then we add the Authorization header.
133
+ next if name == "Authorization" && !request.custom_authorization_format?
134
+
106
135
  {
107
136
  name: name.to_s,
108
137
  in: "header",
@@ -12,13 +12,15 @@ class Explicit::Request
12
12
 
13
13
  instance_eval(&block)
14
14
 
15
+ define_missing_path_params!
16
+
15
17
  if Explicit.configuration.rescue_from_invalid_params? && @params.any?
16
18
  @responses[422] << {
17
19
  error: "invalid_params",
18
20
  params: [
19
21
  :description,
20
22
  "An object containing error messages for all invalid params",
21
- [:hash, :string, :string]
23
+ [ :hash, :string, :string ]
22
24
  ]
23
25
  }
24
26
  end
@@ -76,19 +78,19 @@ class Explicit::Request
76
78
  raise ArgumentError("duplicated param #{name}") if @params.key?(name)
77
79
 
78
80
  if options[:optional]
79
- type = [:nilable, type]
81
+ type = [ :nilable, type ]
80
82
  end
81
83
 
82
84
  if (defaultval = options[:default])
83
- type = [:default, defaultval, type]
85
+ type = [ :default, defaultval, type ]
84
86
  end
85
87
 
86
88
  if (description = options[:description])
87
- type = [:description, description, type]
89
+ type = [ :description, description, type ]
88
90
  end
89
91
 
90
92
  if @routes.first&.params&.include?(name)
91
- type = [:_param_location, :path, type]
93
+ type = [ :_param_location, :path, type ]
92
94
  end
93
95
 
94
96
  @params[name] = type
@@ -140,6 +142,31 @@ class Explicit::Request
140
142
  end
141
143
 
142
144
  def responses_type(status:)
143
- Explicit::Type.build([:one_of, *@responses[status]])
145
+ Explicit::Type.build([ :one_of, *@responses[status] ])
146
+ end
147
+
148
+ def custom_authorization_format?
149
+ @headers.key?("Authorization") && !requires_basic_authorization? && !requires_bearer_authorization?
150
+ end
151
+
152
+ def requires_basic_authorization?
153
+ authorization = headers_type.attributes["Authorization"]
154
+
155
+ authorization&.format&.to_s&.include?("Basic")
144
156
  end
157
+
158
+ def requires_bearer_authorization?
159
+ authorization = headers_type.attributes["Authorization"]
160
+
161
+ authorization&.format&.to_s&.include?("Bearer")
162
+ end
163
+
164
+ private
165
+ def define_missing_path_params!
166
+ @routes.first&.params&.each do |path_param_name|
167
+ if @params[path_param_name.to_sym].blank?
168
+ param(path_param_name.to_sym, :string)
169
+ end
170
+ end
171
+ end
145
172
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Explicit::Type::Any < Explicit::Type
4
+ def validate(value)
5
+ [ :ok, value ]
6
+ end
7
+
8
+ concerning :Webpage do
9
+ def summary
10
+ "any"
11
+ end
12
+
13
+ def partial
14
+ "explicit/documentation/type/any"
15
+ end
16
+
17
+ def has_details?
18
+ false
19
+ end
20
+ end
21
+
22
+ concerning :Swagger do
23
+ def swagger_schema
24
+ merge_base_swagger_schema({})
25
+ end
26
+ end
27
+ end
@@ -81,7 +81,7 @@ class Explicit::Type::Float < Explicit::Type
81
81
  negative == false ? swagger_i18n("number_not_negative") : nil,
82
82
  negative == true ? swagger_i18n("number_only_negative") : nil
83
83
  ]
84
- })
84
+ }.compact_blank)
85
85
  end
86
86
  end
87
87
  end
@@ -81,7 +81,7 @@ class Explicit::Type::Integer < Explicit::Type
81
81
  negative == false ? swagger_i18n("number_not_negative") : nil,
82
82
  negative == true ? swagger_i18n("number_only_negative") : nil
83
83
  ]
84
- })
84
+ }.compact_blank)
85
85
  end
86
86
  end
87
87
  end
@@ -7,7 +7,7 @@ class Explicit::Type::Record < Explicit::Type
7
7
  @attributes = attributes.to_h do |attribute_name, type|
8
8
  type = Explicit::Type.build(type) if !type.is_a?(Explicit::Type)
9
9
 
10
- [attribute_name, type]
10
+ [ attribute_name, type ]
11
11
  end
12
12
  end
13
13
 
@@ -28,9 +28,9 @@ class Explicit::Type::Record < Explicit::Type
28
28
  end
29
29
  end
30
30
 
31
- return [:error, errors] if errors.any?
31
+ return [ :error, errors ] if errors.any?
32
32
 
33
- [:ok, validated_data]
33
+ [ :ok, validated_data ]
34
34
  end
35
35
 
36
36
  def path_params_type
@@ -78,7 +78,7 @@ class Explicit::Type::Record < Explicit::Type
78
78
 
79
79
  def swagger_schema
80
80
  properties = attributes.to_h do |name, type|
81
- [name, type.swagger_schema]
81
+ [ name, type.swagger_schema ]
82
82
  end
83
83
 
84
84
  required = attributes.filter_map do |name, type|
@@ -89,7 +89,7 @@ class Explicit::Type::Record < Explicit::Type
89
89
  type: "object",
90
90
  properties:,
91
91
  required:
92
- })
92
+ }.compact_blank)
93
93
  end
94
94
  end
95
95
  end
@@ -34,7 +34,7 @@ class Explicit::Type::String < Explicit::Type
34
34
  return error_i18n("format", regex: format.inspect)
35
35
  end
36
36
 
37
- [:ok, value]
37
+ [ :ok, value ]
38
38
  end
39
39
 
40
40
  concerning :Webpage do
@@ -62,7 +62,7 @@ class Explicit::Type::String < Explicit::Type
62
62
  empty == false ? swagger_i18n("string_not_empty") : nil,
63
63
  downcase == true ? swagger_i18n("string_downcase") : nil
64
64
  ]
65
- })
65
+ }.compact_blank)
66
66
  end
67
67
  end
68
- end
68
+ end
data/lib/explicit/type.rb CHANGED
@@ -5,6 +5,9 @@ class Explicit::Type
5
5
 
6
6
  def self.build(type)
7
7
  case type
8
+ in :any
9
+ Explicit::Type::Any.new
10
+
8
11
  in :agreement
9
12
  Explicit::Type::Agreement.new
10
13
 
@@ -156,8 +159,8 @@ class Explicit::Type
156
159
  base_attributes = {
157
160
  default: default_value,
158
161
  description: formatted_description
159
- }
162
+ }.compact_blank
160
163
 
161
- base_attributes.merge(attributes).compact_blank
164
+ base_attributes.merge(attributes)
162
165
  end
163
166
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Explicit
4
- VERSION = "0.2.8"
4
+ VERSION = "0.2.10"
5
5
  end
data/lib/explicit.rb CHANGED
@@ -25,6 +25,7 @@ require "explicit/request/route"
25
25
 
26
26
  require "explicit/type"
27
27
  require "explicit/type/agreement"
28
+ require "explicit/type/any"
28
29
  require "explicit/type/array"
29
30
  require "explicit/type/big_decimal"
30
31
  require "explicit/type/boolean"
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.8
4
+ version: 0.2.10
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-02-20 00:00:00.000000000 Z
11
+ date: 2025-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -95,6 +95,7 @@ files:
95
95
  - lib/explicit/test_helper/example_recorder.rb
96
96
  - lib/explicit/type.rb
97
97
  - lib/explicit/type/agreement.rb
98
+ - lib/explicit/type/any.rb
98
99
  - lib/explicit/type/array.rb
99
100
  - lib/explicit/type/big_decimal.rb
100
101
  - lib/explicit/type/boolean.rb