spec_forge 0.6.0 → 0.7.1

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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +174 -8
  3. data/README.md +135 -10
  4. data/flake.lock +3 -3
  5. data/flake.nix +3 -3
  6. data/lib/spec_forge/attribute/factory.rb +1 -1
  7. data/lib/spec_forge/attribute/transform.rb +1 -1
  8. data/lib/spec_forge/callbacks.rb +9 -0
  9. data/lib/spec_forge/cli/docs/generate.rb +72 -0
  10. data/lib/spec_forge/cli/docs.rb +92 -0
  11. data/lib/spec_forge/cli/init.rb +39 -7
  12. data/lib/spec_forge/cli/new.rb +13 -3
  13. data/lib/spec_forge/cli/run.rb +12 -4
  14. data/lib/spec_forge/cli/serve.rb +156 -0
  15. data/lib/spec_forge/cli.rb +14 -6
  16. data/lib/spec_forge/configuration.rb +13 -9
  17. data/lib/spec_forge/context/store.rb +23 -40
  18. data/lib/spec_forge/core_ext/array.rb +27 -0
  19. data/lib/spec_forge/documentation/builder.rb +383 -0
  20. data/lib/spec_forge/documentation/document/operation.rb +47 -0
  21. data/lib/spec_forge/documentation/document/parameter.rb +22 -0
  22. data/lib/spec_forge/documentation/document/request_body.rb +24 -0
  23. data/lib/spec_forge/documentation/document/response.rb +39 -0
  24. data/lib/spec_forge/documentation/document/response_body.rb +27 -0
  25. data/lib/spec_forge/documentation/document.rb +48 -0
  26. data/lib/spec_forge/documentation/generators/base.rb +81 -0
  27. data/lib/spec_forge/documentation/generators/openapi/base.rb +100 -0
  28. data/lib/spec_forge/documentation/generators/openapi/error_formatter.rb +149 -0
  29. data/lib/spec_forge/documentation/generators/openapi/v3_0.rb +65 -0
  30. data/lib/spec_forge/documentation/generators/openapi.rb +59 -0
  31. data/lib/spec_forge/documentation/generators.rb +17 -0
  32. data/lib/spec_forge/documentation/loader/cache.rb +138 -0
  33. data/lib/spec_forge/documentation/loader.rb +159 -0
  34. data/lib/spec_forge/documentation/openapi/base.rb +33 -0
  35. data/lib/spec_forge/documentation/openapi/v3_0/example.rb +44 -0
  36. data/lib/spec_forge/documentation/openapi/v3_0/media_type.rb +42 -0
  37. data/lib/spec_forge/documentation/openapi/v3_0/operation.rb +175 -0
  38. data/lib/spec_forge/documentation/openapi/v3_0/response.rb +65 -0
  39. data/lib/spec_forge/documentation/openapi/v3_0/schema.rb +80 -0
  40. data/lib/spec_forge/documentation/openapi/v3_0/tag.rb +71 -0
  41. data/lib/spec_forge/documentation/openapi.rb +23 -0
  42. data/lib/spec_forge/documentation.rb +27 -0
  43. data/lib/spec_forge/error.rb +17 -0
  44. data/lib/spec_forge/factory.rb +2 -2
  45. data/lib/spec_forge/filter.rb +3 -4
  46. data/lib/spec_forge/forge.rb +5 -4
  47. data/lib/spec_forge/http/backend.rb +5 -0
  48. data/lib/spec_forge/http/request.rb +14 -3
  49. data/lib/spec_forge/loader.rb +14 -24
  50. data/lib/spec_forge/normalizer/default.rb +51 -0
  51. data/lib/spec_forge/normalizer/definition.rb +248 -0
  52. data/lib/spec_forge/normalizer/validators.rb +99 -0
  53. data/lib/spec_forge/normalizer.rb +356 -199
  54. data/lib/spec_forge/normalizers/_shared.yml +76 -0
  55. data/lib/spec_forge/normalizers/configuration.yml +23 -0
  56. data/lib/spec_forge/normalizers/constraint.yml +8 -0
  57. data/lib/spec_forge/normalizers/expectation.yml +47 -0
  58. data/lib/spec_forge/normalizers/factory.yml +12 -0
  59. data/lib/spec_forge/normalizers/factory_reference.yml +15 -0
  60. data/lib/spec_forge/normalizers/global_context.yml +28 -0
  61. data/lib/spec_forge/normalizers/spec.yml +50 -0
  62. data/lib/spec_forge/runner/adapter.rb +181 -0
  63. data/lib/spec_forge/runner/debug_proxy.rb +44 -42
  64. data/lib/spec_forge/runner/state.rb +4 -5
  65. data/lib/spec_forge/runner.rb +40 -124
  66. data/lib/spec_forge/spec/expectation/constraint.rb +13 -5
  67. data/lib/spec_forge/spec/expectation.rb +7 -3
  68. data/lib/spec_forge/spec.rb +13 -58
  69. data/lib/spec_forge/version.rb +1 -1
  70. data/lib/spec_forge.rb +30 -23
  71. data/lib/templates/openapi.yml.tt +22 -0
  72. data/lib/templates/redoc.html.tt +28 -0
  73. data/lib/templates/swagger.html.tt +59 -0
  74. metadata +92 -14
  75. data/lib/spec_forge/normalizer/configuration.rb +0 -90
  76. data/lib/spec_forge/normalizer/constraint.rb +0 -60
  77. data/lib/spec_forge/normalizer/expectation.rb +0 -105
  78. data/lib/spec_forge/normalizer/factory.rb +0 -78
  79. data/lib/spec_forge/normalizer/factory_reference.rb +0 -85
  80. data/lib/spec_forge/normalizer/global_context.rb +0 -88
  81. data/lib/spec_forge/normalizer/spec.rb +0 -97
  82. /data/lib/templates/{forge_helper.tt → forge_helper.rb.tt} +0 -0
  83. /data/lib/templates/{new_factory.tt → new_factory.yml.tt} +0 -0
  84. /data/lib/templates/{new_spec.tt → new_spec.yml.tt} +0 -0
