reqcord 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/.gitignore +8 -0
- data/CHANGELOG.md +77 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +171 -0
- data/LICENSE.txt +21 -0
- data/README.md +239 -95
- data/Rakefile +13 -0
- data/docs/configuration.md +319 -0
- data/examples/complex-spec-app/.rspec +1 -0
- data/examples/complex-spec-app/README.md +21 -0
- data/examples/complex-spec-app/app.rb +363 -0
- data/examples/complex-spec-app/docs/api/README.md +104 -0
- data/examples/complex-spec-app/docs/api/api/v1/admin/products/create.md +117 -0
- data/examples/complex-spec-app/docs/api/api/v1/admin/products/destroy.md +51 -0
- data/examples/complex-spec-app/docs/api/api/v1/admin/products/index.md +11 -0
- data/examples/complex-spec-app/docs/api/api/v1/admin/products/list.md +106 -0
- data/examples/complex-spec-app/docs/api/api/v1/auth/index.md +9 -0
- data/examples/complex-spec-app/docs/api/api/v1/auth/login.md +76 -0
- data/examples/complex-spec-app/docs/api/api/v1/cart-items/create.md +91 -0
- data/examples/complex-spec-app/docs/api/api/v1/cart-items/destroy.md +51 -0
- data/examples/complex-spec-app/docs/api/api/v1/cart-items/index.md +10 -0
- data/examples/complex-spec-app/docs/api/api/v1/carts/checkout.md +102 -0
- data/examples/complex-spec-app/docs/api/api/v1/carts/index.md +10 -0
- data/examples/complex-spec-app/docs/api/api/v1/carts/show.md +73 -0
- data/examples/complex-spec-app/docs/api/api/v1/home/index.md +9 -0
- data/examples/complex-spec-app/docs/api/api/v1/home/list.md +47 -0
- data/examples/complex-spec-app/docs/api/api/v1/notes/create.md +94 -0
- data/examples/complex-spec-app/docs/api/api/v1/notes/index.md +10 -0
- data/examples/complex-spec-app/docs/api/api/v1/notes/list.md +61 -0
- data/examples/complex-spec-app/docs/api/api/v1/orders/cancel.md +69 -0
- data/examples/complex-spec-app/docs/api/api/v1/orders/create.md +150 -0
- data/examples/complex-spec-app/docs/api/api/v1/orders/destroy.md +37 -0
- data/examples/complex-spec-app/docs/api/api/v1/orders/index.md +13 -0
- data/examples/complex-spec-app/docs/api/api/v1/orders/list.md +78 -0
- data/examples/complex-spec-app/docs/api/api/v1/orders/show.md +83 -0
- data/examples/complex-spec-app/docs/api/api/v1/products/index.md +11 -0
- data/examples/complex-spec-app/docs/api/api/v1/products/list.md +83 -0
- data/examples/complex-spec-app/docs/api/api/v1/products/search.md +88 -0
- data/examples/complex-spec-app/docs/api/api/v1/products/show.md +71 -0
- data/examples/complex-spec-app/docs/api/api/v1/profiles/index.md +10 -0
- data/examples/complex-spec-app/docs/api/api/v1/profiles/show.md +61 -0
- data/examples/complex-spec-app/docs/api/api/v1/profiles/update.md +91 -0
- data/examples/complex-spec-app/docs/api/api/v2/products/index.md +9 -0
- data/examples/complex-spec-app/docs/api/api/v2/products/list.md +77 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/admin/products/create.sh +18 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/admin/products/destroy.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/admin/products/list.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/auth/login.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/cart-items/create.sh +16 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/cart-items/destroy.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/carts/checkout.sh +13 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/carts/show.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/home/list.sh +8 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/notes/create.sh +15 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/notes/list.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/orders/cancel.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/orders/create.sh +29 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/orders/destroy.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/orders/list.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/orders/show.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/products/list.sh +8 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/products/search.sh +8 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/products/show.sh +8 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/profiles/show.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/profiles/update.sh +16 -0
- data/examples/complex-spec-app/docs/api/curl/api/v2/products/list.sh +8 -0
- data/examples/complex-spec-app/docs/api/dataset.json +5116 -0
- data/examples/complex-spec-app/docs/api/postman/collection.json +2519 -0
- data/examples/complex-spec-app/generate.rb +14 -0
- data/examples/complex-spec-app/reqcord.yml +28 -0
- data/examples/complex-spec-app/spec/requests/admin_spec.rb +67 -0
- data/examples/complex-spec-app/spec/requests/cart_spec.rb +61 -0
- data/examples/complex-spec-app/spec/requests/home_and_auth_spec.rb +49 -0
- data/examples/complex-spec-app/spec/requests/orders_spec.rb +124 -0
- data/examples/complex-spec-app/spec/requests/products_spec.rb +65 -0
- data/examples/complex-spec-app/spec/spec_helper.rb +17 -0
- data/examples/complex-test-app/README.md +40 -0
- data/examples/complex-test-app/app.rb +384 -0
- data/examples/complex-test-app/docs/api/README.md +104 -0
- data/examples/complex-test-app/docs/api/api/v1/admin/products/create.md +117 -0
- data/examples/complex-test-app/docs/api/api/v1/admin/products/destroy.md +51 -0
- data/examples/complex-test-app/docs/api/api/v1/admin/products/index.md +11 -0
- data/examples/complex-test-app/docs/api/api/v1/admin/products/list.md +106 -0
- data/examples/complex-test-app/docs/api/api/v1/auth/index.md +9 -0
- data/examples/complex-test-app/docs/api/api/v1/auth/login.md +76 -0
- data/examples/complex-test-app/docs/api/api/v1/cart-items/create.md +91 -0
- data/examples/complex-test-app/docs/api/api/v1/cart-items/destroy.md +51 -0
- data/examples/complex-test-app/docs/api/api/v1/cart-items/index.md +10 -0
- data/examples/complex-test-app/docs/api/api/v1/carts/checkout.md +102 -0
- data/examples/complex-test-app/docs/api/api/v1/carts/index.md +10 -0
- data/examples/complex-test-app/docs/api/api/v1/carts/show.md +73 -0
- data/examples/complex-test-app/docs/api/api/v1/home/index.md +9 -0
- data/examples/complex-test-app/docs/api/api/v1/home/list.md +47 -0
- data/examples/complex-test-app/docs/api/api/v1/notes/create.md +94 -0
- data/examples/complex-test-app/docs/api/api/v1/notes/index.md +10 -0
- data/examples/complex-test-app/docs/api/api/v1/notes/list.md +61 -0
- data/examples/complex-test-app/docs/api/api/v1/orders/cancel.md +69 -0
- data/examples/complex-test-app/docs/api/api/v1/orders/create.md +150 -0
- data/examples/complex-test-app/docs/api/api/v1/orders/destroy.md +37 -0
- data/examples/complex-test-app/docs/api/api/v1/orders/index.md +13 -0
- data/examples/complex-test-app/docs/api/api/v1/orders/list.md +71 -0
- data/examples/complex-test-app/docs/api/api/v1/orders/show.md +83 -0
- data/examples/complex-test-app/docs/api/api/v1/products/index.md +11 -0
- data/examples/complex-test-app/docs/api/api/v1/products/list.md +83 -0
- data/examples/complex-test-app/docs/api/api/v1/products/search.md +88 -0
- data/examples/complex-test-app/docs/api/api/v1/products/show.md +71 -0
- data/examples/complex-test-app/docs/api/api/v1/profiles/index.md +10 -0
- data/examples/complex-test-app/docs/api/api/v1/profiles/show.md +61 -0
- data/examples/complex-test-app/docs/api/api/v1/profiles/update.md +91 -0
- data/examples/complex-test-app/docs/api/api/v2/products/index.md +9 -0
- data/examples/complex-test-app/docs/api/api/v2/products/list.md +77 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/admin/products/create.sh +18 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/admin/products/destroy.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/admin/products/list.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/auth/login.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/cart-items/create.sh +16 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/cart-items/destroy.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/carts/checkout.sh +13 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/carts/show.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/home/list.sh +8 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/notes/create.sh +15 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/notes/list.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/orders/cancel.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/orders/create.sh +29 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/orders/destroy.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/orders/list.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/orders/show.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/products/list.sh +8 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/products/search.sh +8 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/products/show.sh +8 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/profiles/show.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/profiles/update.sh +16 -0
- data/examples/complex-test-app/docs/api/curl/api/v2/products/list.sh +8 -0
- data/examples/complex-test-app/docs/api/dataset.json +5109 -0
- data/examples/complex-test-app/docs/api/postman/collection.json +2568 -0
- data/examples/complex-test-app/generate.rb +14 -0
- data/examples/complex-test-app/reqcord.yml +28 -0
- data/examples/complex-test-app/test/integration/admin_test.rb +70 -0
- data/examples/complex-test-app/test/integration/cart_test.rb +64 -0
- data/examples/complex-test-app/test/integration/home_and_auth_test.rb +53 -0
- data/examples/complex-test-app/test/integration/orders_test.rb +127 -0
- data/examples/complex-test-app/test/integration/products_test.rb +66 -0
- data/examples/reqcord.yml +58 -0
- data/examples/spec-app/.rspec +1 -0
- data/examples/spec-app/README.md +36 -0
- data/examples/spec-app/app.rb +209 -0
- data/examples/spec-app/docs/api/README.md +45 -0
- data/examples/spec-app/docs/api/api/v1/customers/create.md +95 -0
- data/examples/spec-app/docs/api/api/v1/customers/index.md +11 -0
- data/examples/spec-app/docs/api/api/v1/customers/list.md +67 -0
- data/examples/spec-app/docs/api/api/v1/customers/show.md +65 -0
- data/examples/spec-app/docs/api/api/v1/tasks/complete.md +51 -0
- data/examples/spec-app/docs/api/api/v1/tasks/create.md +90 -0
- data/examples/spec-app/docs/api/api/v1/tasks/destroy.md +35 -0
- data/examples/spec-app/docs/api/api/v1/tasks/index.md +14 -0
- data/examples/spec-app/docs/api/api/v1/tasks/list.md +65 -0
- data/examples/spec-app/docs/api/api/v1/tasks/show.md +65 -0
- data/examples/spec-app/docs/api/api/v1/tasks/update.md +74 -0
- data/examples/spec-app/docs/api/api/v1/users/create.md +95 -0
- data/examples/spec-app/docs/api/api/v1/users/index.md +11 -0
- data/examples/spec-app/docs/api/api/v1/users/list.md +67 -0
- data/examples/spec-app/docs/api/api/v1/users/show.md +65 -0
- data/examples/spec-app/docs/api/curl/api/v1/customers/create.sh +17 -0
- data/examples/spec-app/docs/api/curl/api/v1/customers/list.sh +9 -0
- data/examples/spec-app/docs/api/curl/api/v1/customers/show.sh +9 -0
- data/examples/spec-app/docs/api/curl/api/v1/tasks/complete.sh +8 -0
- data/examples/spec-app/docs/api/curl/api/v1/tasks/create.sh +15 -0
- data/examples/spec-app/docs/api/curl/api/v1/tasks/destroy.sh +8 -0
- data/examples/spec-app/docs/api/curl/api/v1/tasks/list.sh +8 -0
- data/examples/spec-app/docs/api/curl/api/v1/tasks/show.sh +8 -0
- data/examples/spec-app/docs/api/curl/api/v1/tasks/update.sh +14 -0
- data/examples/spec-app/docs/api/curl/api/v1/users/create.sh +17 -0
- data/examples/spec-app/docs/api/curl/api/v1/users/list.sh +9 -0
- data/examples/spec-app/docs/api/curl/api/v1/users/show.sh +9 -0
- data/examples/spec-app/docs/api/dataset.json +2402 -0
- data/examples/spec-app/docs/api/postman/collection.json +1290 -0
- data/examples/spec-app/generate.rb +14 -0
- data/examples/spec-app/reqcord.yml +27 -0
- data/examples/spec-app/spec/requests/customers_spec.rb +71 -0
- data/examples/spec-app/spec/requests/tasks_spec.rb +78 -0
- data/examples/spec-app/spec/requests/users_spec.rb +68 -0
- data/examples/spec-app/spec/spec_helper.rb +17 -0
- data/examples/test-app/README.md +57 -0
- data/examples/test-app/app.rb +212 -0
- data/examples/test-app/docs/api/README.md +45 -0
- data/examples/test-app/docs/api/api/v1/customers/create.md +95 -0
- data/examples/test-app/docs/api/api/v1/customers/index.md +11 -0
- data/examples/test-app/docs/api/api/v1/customers/list.md +67 -0
- data/examples/test-app/docs/api/api/v1/customers/show.md +65 -0
- data/examples/test-app/docs/api/api/v1/tasks/complete.md +51 -0
- data/examples/test-app/docs/api/api/v1/tasks/create.md +90 -0
- data/examples/test-app/docs/api/api/v1/tasks/destroy.md +35 -0
- data/examples/test-app/docs/api/api/v1/tasks/index.md +14 -0
- data/examples/test-app/docs/api/api/v1/tasks/list.md +65 -0
- data/examples/test-app/docs/api/api/v1/tasks/show.md +65 -0
- data/examples/test-app/docs/api/api/v1/tasks/update.md +74 -0
- data/examples/test-app/docs/api/api/v1/users/create.md +95 -0
- data/examples/test-app/docs/api/api/v1/users/index.md +11 -0
- data/examples/test-app/docs/api/api/v1/users/list.md +67 -0
- data/examples/test-app/docs/api/api/v1/users/show.md +65 -0
- data/examples/test-app/docs/api/curl/api/v1/customers/create.sh +17 -0
- data/examples/test-app/docs/api/curl/api/v1/customers/list.sh +9 -0
- data/examples/test-app/docs/api/curl/api/v1/customers/show.sh +9 -0
- data/examples/test-app/docs/api/curl/api/v1/tasks/complete.sh +8 -0
- data/examples/test-app/docs/api/curl/api/v1/tasks/create.sh +15 -0
- data/examples/test-app/docs/api/curl/api/v1/tasks/destroy.sh +8 -0
- data/examples/test-app/docs/api/curl/api/v1/tasks/list.sh +8 -0
- data/examples/test-app/docs/api/curl/api/v1/tasks/show.sh +8 -0
- data/examples/test-app/docs/api/curl/api/v1/tasks/update.sh +14 -0
- data/examples/test-app/docs/api/curl/api/v1/users/create.sh +17 -0
- data/examples/test-app/docs/api/curl/api/v1/users/list.sh +9 -0
- data/examples/test-app/docs/api/curl/api/v1/users/show.sh +9 -0
- data/examples/test-app/docs/api/dataset.json +2402 -0
- data/examples/test-app/docs/api/postman/collection.json +1290 -0
- data/examples/test-app/generate.rb +14 -0
- data/examples/test-app/reqcord.yml +28 -0
- data/examples/test-app/test/integration/customers_test.rb +72 -0
- data/examples/test-app/test/integration/tasks_test.rb +80 -0
- data/examples/test-app/test/integration/users_test.rb +72 -0
- data/lib/reqcord/capture/collector.rb +31 -0
- data/lib/reqcord/capture/integration_patch.rb +209 -0
- data/lib/reqcord/capture/minitest_context.rb +34 -0
- data/lib/reqcord/capture/rspec_context.rb +42 -0
- data/lib/reqcord/capture/test_context.rb +25 -0
- data/lib/reqcord/capture.rb +19 -0
- data/lib/reqcord/configuration.rb +198 -0
- data/lib/reqcord/dataset.rb +176 -0
- data/lib/reqcord/endpoint.rb +263 -0
- data/lib/reqcord/errors.rb +9 -0
- data/lib/reqcord/exporters/curl.rb +68 -0
- data/lib/reqcord/exporters/markdown.rb +295 -0
- data/lib/reqcord/exporters/postman.rb +206 -0
- data/lib/reqcord/exporters.rb +32 -0
- data/lib/reqcord/generator.rb +364 -0
- data/lib/reqcord/railtie.rb +51 -0
- data/lib/reqcord/renderers/curl.rb +56 -0
- data/lib/reqcord/renderers/payload.rb +69 -0
- data/lib/reqcord/request_example.rb +104 -0
- data/lib/reqcord/response_example.rb +72 -0
- data/lib/reqcord/route_collector.rb +242 -0
- data/lib/reqcord/sanitizers/sanitizer.rb +140 -0
- data/lib/reqcord/schema.rb +187 -0
- data/lib/reqcord/support.rb +58 -0
- data/lib/reqcord/version.rb +5 -0
- data/lib/reqcord.rb +78 -0
- data/lib/tasks/reqcord.rake +99 -0
- data/reqcord.gemspec +46 -0
- metadata +317 -3
data/README.md
CHANGED
|
@@ -19,9 +19,7 @@ Minitest Integration Tests
|
|
|
19
19
|
↓
|
|
20
20
|
Canonical Dataset
|
|
21
21
|
↓
|
|
22
|
-
Markdown
|
|
23
|
-
↓
|
|
24
|
-
cURL Examples
|
|
22
|
+
Markdown · cURL · Postman
|
|
25
23
|
```
|
|
26
24
|
|
|
27
25
|
## Why Reqcord?
|
|
@@ -92,13 +90,22 @@ It can generate documentation such as:
|
|
|
92
90
|
| X-Account-Id | `{{account_id}}` |
|
|
93
91
|
| Content-Type | `application/json` |
|
|
94
92
|
|
|
95
|
-
##
|
|
93
|
+
## Body Parameters
|
|
94
|
+
|
|
95
|
+
| Field | Type | Required | Values |
|
|
96
|
+
| --- | --- | --- | --- |
|
|
97
|
+
| `customer.name` | string | yes | `"John Doe"` |
|
|
98
|
+
| `customer.email` | string | yes | `"john@example.com"` |
|
|
99
|
+
| `customer.status` | string | yes | `"active"` \| `"passive"` |
|
|
100
|
+
|
|
101
|
+
## Example Request
|
|
96
102
|
|
|
97
103
|
```json
|
|
98
104
|
{
|
|
99
105
|
"customer": {
|
|
100
106
|
"name": "John Doe",
|
|
101
|
-
"email": "john@example.com"
|
|
107
|
+
"email": "john@example.com",
|
|
108
|
+
"status": "active"
|
|
102
109
|
}
|
|
103
110
|
}
|
|
104
111
|
```
|
|
@@ -107,27 +114,34 @@ It can generate documentation such as:
|
|
|
107
114
|
|
|
108
115
|
```bash
|
|
109
116
|
curl --request POST \
|
|
110
|
-
--url "
|
|
117
|
+
--url "http://localhost:3000/api/v2/customers" \
|
|
111
118
|
--header "Authorization: Bearer {{token}}" \
|
|
112
119
|
--header "X-Account-Id: {{account_id}}" \
|
|
113
120
|
--header "Content-Type: application/json" \
|
|
114
121
|
--data '{
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
122
|
+
"customer": {
|
|
123
|
+
"name": "John Doe",
|
|
124
|
+
"email": "john@example.com",
|
|
125
|
+
"status": "active"
|
|
126
|
+
}
|
|
127
|
+
}'
|
|
120
128
|
```
|
|
121
129
|
|
|
122
130
|
## Responses
|
|
123
131
|
|
|
124
132
|
### 201 Created
|
|
125
133
|
|
|
134
|
+
#### Fields
|
|
135
|
+
|
|
136
|
+
| Field | Type | Required | Values |
|
|
137
|
+
| --- | --- | --- | --- |
|
|
138
|
+
| `id` | integer | yes | `42` |
|
|
139
|
+
| `name` | string | yes | `"John Doe"` |
|
|
140
|
+
|
|
126
141
|
```json
|
|
127
142
|
{
|
|
128
143
|
"id": 42,
|
|
129
|
-
"name": "John Doe"
|
|
130
|
-
"email": "john@example.com"
|
|
144
|
+
"name": "John Doe"
|
|
131
145
|
}
|
|
132
146
|
```
|
|
133
147
|
|
|
@@ -139,7 +153,7 @@ curl --request POST \
|
|
|
139
153
|
}
|
|
140
154
|
```
|
|
141
155
|
|
|
142
|
-
### 422 Unprocessable
|
|
156
|
+
### 422 Unprocessable Content
|
|
143
157
|
|
|
144
158
|
```json
|
|
145
159
|
{
|
|
@@ -152,6 +166,28 @@ curl --request POST \
|
|
|
152
166
|
```
|
|
153
167
|
````
|
|
154
168
|
|
|
169
|
+
The parameter tables are inferred from the requests the application
|
|
170
|
+
**accepted**: two passing tests sent `"active"` and `"passive"`, a third sent
|
|
171
|
+
`"inactive"` and got a `422`, so the documentation lists the two values that
|
|
172
|
+
work and keeps the rejection only as a response example. The same run also
|
|
173
|
+
writes a runnable `curl/api/v2/customers/create.sh` and a Postman collection
|
|
174
|
+
with this request and its three saved responses.
|
|
175
|
+
|
|
176
|
+
## Examples
|
|
177
|
+
|
|
178
|
+
Two runnable examples live in [`examples/`](examples):
|
|
179
|
+
|
|
180
|
+
| Example | Test framework | What it shows |
|
|
181
|
+
| --- | --- | --- |
|
|
182
|
+
| [`examples/test-app`](examples/test-app) | Minitest | three small resources: auth, closed value sets, PATCH/PUT folding, a member action |
|
|
183
|
+
| [`examples/spec-app`](examples/spec-app) | RSpec | the same API, documented from request specs |
|
|
184
|
+
| [`examples/complex-test-app`](examples/complex-test-app) | Minitest | a store API: products, cart, orders, nested notes, array bodies, `filter[category]`, a form login, `X-Api-Key` admin namespace, two API versions, 400/403/404/409 |
|
|
185
|
+
| [`examples/complex-spec-app`](examples/complex-spec-app) | RSpec | the store API from request specs |
|
|
186
|
+
|
|
187
|
+
Each one ships the documentation it generates, so you can read the output
|
|
188
|
+
before running anything. [`examples/reqcord.yml`](examples/reqcord.yml) is an
|
|
189
|
+
annotated configuration file.
|
|
190
|
+
|
|
155
191
|
## Core Idea
|
|
156
192
|
|
|
157
193
|
Reqcord separates **capturing API behavior** from **rendering documentation**.
|
|
@@ -167,12 +203,9 @@ Minitest ──────► Test Adapter
|
|
|
167
203
|
▼
|
|
168
204
|
Reqcord Dataset
|
|
169
205
|
│
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
│
|
|
174
|
-
▼
|
|
175
|
-
cURL
|
|
206
|
+
┌────────────┼────────────┐
|
|
207
|
+
▼ ▼ ▼
|
|
208
|
+
Markdown cURL Postman
|
|
176
209
|
```
|
|
177
210
|
|
|
178
211
|
The internal dataset is framework-independent and output-independent.
|
|
@@ -211,13 +244,15 @@ docs/
|
|
|
211
244
|
|
|
212
245
|
## Configuration
|
|
213
246
|
|
|
214
|
-
Reqcord reads its configuration from `reqcord.yml` in the project root.
|
|
247
|
+
Reqcord reads its configuration from `reqcord.yml` in the project root. Every
|
|
248
|
+
key, default and environment override is described in
|
|
249
|
+
[docs/configuration.md](docs/configuration.md); the short version:
|
|
215
250
|
|
|
216
251
|
```yaml
|
|
217
252
|
version: 1
|
|
218
253
|
|
|
219
254
|
test:
|
|
220
|
-
framework: minitest
|
|
255
|
+
framework: minitest # or: rspec
|
|
221
256
|
|
|
222
257
|
routes:
|
|
223
258
|
prefix: /api
|
|
@@ -226,7 +261,9 @@ output:
|
|
|
226
261
|
directory: docs/api
|
|
227
262
|
|
|
228
263
|
exporters:
|
|
264
|
+
- curl
|
|
229
265
|
- markdown
|
|
266
|
+
- postman
|
|
230
267
|
|
|
231
268
|
variables:
|
|
232
269
|
base_url: http://localhost:3000
|
|
@@ -279,28 +316,122 @@ Combine filters:
|
|
|
279
316
|
bin/rails reqcord:generate RESOURCE=customers VERSION=v2
|
|
280
317
|
```
|
|
281
318
|
|
|
319
|
+
`reqcord:generate` runs the test suite itself, in a subprocess, with capture
|
|
320
|
+
enabled:
|
|
321
|
+
|
|
322
|
+
```text
|
|
323
|
+
bin/rails reqcord:generate
|
|
324
|
+
|
|
|
325
|
+
+-- collects the application's routes
|
|
326
|
+
|
|
|
327
|
+
+-- runs `test.command` with REQCORD_CAPTURE=1
|
|
328
|
+
| |
|
|
329
|
+
| +-- each request appends a JSON line to the capture file
|
|
330
|
+
|
|
|
331
|
+
+-- reads the capture file, sanitizes, writes the documentation
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Because capture is driven by `REQCORD_CAPTURE` and `REQCORD_CAPTURE_FILE`, an
|
|
335
|
+
ordinary `bin/rails test` patches nothing and writes nothing. The capture file
|
|
336
|
+
is append-only and locked per write, so parallel test workers can share it.
|
|
337
|
+
|
|
338
|
+
Point Reqcord at the tests that exercise the API — a directory is enough, it
|
|
339
|
+
picks the runner (`bin/rails test`, `rspec`, or a plain Ruby runner when the
|
|
340
|
+
project has no `bin/rails`):
|
|
341
|
+
|
|
342
|
+
```yaml
|
|
343
|
+
test:
|
|
344
|
+
framework: minitest
|
|
345
|
+
paths:
|
|
346
|
+
- test/integration
|
|
347
|
+
- test/api
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Or spell the command out; it wins over `paths`, and globs are expanded:
|
|
351
|
+
|
|
352
|
+
```yaml
|
|
353
|
+
test:
|
|
354
|
+
command: bin/rails test test/integration test/api/*_test.rb
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
The run ends with a reconciliation of the whole route table, so nothing can
|
|
358
|
+
go missing quietly:
|
|
359
|
+
|
|
360
|
+
```text
|
|
361
|
+
[reqcord] captured 87 request(s), 85 matched a documented route
|
|
362
|
+
[reqcord] captured a successful 2xx request for 15 of 16 endpoint(s)
|
|
363
|
+
[reqcord] routes: 18 = 15 documented + 1 uncovered + 2 skipped
|
|
364
|
+
[reqcord] skipped 2 route(s) that cannot be documented: 1 redirect, 1 mount
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Every route is in exactly one bucket: *documented* (a test got a `2xx`),
|
|
368
|
+
*uncovered* (listed in the index, no page), or *skipped* with its reason.
|
|
369
|
+
|
|
282
370
|
## Generated Files
|
|
283
371
|
|
|
284
|
-
|
|
372
|
+
Directories follow the controller path, so `admin/customers` and
|
|
373
|
+
`api/v2/customers` never collide:
|
|
285
374
|
|
|
286
375
|
```text
|
|
287
376
|
docs/api/
|
|
288
377
|
├── dataset.json
|
|
289
378
|
├── README.md
|
|
290
|
-
├── customers/
|
|
379
|
+
├── api/v2/customers/
|
|
291
380
|
│ ├── index.md
|
|
292
381
|
│ ├── create.md
|
|
293
382
|
│ ├── show.md
|
|
294
|
-
│
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
383
|
+
│ └── update.md
|
|
384
|
+
├── api/v2/surveys/
|
|
385
|
+
│ ├── index.md
|
|
386
|
+
│ └── list.md
|
|
387
|
+
├── curl/
|
|
388
|
+
│ └── api/v2/customers/
|
|
389
|
+
│ ├── create.sh
|
|
390
|
+
│ └── show.sh
|
|
391
|
+
└── postman/
|
|
392
|
+
└── collection.json
|
|
299
393
|
```
|
|
300
394
|
|
|
301
|
-
`dataset.json` contains Reqcord's normalized representation of the captured
|
|
395
|
+
`dataset.json` contains Reqcord's normalized representation of the captured
|
|
396
|
+
API; every exporter reads that and nothing else.
|
|
397
|
+
|
|
398
|
+
## Route coverage
|
|
399
|
+
|
|
400
|
+
The documented surface is the route table, not only `resources`. These all
|
|
401
|
+
become endpoints:
|
|
302
402
|
|
|
303
|
-
|
|
403
|
+
| Route | Documented as |
|
|
404
|
+
| --- | --- |
|
|
405
|
+
| `resources :customers` | one endpoint per action |
|
|
406
|
+
| `resource :cart` | `GET /cart`, `PATCH /cart` (also `PUT`) |
|
|
407
|
+
| `match "/echo", via: [:get, :post]` | `GET /echo` and `POST /echo` |
|
|
408
|
+
| `match "/anything", via: :all` | one endpoint per verb the tests used |
|
|
409
|
+
| `root to: "home#index"` | `GET /`, titled "Home" |
|
|
410
|
+
| `get "/items(/:id)"` | one endpoint, `:id` optional |
|
|
411
|
+
| `get "/files/*path"` | `path` as a path parameter |
|
|
412
|
+
| `mount Billing => "/billing"` | the engine's own routes, under `/billing` |
|
|
413
|
+
| `namespace :admin { resources :customers }` | `admin/customers/`, apart from `api/v2/customers/` |
|
|
414
|
+
|
|
415
|
+
`redirect(...)` routes and plain Rack mounts cannot be documented from a test;
|
|
416
|
+
they are counted as *skipped* in the report rather than dropped.
|
|
417
|
+
|
|
418
|
+
## Postman and Hoppscotch
|
|
419
|
+
|
|
420
|
+
`postman/collection.json` is a Postman Collection v2.1:
|
|
421
|
+
|
|
422
|
+
* one folder per controller namespace (`Api › V2 › Customers`),
|
|
423
|
+
* one request per documented endpoint, built from the successful captured
|
|
424
|
+
example — JSON bodies as `raw`, form bodies as `urlencoded`,
|
|
425
|
+
* every captured status saved as a response example on that request,
|
|
426
|
+
* collection variables for `base_url` and every placeholder the sanitizer
|
|
427
|
+
wrote (`{{token}}`, `{{api_key}}` …) — Postman's variable syntax is the
|
|
428
|
+
same, so the collection is usable as soon as the variables are filled in,
|
|
429
|
+
* `Authorization: Bearer {{token}}` lifted to collection-level bearer auth;
|
|
430
|
+
requests that were made without credentials are marked `noauth`, so they
|
|
431
|
+
replay exactly as their tests did.
|
|
432
|
+
|
|
433
|
+
Hoppscotch imports Postman v2.1 collections directly: *Import → Postman* and
|
|
434
|
+
point it at the same file.
|
|
304
435
|
|
|
305
436
|
## Request Capture
|
|
306
437
|
|
|
@@ -365,18 +496,18 @@ Responses
|
|
|
365
496
|
└── 422 Unprocessable Entity
|
|
366
497
|
```
|
|
367
498
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
For example:
|
|
499
|
+
Every distinct body captured for a status is kept in `dataset.json`, and the
|
|
500
|
+
fields of a response are inferred from all of them:
|
|
371
501
|
|
|
372
502
|
```text
|
|
373
|
-
422 Unprocessable
|
|
503
|
+
422 Unprocessable Content
|
|
374
504
|
├── Email already exists
|
|
375
505
|
├── Name is required
|
|
376
506
|
└── Invalid phone number
|
|
377
507
|
```
|
|
378
508
|
|
|
379
|
-
|
|
509
|
+
The Markdown page shows one example body per status plus the inferred field
|
|
510
|
+
table; Reqcord does not overwrite one `422` example with another.
|
|
380
511
|
|
|
381
512
|
## Sanitization
|
|
382
513
|
|
|
@@ -408,7 +539,15 @@ Authorization: Bearer {{token}}
|
|
|
408
539
|
|
|
409
540
|
Sensitive headers such as authorization credentials, cookies and API keys are treated specially by Reqcord.
|
|
410
541
|
|
|
411
|
-
Request and response
|
|
542
|
+
Request and response bodies follow the same principle, matched by key at any
|
|
543
|
+
depth:
|
|
544
|
+
|
|
545
|
+
```yaml
|
|
546
|
+
sanitize:
|
|
547
|
+
body:
|
|
548
|
+
password: "{{password}}"
|
|
549
|
+
access_token: "{{token}}"
|
|
550
|
+
```
|
|
412
551
|
|
|
413
552
|
## Canonical Dataset
|
|
414
553
|
|
|
@@ -430,45 +569,43 @@ A simplified endpoint representation looks like:
|
|
|
430
569
|
|
|
431
570
|
```json
|
|
432
571
|
{
|
|
572
|
+
"name": "Create Customer",
|
|
433
573
|
"method": "POST",
|
|
434
574
|
"path": "/api/v2/customers",
|
|
435
|
-
"
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
],
|
|
447
|
-
"response_examples": [
|
|
575
|
+
"controller": "api/v2/customers",
|
|
576
|
+
"action": "create",
|
|
577
|
+
"parameters": {
|
|
578
|
+
"path": [],
|
|
579
|
+
"query": [],
|
|
580
|
+
"body": [
|
|
581
|
+
{ "path": "customer.name", "type": "string", "required": true, "values": ["John Doe"] },
|
|
582
|
+
{ "path": "customer.status", "type": "string", "required": true, "values": ["active", "passive"] }
|
|
583
|
+
]
|
|
584
|
+
},
|
|
585
|
+
"responses": [
|
|
448
586
|
{
|
|
449
|
-
"name": "Created",
|
|
450
587
|
"status": 201,
|
|
451
|
-
"
|
|
452
|
-
"id": 42
|
|
453
|
-
|
|
454
|
-
}
|
|
588
|
+
"schema": [
|
|
589
|
+
{ "path": "id", "type": "integer", "required": true, "values": [42] }
|
|
590
|
+
],
|
|
591
|
+
"example": { "id": 42, "name": "John Doe" }
|
|
455
592
|
},
|
|
456
|
-
{
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
"error": "Unauthorized"
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
]
|
|
593
|
+
{ "status": 401, "schema": [ { "path": "error", "type": "string", "required": true, "values": ["Unauthorized"] } ], "example": { "error": "Unauthorized" } }
|
|
594
|
+
],
|
|
595
|
+
"request_examples": [ "… every captured request, sanitized" ],
|
|
596
|
+
"response_examples": [ "… every captured response, sanitized" ]
|
|
464
597
|
}
|
|
465
598
|
```
|
|
466
599
|
|
|
600
|
+
`parameters` and `responses[].schema` are inferred only from requests the
|
|
601
|
+
application accepted; `request_examples` keeps everything that was captured.
|
|
602
|
+
Routes no test reached are listed separately under `uncovered_routes`.
|
|
603
|
+
|
|
467
604
|
Every dataset contains a schema version so the internal format can evolve safely.
|
|
468
605
|
|
|
469
606
|
```json
|
|
470
607
|
{
|
|
471
|
-
"schema_version":
|
|
608
|
+
"schema_version": 2
|
|
472
609
|
}
|
|
473
610
|
```
|
|
474
611
|
|
|
@@ -478,26 +615,33 @@ Every dataset contains a schema version so the internal format can evolve safely
|
|
|
478
615
|
Reqcord
|
|
479
616
|
├── Configuration
|
|
480
617
|
├── Dataset
|
|
481
|
-
│ ├── Resource
|
|
618
|
+
│ ├── Resource (one controller path, nested directories/folders)
|
|
482
619
|
│ ├── Endpoint
|
|
483
620
|
│ ├── RequestExample
|
|
484
|
-
│
|
|
621
|
+
│ ├── ResponseExample
|
|
622
|
+
│ └── Schema (fields, types, required, closed value sets)
|
|
485
623
|
│
|
|
486
|
-
├── RouteCollector
|
|
624
|
+
├── RouteCollector (every route kind, engines walked, skips counted)
|
|
487
625
|
│
|
|
488
|
-
├──
|
|
489
|
-
│
|
|
626
|
+
├── Capture
|
|
627
|
+
│ ├── Collector (NDJSON, one line per exchange)
|
|
628
|
+
│ ├── TestContext
|
|
629
|
+
│ ├── MinitestContext / RSpecContext
|
|
630
|
+
│ └── IntegrationPatch
|
|
631
|
+
│
|
|
632
|
+
├── Generator (run tests → dataset → exporters → report)
|
|
490
633
|
│
|
|
491
634
|
├── Sanitizers
|
|
492
|
-
│
|
|
493
|
-
│ ├── RequestBody
|
|
494
|
-
│ └── ResponseBody
|
|
635
|
+
│ └── Sanitizer (headers and bodies)
|
|
495
636
|
│
|
|
496
637
|
├── Renderers
|
|
638
|
+
│ ├── Payload (JSON vs form, nested query flattening)
|
|
497
639
|
│ └── Curl
|
|
498
640
|
│
|
|
499
641
|
└── Exporters
|
|
500
|
-
|
|
642
|
+
├── Markdown
|
|
643
|
+
├── Curl (one .sh per endpoint)
|
|
644
|
+
└── Postman (Collection v2.1, also for Hoppscotch)
|
|
501
645
|
```
|
|
502
646
|
|
|
503
647
|
Test adapters are responsible only for converting test execution into Reqcord's canonical model.
|
|
@@ -508,9 +652,9 @@ Exporters know nothing about Minitest or Rails test internals.
|
|
|
508
652
|
Minitest ──┐
|
|
509
653
|
│
|
|
510
654
|
RSpec ─────┼──► Dataset ──► Markdown
|
|
511
|
-
│ ├─►
|
|
512
|
-
Other ─────┘ ├─► Postman
|
|
513
|
-
└─►
|
|
655
|
+
│ ├─► cURL
|
|
656
|
+
Other ─────┘ ├─► Postman (→ Hoppscotch)
|
|
657
|
+
└─► OpenAPI (0.2)
|
|
514
658
|
```
|
|
515
659
|
|
|
516
660
|
## v0.1 Scope
|
|
@@ -520,28 +664,28 @@ The first Reqcord release focuses on proving the capture pipeline.
|
|
|
520
664
|
### Included
|
|
521
665
|
|
|
522
666
|
* Rails 8
|
|
523
|
-
* Minitest integration
|
|
667
|
+
* Minitest integration tests
|
|
668
|
+
* RSpec request specs
|
|
524
669
|
* Rails route discovery
|
|
525
670
|
* `reqcord.yml`
|
|
526
671
|
* Request capture
|
|
527
672
|
* Response capture
|
|
528
673
|
* Multiple response scenarios
|
|
529
674
|
* Sensitive data sanitization
|
|
530
|
-
* Canonical `dataset.json`
|
|
675
|
+
* Canonical `dataset.json` with inferred request parameters and response fields
|
|
531
676
|
* Markdown documentation
|
|
532
|
-
* Generated cURL requests
|
|
677
|
+
* Generated cURL requests (in the Markdown and as runnable `.sh` files)
|
|
678
|
+
* Postman Collection v2.1 (imports into Hoppscotch as well)
|
|
679
|
+
* The whole route table: custom actions, `match via:`, `via: :all`,
|
|
680
|
+
singular resources, optional segments and globs, mounted engines
|
|
533
681
|
* Resource filtering
|
|
534
682
|
* API version filtering
|
|
535
683
|
|
|
536
684
|
### Not included yet
|
|
537
685
|
|
|
538
|
-
* RSpec adapter
|
|
539
686
|
* OpenAPI generation
|
|
540
687
|
* Scalar integration
|
|
541
|
-
* Postman collections
|
|
542
|
-
* Hoppscotch collections
|
|
543
688
|
* Multipart requests
|
|
544
|
-
* Advanced schema inference
|
|
545
689
|
* CI documentation drift detection
|
|
546
690
|
|
|
547
691
|
These features belong to later releases rather than expanding the initial scope.
|
|
@@ -568,22 +712,13 @@ http://localhost:3000/api-docs
|
|
|
568
712
|
|
|
569
713
|
### v0.3
|
|
570
714
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
Both test frameworks will produce the exact same Reqcord dataset:
|
|
574
|
-
|
|
575
|
-
```text
|
|
576
|
-
Minitest ─┐
|
|
577
|
-
├──► Reqcord Dataset
|
|
578
|
-
RSpec ────┘
|
|
579
|
-
```
|
|
715
|
+
Rack::Test capture, so frameworks other than Rails (Sinatra, Roda, Hanami) can
|
|
716
|
+
be documented from the same dataset.
|
|
580
717
|
|
|
581
718
|
### Future
|
|
582
719
|
|
|
583
720
|
Potential exporters and integrations include:
|
|
584
721
|
|
|
585
|
-
* Postman
|
|
586
|
-
* Hoppscotch
|
|
587
722
|
* Bruno
|
|
588
723
|
* Insomnia
|
|
589
724
|
* `llms.txt`
|
|
@@ -619,10 +754,19 @@ Reqcord is currently in early development.
|
|
|
619
754
|
|
|
620
755
|
The initial goal is intentionally narrow:
|
|
621
756
|
|
|
622
|
-
> Capture real Rails API requests and responses from
|
|
757
|
+
> Capture real Rails API requests and responses from the test suite and generate accurate, sanitized Markdown documentation, executable cURL examples and a Postman collection — without the developer writing any of them by hand.
|
|
623
758
|
|
|
624
759
|
Once that pipeline is reliable, additional adapters and exporters can be built on top of the same dataset.
|
|
625
760
|
|
|
626
761
|
## License
|
|
627
762
|
|
|
628
763
|
Reqcord is available as open source under the terms of the MIT License.
|
|
764
|
+
|
|
765
|
+
## cURL source of truth
|
|
766
|
+
|
|
767
|
+
Reqcord does not invent request payloads. For Rails integration tests, the
|
|
768
|
+
arguments passed to `get`, `post`, `put`, `patch`, and `delete` are captured at
|
|
769
|
+
runtime. Generated cURL commands use a successful `2xx` test case whenever one
|
|
770
|
+
exists, including its concrete URL, request headers, query parameters, and
|
|
771
|
+
payload. Error-case payloads remain available as examples but do not replace
|
|
772
|
+
the canonical successful request.
|
data/Rakefile
ADDED