reqcord 0.1.4 → 0.3.0
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 +4 -4
- data/CHANGELOG.md +42 -0
- data/README.md +115 -713
- data/docs/architecture.md +134 -0
- data/docs/capture.md +169 -0
- data/docs/configuration.md +32 -3
- data/docs/exporters.md +182 -0
- data/docs/getting-started.md +186 -0
- data/docs/route-coverage.md +91 -0
- data/docs/troubleshooting.md +113 -0
- data/docs/web.md +66 -0
- data/examples/reqcord.yml +3 -1
- data/lib/reqcord/capture/integration_patch.rb +9 -2
- data/lib/reqcord/check.rb +73 -0
- data/lib/reqcord/configuration.rb +10 -1
- data/lib/reqcord/dataset.rb +0 -1
- data/lib/reqcord/exporters/markdown.rb +5 -2
- data/lib/reqcord/exporters/openapi.rb +295 -0
- data/lib/reqcord/exporters/postman.rb +11 -1
- data/lib/reqcord/file_value.rb +62 -0
- data/lib/reqcord/generator.rb +17 -3
- data/lib/reqcord/renderers/curl.rb +22 -1
- data/lib/reqcord/renderers/payload.rb +25 -7
- data/lib/reqcord/route_collector.rb +10 -1
- data/lib/reqcord/schema.rb +11 -1
- data/lib/reqcord/version.rb +1 -1
- data/lib/reqcord/web.rb +122 -0
- data/lib/reqcord.rb +4 -0
- data/lib/tasks/reqcord.rake +26 -1
- data/reqcord.gemspec +2 -1
- metadata +14 -2
data/lib/reqcord/web.rb
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rack/mime"
|
|
4
|
+
|
|
5
|
+
module Reqcord
|
|
6
|
+
# Serves the generated documentation from inside the application, the way
|
|
7
|
+
# Sidekiq::Web does:
|
|
8
|
+
#
|
|
9
|
+
# # config/routes.rb
|
|
10
|
+
# mount Reqcord::Web => "/api-docs" if Rails.env.development?
|
|
11
|
+
#
|
|
12
|
+
# `/api-docs` renders the OpenAPI document with Scalar; every other path is
|
|
13
|
+
# a file from the output directory (`dataset.json`, `postman/collection.json`,
|
|
14
|
+
# the Markdown pages, the cURL scripts). Nothing is generated on request —
|
|
15
|
+
# run `bin/rails reqcord:generate` first.
|
|
16
|
+
class Web
|
|
17
|
+
SCALAR_SCRIPT = "https://cdn.jsdelivr.net/npm/@scalar/api-reference"
|
|
18
|
+
OPENAPI_PATH = "openapi/openapi.json"
|
|
19
|
+
|
|
20
|
+
class << self
|
|
21
|
+
# `mount Reqcord::Web => "/api-docs"` calls the class itself.
|
|
22
|
+
def call(env)
|
|
23
|
+
app.call(env)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def app
|
|
27
|
+
@app ||= new
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def initialize(root: nil)
|
|
32
|
+
@root = root
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def call(env)
|
|
36
|
+
path = Rack::Utils.unescape_path(env["PATH_INFO"].to_s)
|
|
37
|
+
|
|
38
|
+
return index(env) if path.empty? || path == "/"
|
|
39
|
+
|
|
40
|
+
file = resolve(path)
|
|
41
|
+
|
|
42
|
+
return not_found unless file
|
|
43
|
+
|
|
44
|
+
[200, { "content-type" => content_type(file) }, [File.binread(file)]]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def root
|
|
50
|
+
Pathname(@root || Reqcord.configuration.output_directory).expand_path
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def index(env)
|
|
54
|
+
html = File.exist?(root.join(OPENAPI_PATH)) ? scalar_page(env) : empty_page(env)
|
|
55
|
+
|
|
56
|
+
[200, { "content-type" => "text/html; charset=utf-8" }, [html]]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# The path traversal guard: whatever the request says, the file must be
|
|
60
|
+
# inside the output directory.
|
|
61
|
+
def resolve(path)
|
|
62
|
+
candidate = root.join(path.delete_prefix("/")).expand_path
|
|
63
|
+
|
|
64
|
+
return nil unless candidate.to_s.start_with?("#{root}/")
|
|
65
|
+
return nil unless candidate.file?
|
|
66
|
+
|
|
67
|
+
candidate.to_s
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def content_type(file)
|
|
71
|
+
case File.extname(file)
|
|
72
|
+
when ".md" then "text/markdown; charset=utf-8"
|
|
73
|
+
when ".sh" then "text/plain; charset=utf-8"
|
|
74
|
+
else Rack::Mime.mime_type(File.extname(file), "application/octet-stream")
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def not_found
|
|
79
|
+
[404, { "content-type" => "text/plain" }, ["Not Found"]]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Absolute links: the page is served at the mount point itself, so a
|
|
83
|
+
# relative `openapi/…` would resolve one level too high.
|
|
84
|
+
def base(env)
|
|
85
|
+
env["SCRIPT_NAME"].to_s.chomp("/")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def scalar_page(env)
|
|
89
|
+
<<~HTML
|
|
90
|
+
<!doctype html>
|
|
91
|
+
<html>
|
|
92
|
+
<head>
|
|
93
|
+
<meta charset="utf-8">
|
|
94
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
95
|
+
<title>API Reference</title>
|
|
96
|
+
</head>
|
|
97
|
+
<body>
|
|
98
|
+
<script id="api-reference" data-url="#{base(env)}/#{OPENAPI_PATH}"></script>
|
|
99
|
+
<script src="#{SCALAR_SCRIPT}"></script>
|
|
100
|
+
</body>
|
|
101
|
+
</html>
|
|
102
|
+
HTML
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def empty_page(env)
|
|
106
|
+
<<~HTML
|
|
107
|
+
<!doctype html>
|
|
108
|
+
<html>
|
|
109
|
+
<head><meta charset="utf-8"><title>Reqcord</title></head>
|
|
110
|
+
<body style="font-family: system-ui, sans-serif; max-width: 40rem; margin: 4rem auto; line-height: 1.5">
|
|
111
|
+
<h1>No documentation generated yet</h1>
|
|
112
|
+
<p>Reqcord serves the files under <code>#{root}</code>. Generate them from your test suite:</p>
|
|
113
|
+
<pre>bin/rails reqcord:generate</pre>
|
|
114
|
+
<p>Then reload this page: the OpenAPI document is rendered here with Scalar, and
|
|
115
|
+
<a href="#{base(env)}/dataset.json">dataset.json</a>,
|
|
116
|
+
<a href="#{base(env)}/postman/collection.json">postman/collection.json</a> and the Markdown pages are served alongside it.</p>
|
|
117
|
+
</body>
|
|
118
|
+
</html>
|
|
119
|
+
HTML
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
data/lib/reqcord.rb
CHANGED
|
@@ -11,6 +11,7 @@ require "pathname"
|
|
|
11
11
|
require_relative "reqcord/version"
|
|
12
12
|
require_relative "reqcord/errors"
|
|
13
13
|
require_relative "reqcord/support"
|
|
14
|
+
require_relative "reqcord/file_value"
|
|
14
15
|
require_relative "reqcord/configuration"
|
|
15
16
|
|
|
16
17
|
require_relative "reqcord/request_example"
|
|
@@ -32,7 +33,10 @@ require_relative "reqcord/exporters"
|
|
|
32
33
|
require_relative "reqcord/exporters/curl"
|
|
33
34
|
require_relative "reqcord/exporters/markdown"
|
|
34
35
|
require_relative "reqcord/exporters/postman"
|
|
36
|
+
require_relative "reqcord/exporters/openapi"
|
|
35
37
|
require_relative "reqcord/generator"
|
|
38
|
+
require_relative "reqcord/check"
|
|
39
|
+
require_relative "reqcord/web"
|
|
36
40
|
|
|
37
41
|
require_relative "reqcord/railtie" if defined?(Rails::Railtie)
|
|
38
42
|
|
data/lib/tasks/reqcord.rake
CHANGED
|
@@ -45,11 +45,13 @@ namespace :reqcord do
|
|
|
45
45
|
include_uncovered: false
|
|
46
46
|
|
|
47
47
|
# markdown: pages under docs/api, curl: one runnable .sh per endpoint,
|
|
48
|
-
# postman: postman/collection.json (import into Postman or Hoppscotch)
|
|
48
|
+
# postman: postman/collection.json (import into Postman or Hoppscotch),
|
|
49
|
+
# openapi: openapi/openapi.json (rendered with Scalar by Reqcord::Web).
|
|
49
50
|
exporters:
|
|
50
51
|
- curl
|
|
51
52
|
- markdown
|
|
52
53
|
- postman
|
|
54
|
+
- openapi
|
|
53
55
|
|
|
54
56
|
variables:
|
|
55
57
|
# Host of every generated cURL and the Postman `base_url` variable.
|
|
@@ -107,6 +109,29 @@ namespace :reqcord do
|
|
|
107
109
|
puts "Output: #{Reqcord.configuration.output_directory}"
|
|
108
110
|
end
|
|
109
111
|
|
|
112
|
+
desc "Fail when the committed documentation is behind the tests"
|
|
113
|
+
task check: :environment do
|
|
114
|
+
Reqcord.reload_configuration!
|
|
115
|
+
|
|
116
|
+
result =
|
|
117
|
+
Reqcord::Check.call(
|
|
118
|
+
resources: ENV.fetch("RESOURCE", "").split(",").map(&:strip).reject(&:empty?),
|
|
119
|
+
version: ENV["VERSION"].to_s.strip.then { |value| value.empty? ? nil : value }
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
output = Reqcord.configuration.output_directory
|
|
123
|
+
|
|
124
|
+
if result.clean?
|
|
125
|
+
puts "Reqcord: #{output} is up to date."
|
|
126
|
+
else
|
|
127
|
+
puts "Reqcord: #{output} is out of date."
|
|
128
|
+
puts
|
|
129
|
+
result.lines.each { |line| puts " #{line}" }
|
|
130
|
+
puts
|
|
131
|
+
abort "Run `bin/rails reqcord:generate` and commit the result."
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
110
135
|
desc "List the routes Reqcord would document"
|
|
111
136
|
task routes: :environment do
|
|
112
137
|
routes =
|
data/reqcord.gemspec
CHANGED
|
@@ -20,7 +20,8 @@ Gem::Specification.new do |spec|
|
|
|
20
20
|
spec.required_ruby_version = ">= 3.2"
|
|
21
21
|
|
|
22
22
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
23
|
-
spec.metadata["changelog_uri"] = spec.homepage
|
|
23
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
|
|
24
|
+
spec.metadata["documentation_uri"] = "#{spec.homepage}/tree/master/docs"
|
|
24
25
|
spec.metadata["source_code_uri"] = spec.homepage
|
|
25
26
|
spec.metadata["rubygems_mfa_required"] = "true"
|
|
26
27
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: reqcord
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ahmet Saridogan
|
|
@@ -145,7 +145,14 @@ files:
|
|
|
145
145
|
- LICENSE.txt
|
|
146
146
|
- README.md
|
|
147
147
|
- Rakefile
|
|
148
|
+
- docs/architecture.md
|
|
149
|
+
- docs/capture.md
|
|
148
150
|
- docs/configuration.md
|
|
151
|
+
- docs/exporters.md
|
|
152
|
+
- docs/getting-started.md
|
|
153
|
+
- docs/route-coverage.md
|
|
154
|
+
- docs/troubleshooting.md
|
|
155
|
+
- docs/web.md
|
|
149
156
|
- examples/reqcord.yml
|
|
150
157
|
- gemfiles/rails_7.1.gemfile
|
|
151
158
|
- gemfiles/rails_7.2.gemfile
|
|
@@ -158,6 +165,7 @@ files:
|
|
|
158
165
|
- lib/reqcord/capture/minitest_context.rb
|
|
159
166
|
- lib/reqcord/capture/rspec_context.rb
|
|
160
167
|
- lib/reqcord/capture/test_context.rb
|
|
168
|
+
- lib/reqcord/check.rb
|
|
161
169
|
- lib/reqcord/configuration.rb
|
|
162
170
|
- lib/reqcord/dataset.rb
|
|
163
171
|
- lib/reqcord/endpoint.rb
|
|
@@ -165,7 +173,9 @@ files:
|
|
|
165
173
|
- lib/reqcord/exporters.rb
|
|
166
174
|
- lib/reqcord/exporters/curl.rb
|
|
167
175
|
- lib/reqcord/exporters/markdown.rb
|
|
176
|
+
- lib/reqcord/exporters/openapi.rb
|
|
168
177
|
- lib/reqcord/exporters/postman.rb
|
|
178
|
+
- lib/reqcord/file_value.rb
|
|
169
179
|
- lib/reqcord/generator.rb
|
|
170
180
|
- lib/reqcord/railtie.rb
|
|
171
181
|
- lib/reqcord/renderers/curl.rb
|
|
@@ -177,6 +187,7 @@ files:
|
|
|
177
187
|
- lib/reqcord/schema.rb
|
|
178
188
|
- lib/reqcord/support.rb
|
|
179
189
|
- lib/reqcord/version.rb
|
|
190
|
+
- lib/reqcord/web.rb
|
|
180
191
|
- lib/tasks/reqcord.rake
|
|
181
192
|
- reqcord.gemspec
|
|
182
193
|
homepage: https://github.com/ahmetsaridogan/reqcord
|
|
@@ -184,7 +195,8 @@ licenses:
|
|
|
184
195
|
- MIT
|
|
185
196
|
metadata:
|
|
186
197
|
homepage_uri: https://github.com/ahmetsaridogan/reqcord
|
|
187
|
-
changelog_uri: https://github.com/ahmetsaridogan/reqcord
|
|
198
|
+
changelog_uri: https://github.com/ahmetsaridogan/reqcord/blob/master/CHANGELOG.md
|
|
199
|
+
documentation_uri: https://github.com/ahmetsaridogan/reqcord/tree/master/docs
|
|
188
200
|
source_code_uri: https://github.com/ahmetsaridogan/reqcord
|
|
189
201
|
rubygems_mfa_required: 'true'
|
|
190
202
|
rdoc_options: []
|