@@ -8,150 +8,66 @@ module SpecForge
8
8
  class Runner
9
9
  class << self
10
10
  #
11
- # Defines RSpec examples for a collection of forges
12
- # Creates the test structure that will be executed
11
+ # Prepares forge objects for test execution
13
12
  #
14
- # @param forges [Array<Forge>] The forges to define as RSpec examples
13
+ # Loads the forge helper, registers factories, loads specs from files,
14
+ # applies filtering, and returns ready-to-run forge objects.
15
15
  #
16
- def define(forges)
17
- forges.each do |forge|
18
- define_forge(forge)
19
- end
20
- end
21
-
22
- #
23
- # Runs the defined RSpec examples
24
- # Executes the tests after they've been defined
25
- #
26
- def run
27
- prepare_for_run
28
-
29
- ARGV.clear
30
- RSpec::Core::Runner.invoke
31
- end
32
-
33
- #
34
- # Defines RSpec examples for a specific forge
35
- # Creates the test structure for a single forge file
16
+ # @param file_name [String, nil] Optional file name filter
17
+ # @param spec_name [String, nil] Optional spec name filter
18
+ # @param expectation_name [String, nil] Optional expectation name filter
36
19
  #
37
- # @param forge [Forge] The forge to define
20
+ # @return [Array<Forge>] Array of prepared forge objects
38
21
  #
39
- def define_forge(forge)
40
- # This is just like writing a normal RSpec test, except with loops ;)
41
- RSpec.describe(forge.name) do
42
- # Callback for the file
43
- before(:context) { Callbacks.before_file(forge) }
44
- after(:context) { Callbacks.after_file(forge) }
45
-
46
- # Specs
47
- forge.specs.each do |spec|
48
- # Describe the spec
49
- describe(spec.name) do
50
- # Request data is for the spec and contains the base and overlays
51
- let!(:request_data) { forge.request[spec.id] }
52
-
53
- # The HTTP client for the spec
54
- let!(:http_client) { HTTP::Client.new(**request_data[:base]) }
55
-
56
- # Callback for the spec
57
- before(:context) { Callbacks.before_spec(forge, spec) }
58
- after(:context) { Callbacks.after_spec(forge, spec) }
59
-
60
- # Expectations
61
- spec.expectations.each do |expectation|
62
- # Onto the actual expectation itself
63
- describe(expectation.name) do
64
- # Set metadata for the example group for error reporting
65
- Metadata.set_for_group(spec, expectation, self)
22
+ def prepare(file_name: nil, spec_name: nil, expectation_name: nil)
23
+ load_forge_helper
66
24
 
67
- # Lazily load the constraints
68
- let(:constraints) { expectation.constraints.as_matchers }
25
+ # Load factories
26
+ Factory.load_and_register
69
27
 
70
- let(:match_status) { constraints[:status] }
71
- let(:match_json) { constraints[:json] }
72
- let(:match_json_class) { be_kind_of(match_json.class) }
28
+ # Load the specs from their files and create forges from them
29
+ forges = Loader.load_from_files.map { |f| Forge.new(*f) }
73
30
 
74
- # The request for the test itself. Overlays the expectation's data if it exists
75
- let(:request) do
76
- request = request_data[:base]
31
+ # Filter out the specs and expectations
32
+ forges = Filter.apply(forges, file_name:, spec_name:, expectation_name:)
77
33
 
78
- if (overlay = request_data[:overlay][expectation.id])
79
- request = request.deep_merge(overlay)
80
- end
34
+ # Tell the user that we filtered if we did
35
+ Filter.announce(forges, file_name:, spec_name:, expectation_name:)
81
36
 
82
- HTTP::Request.new(**request)
83
- end
84
-
85
- # The Faraday response
86
- subject(:response) { http_client.call(request) }
87
-
88
- # Callbacks for the expectation
89
- before :each do
90
- Callbacks.before_expectation(
91
- forge, spec, expectation, self, RSpec.current_example
92
- )
93
- end
94
-
95
- # The 'after_expectation' callback is handled by Listener due to RSpec not
96
- # reporting the example's status until after the describe block has finished.
97
- after :each do
98
- # However, the downside about having the callback triggered later is that RSpec
99
- # will have reset the memoized let variables back to nil.
100
- # This causes an issue when an expectation goes to store the state, it will end
101
- # up re-calling the various variables and triggering another HTTP request.
102
- # Since the variables are still memoized in this hook, it is the perfect
103
- # time to store the referenced to them.
104
- State.set(response:)
105
- end
106
-
107
- # The test itself
108
- it(expectation.constraints.description) do
109
- if spec.debug? || expectation.debug?
110
- Callbacks.on_debug(forge, spec, expectation, self)
111
- end
112
-
113
- # Status check
114
- expect(response.status).to match_status
115
-
116
- # JSON check
117
- if match_json.present?
118
- expect(response.body).to match_json_class
37
+ forges
38
+ end
119
39
 
120
- case match_json
121
- when Hash
122
- # Check per key for easier debugging
123
- match_json.each do |key, matcher|
124
- expect(response.body).to have_key(key)
125
- expect(response.body[key]).to matcher
126
- end
127
- else
128
- expect(response.body).to match_json
129
- end
130
- end
131
- end
132
- end
133
- end
134
- end
135
- end
136
- end
40
+ #
41
+ # Runs the prepared forges through RSpec
42
+ #
43
+ # Sets up the RSpec adapter and executes all tests, with optional
44
+ # exit behavior for CLI usage.
45
+ #
46
+ # @param forges [Array<Forge>] The forge objects to run
47
+ # @param exit_on_finish [Boolean] Whether to exit the process when complete
48
+ # @param exit_on_failure [Boolean] Whether to exit the process if any test fails
49
+ #
50
+ # @return [Integer, nil] Exit status if exit_on_finish is false
51
+ #
52
+ def run(forges, **)
53
+ Adapter.setup(forges)
54
+ Adapter.run(**)
137
55
  end
138
56
 
139
57
  private
140
58
 
141
- def prepare_for_run
142
- # Allows modifying the error backtrace reporting within rspec
143
- RSpec.configuration.instance_variable_set(:@backtrace_formatter, BacktraceFormatter)
59
+ def load_forge_helper
60
+ forge_helper = SpecForge.forge_path.join("forge_helper.rb")
61
+ require_relative forge_helper if File.exist?(forge_helper)
144
62
 
145
- # Listen for passed/failed events to trigger the "after_each" callback
146
- RSpec.configuration.reporter.register_listener(
147
- Listener.instance,
148
- :example_passed, :example_failed
149
- )
63
+ # Validate in case anything was changed
64
+ SpecForge.configuration.validate
150
65
  end
151
66
  end
152
67
  end
153
68
  end
154
69
 
70
+ require_relative "runner/adapter"
155
71
  require_relative "runner/callbacks"
156
72
  require_relative "runner/debug_proxy"
157
73
  require_relative "runner/listener"
@@ -12,21 +12,24 @@ module SpecForge
12
12
  # @example In code
13
13
  # constraint = Constraint.new(
14
14
  # status: 200,
15
- # json: {name: "matcher.eq" => "John"}
15
+ # headers: {response_header: "kind_of.string"},
16
+ # json: {name: {"matcher.eq" => "John"}}
16
17
  # )
17
18
  #
18
- class Constraint < Data.define(:status, :json) # :xml, :html
19
+ class Constraint < Data.define(:status, :headers, :json) # :xml, :html
19
20
  #
20
21
  # Creates a new constraint
21
22
  #
22
23
  # @param status [Integer, String] The expected HTTP status code, or reference to one
24
+ # @param headers [Hash] The expected headers with matchers
23
25
  # @param json [Hash, Array] The expected JSON with matchers
24
26
  #
25
27
  # @return [Constraint] A new constraint instance
26
28
  #
27
- def initialize(status:, json: {})
29
+ def initialize(status:, headers: {}, json: {})
28
30
  super(
29
31
  status: Attribute.from(status),
32
+ headers: Attribute.from(headers),
30
33
  json: Attribute.from(json)
31
34
  )
32
35
  end
@@ -58,7 +61,8 @@ module SpecForge
58
61
  def as_matchers
59
62
  {
60
63
  status: status.resolve_as_matcher,
61
- json: resolve_json_matcher
64
+ json: resolve_json_matcher,
65
+ headers: resolve_hash_matcher(headers)
62
66
  }
63
67
  end
64
68
 
@@ -108,11 +112,15 @@ module SpecForge
108
112
  def resolve_json_matcher
109
113
  case json
110
114
  when HashLike
111
- json.transform_values(&:resolve_as_matcher).stringify_keys
115
+ resolve_hash_matcher(json)
112
116
  else
113
117
  json.resolve_as_matcher
114
118
  end
115
119
  end
120
+
121
+ def resolve_hash_matcher(hash)
122
+ hash.transform_values(&:resolve_as_matcher).stringify_keys
123
+ end
116
124
  end
117
125
  end
118
126
  end
@@ -15,7 +15,10 @@ module SpecForge
15
15
  # json:
16
16
  # name: kind_of.string
17
17
  #
18
- class Expectation < Data.define(:id, :name, :line_number, :debug, :store_as, :constraints)
18
+ class Expectation < Data.define(
19
+ :id, :name, :line_number,
20
+ :debug, :store_as, :documentation, :constraints
21
+ )
19
22
  #
20
23
  # @return [Boolean] True if debugging is enabled
21
24
  #
@@ -34,14 +37,15 @@ module SpecForge
34
37
  # @param line_number [Integer] Line number in source
35
38
  # @param debug [Boolean] Whether to enable debugging
36
39
  # @param store_as [String] Unique Context::Store identifier
40
+ # @param documentation [Boolean] Whether to include in documentation generation
37
41
  # @param expect [Hash] Expected constraints
38
42
  #
39
43
  # @return [Expectation] A new expectation instance
40
44
  #
41
- def initialize(id:, name:, line_number:, debug:, store_as:, expect:)
45
+ def initialize(id:, name:, line_number:, debug:, store_as:, expect:, documentation:)
42
46
  constraints = Constraint.new(**expect)
43
47
 
44
- super(id:, name:, line_number:, debug:, store_as:, constraints:)
48
+ super(id:, name:, line_number:, debug:, store_as:, documentation:, constraints:)
45
49
  end
46
50
 
47
51
  #
@@ -14,61 +14,15 @@ module SpecForge
14
14
  # - expect:
15
15
  # status: 200
16
16
  #
17
- class Spec
17
+ class Spec < Data.define(
18
+ :id, :name, :file_path, :file_name, :line_number,
19
+ :debug, :documentation, :expectations
20
+ )
18
21
  #
19
22
  # @return [Boolean] True if debugging is enabled
20
23
  #
21
24
  attr_predicate :debug
22
25
 
23
- #
24
- # Unique identifier for this spec
25
- #
26
- # @return [String] The spec ID
27
- #
28
- attr_reader :id
29
-
30
- #
31
- # Human-readable name for this spec
32
- #
33
- # @return [String] The spec name
34
- #
35
- attr_reader :name
36
-
37
- #
38
- # Absolute path to the file containing this spec
39
- #
40
- # @return [String] The file path
41
- #
42
- attr_reader :file_path
43
-
44
- #
45
- # Base name of the file without path or extension
46
- #
47
- # @return [String] The file name
48
- #
49
- attr_reader :file_name
50
-
51
- #
52
- # Whether to enable debugging for this spec
53
- #
54
- # @return [Boolean] Debug flag
55
- #
56
- attr_reader :debug
57
-
58
- #
59
- # Line number in the source file where this spec is defined
60
- #
61
- # @return [Integer] The line number
62
- #
63
- attr_reader :line_number
64
-
65
- #
66
- # The expectations to test for this spec
67
- #
68
- # @return [Array<Expectation>] The expectations
69
- #
70
- attr_accessor :expectations
71
-
72
26
  #
73
27
  # Creates a new spec instance
74
28
  #
@@ -78,18 +32,18 @@ module SpecForge
78
32
  # @param file_name [String] Base name of file
79
33
  # @param debug [Boolean] Whether to enable debugging
80
34
  # @param line_number [Integer] Line number in source
35
+ # @param documentation [Boolean] Whether to include in documentation generation
81
36
  # @param expectations [Array<Hash>] Expectation configurations
82
37
  #
83
38
  # @return [Spec] A new spec instance
84
39
  #
85
- def initialize(id:, name:, file_path:, file_name:, debug:, line_number:, expectations:)
86
- @id = id
87
- @name = name
88
- @file_path = file_path
89
- @file_name = file_name
90
- @debug = debug
91
- @line_number = line_number
92
- @expectations = expectations.map { |e| Expectation.new(**e) }
40
+ def initialize(
41
+ id:, name:, file_path:, file_name:, line_number:,
42
+ debug:, documentation:, expectations:
43
+ )
44
+ expectations = expectations.map { |e| Expectation.new(**e) }
45
+
46
+ super
93
47
  end
94
48
 
95
49
  #
@@ -104,6 +58,7 @@ module SpecForge
104
58
  file_name:,
105
59
  debug:,
106
60
  line_number:,
61
+ documentation:,
107
62
  expectations: expectations.map(&:to_h)
108
63
  }
109
64
  end
@@ -4,5 +4,5 @@ module SpecForge
4
4
  #
5
5
  # Current version of SpecForge
6
6
  #
7
- VERSION = "0.6.0"
7
+ VERSION = "0.7.1"
8
8
  end
data/lib/spec_forge.rb CHANGED
@@ -5,15 +5,19 @@ require "logger"
5
5
  require "active_support"
6
6
  require "active_support/core_ext"
7
7
  require "commander"
8
- require "everythingrb"
8
+ require "everythingrb/prelude"
9
+ require "everythingrb/all"
9
10
  require "factory_bot"
10
11
  require "faker"
11
12
  require "faraday"
12
13
  require "mime/types"
14
+ require "openapi3_parser"
13
15
  require "pathname"
14
16
  require "rspec"
17
+ require "sem_version"
15
18
  require "singleton"
16
19
  require "thor"
20
+ require "webrick"
17
21
  require "yaml"
18
22
 
19
23
  #
@@ -57,28 +61,8 @@ module SpecForge
57
61
  # @param expectation_name [String, nil] Optional name of expectation to run
58
62
  #
59
63
  def run(file_name: nil, spec_name: nil, expectation_name: nil)
60
- # Load spec_helper.rb
61
- forge_helper = SpecForge.forge_path.join("forge_helper.rb")
62
- require_relative forge_helper if File.exist?(forge_helper)
63
-
64
- # Validate in case anything was changed
65
- configuration.validate
66
-
67
- # Load factories
68
- Factory.load_and_register
69
-
70
- # Load the specs from their files and create forges from them
71
- forges = Loader.load_from_files.map { |f| Forge.new(*f) }
72
-
73
- # Filter out the specs and expectations
74
- forges = Filter.apply(forges, file_name:, spec_name:, expectation_name:)
75
-
76
- # Tell the user that we filtered if we did
77
- Filter.announce(forges, file_name:, spec_name:, expectation_name:)
78
-
79
- # Define and run everything
80
- Runner.define(forges)
81
- Runner.run
64
+ forges = Runner.prepare(file_name:, spec_name:, expectation_name:)
65
+ Runner.run(forges, exit_on_finish: true)
82
66
  end
83
67
 
84
68
  #
@@ -99,6 +83,15 @@ module SpecForge
99
83
  @forge_path ||= root.join("spec_forge")
100
84
  end
101
85
 
86
+ #
87
+ # Returns SpecForge's openapi directory
88
+ #
89
+ # @return [Pathname] The spec_forge openapi directory path
90
+ #
91
+ def openapi_path
92
+ @openapi_path ||= forge_path.join("openapi")
93
+ end
94
+
102
95
  #
103
96
  # Returns SpecForge's configuration
104
97
  #
@@ -168,6 +161,19 @@ module SpecForge
168
161
  def register_callback(name, &)
169
162
  Callbacks.register(name, &)
170
163
  end
164
+
165
+ #
166
+ # Generates a unique ID for an object based on hash and object_id
167
+ #
168
+ # @param object [Object] The object to generate an ID for
169
+ #
170
+ # @return [String] A unique ID string
171
+ #
172
+ # @private
173
+ #
174
+ def generate_id(object)
175
+ "#{object.hash.abs.to_s(36)}_#{object.object_id.to_s(36)}"
176
+ end
171
177
  end
172
178
  end
173
179
 
@@ -178,6 +184,7 @@ require_relative "spec_forge/cli"
178
184
  require_relative "spec_forge/configuration"
179
185
  require_relative "spec_forge/context"
180
186
  require_relative "spec_forge/core_ext"
187
+ require_relative "spec_forge/documentation"
181
188
  require_relative "spec_forge/error"
182
189
  require_relative "spec_forge/factory"
183
190
  require_relative "spec_forge/filter"
@@ -0,0 +1,22 @@
1
+ info:
2
+ title: My API
3
+ version: 1.0.0
4
+ description: A description of My API
5
+ contact:
6
+ name: My API Team
7
+ email: api@example.com
8
+ license:
9
+ name: MIT
10
+ url: https://opensource.org/licenses/MIT
11
+
12
+ # servers:
13
+ # - url: https://api.example.com/v1
14
+ # description: Production
15
+ # - url: https://staging-api.example.com/v1
16
+ # description: Staging
17
+ #
18
+ # tags:
19
+ # - name: users
20
+ # description: User account management
21
+ # - name: posts
22
+ # description: Blog post management
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <title>Redoc</title>
6
+ <!-- needed for adaptive design -->
7
+ <meta charset="utf-8" />
8
+ <meta name="viewport" content="width=device-width, initial-scale=1">
9
+ <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
10
+
11
+ <!--
12
+ Redoc doesn't change outer page styles
13
+ -->
14
+ <style>
15
+ body {
16
+ margin: 0;
17
+ padding: 0;
18
+ }
19
+ </style>
20
+ </head>
21
+
22
+ <body>
23
+ <redoc spec-url="<%= spec_url %>"></redoc>
24
+ <script src=" https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js">
25
+ </script>
26
+ </body>
27
+
28
+ </html>
@@ -0,0 +1,59 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <title>Swagger UI</title>
7
+ <style>
8
+ html {
9
+ box-sizing: border-box;
10
+ overflow: -moz-scrollbars-vertical;
11
+ overflow-y: scroll;
12
+ }
13
+
14
+ *,
15
+ *:before,
16
+ *:after {
17
+ box-sizing: inherit;
18
+ }
19
+
20
+ body {
21
+ margin: 0;
22
+ background: #fafafa;
23
+ }
24
+ </style>
25
+
26
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.24.0/swagger-ui.min.css"
27
+ integrity="sha512-wWpxfn2bFvPwxuqDyiJbVB0WR3ffSqJNMMryNP07frPJ1h5Xg9HIDMV1wRr1rpxT5E+KTxDrKTuWfGb1RcV8SA=="
28
+ crossorigin="anonymous" referrerpolicy="no-referrer" />
29
+ </head>
30
+
31
+ <body>
32
+ <div id="swagger-ui"></div>
33
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.24.0/swagger-ui-bundle.min.js"
34
+ integrity="sha512-nEy/zRjIvuFMSr5ljsQUaUW4l7DoSHz8+SRybclmCjCh3MeF9UaooWYdr/SqjGCiyi4RIvBvn9DxCCV0ZDhiNA=="
35
+ crossorigin="anonymous" referrerpolicy="no-referrer"></script>
36
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.24.0/swagger-ui-standalone-preset.min.js"
37
+ integrity="sha512-yJlD9FXQ7YaxAKXhviHSt/0KqWDCkLFdCnk0Ti23HXDMEQtHLAAWMHZ+POglC1mx/MOUB//h8kci3U1JYrywpQ=="
38
+ crossorigin="anonymous" referrerpolicy="no-referrer"></script>
39
+
40
+ <script>
41
+ window.onload = function () {
42
+ const ui = SwaggerUIBundle({
43
+ url: "<%= spec_url %>",
44
+ dom_id: "#swagger-ui",
45
+ deepLinking: true,
46
+ presets: [
47
+ SwaggerUIBundle.presets.apis,
48
+ SwaggerUIStandalonePreset
49
+ ],
50
+ plugins: [
51
+ SwaggerUIBundle.plugins.DownloadUrl
52
+ ],
53
+ layout: "StandaloneLayout"
54
+ });
55
+ };
56
+ </script>
57
+ </body>
58
+
59
+ </html>