reqcord 0.1.1 → 0.1.3

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/Gemfile +5 -0
  4. data/README.md +13 -0
  5. data/docs/configuration.md +14 -0
  6. data/examples/reqcord.yml +4 -2
  7. data/gemfiles/rails_7.1.gemfile +13 -0
  8. data/gemfiles/rails_7.2.gemfile +13 -0
  9. data/gemfiles/rails_8.0.gemfile +13 -0
  10. data/gemfiles/rails_8.1.gemfile +13 -0
  11. data/lib/reqcord/configuration.rb +7 -1
  12. data/lib/reqcord/generator.rb +1 -1
  13. data/lib/reqcord/route_collector.rb +4 -2
  14. data/lib/reqcord/version.rb +1 -1
  15. data/lib/tasks/reqcord.rake +29 -2
  16. data/reqcord.gemspec +18 -4
  17. metadata +65 -217
  18. data/.gitignore +0 -8
  19. data/Gemfile.lock +0 -171
  20. data/examples/complex-spec-app/.rspec +0 -1
  21. data/examples/complex-spec-app/README.md +0 -21
  22. data/examples/complex-spec-app/app.rb +0 -363
  23. data/examples/complex-spec-app/docs/api/README.md +0 -104
  24. data/examples/complex-spec-app/docs/api/api/v1/admin/products/create.md +0 -117
  25. data/examples/complex-spec-app/docs/api/api/v1/admin/products/destroy.md +0 -51
  26. data/examples/complex-spec-app/docs/api/api/v1/admin/products/index.md +0 -11
  27. data/examples/complex-spec-app/docs/api/api/v1/admin/products/list.md +0 -106
  28. data/examples/complex-spec-app/docs/api/api/v1/auth/index.md +0 -9
  29. data/examples/complex-spec-app/docs/api/api/v1/auth/login.md +0 -76
  30. data/examples/complex-spec-app/docs/api/api/v1/cart-items/create.md +0 -91
  31. data/examples/complex-spec-app/docs/api/api/v1/cart-items/destroy.md +0 -51
  32. data/examples/complex-spec-app/docs/api/api/v1/cart-items/index.md +0 -10
  33. data/examples/complex-spec-app/docs/api/api/v1/carts/checkout.md +0 -102
  34. data/examples/complex-spec-app/docs/api/api/v1/carts/index.md +0 -10
  35. data/examples/complex-spec-app/docs/api/api/v1/carts/show.md +0 -73
  36. data/examples/complex-spec-app/docs/api/api/v1/home/index.md +0 -9
  37. data/examples/complex-spec-app/docs/api/api/v1/home/list.md +0 -47
  38. data/examples/complex-spec-app/docs/api/api/v1/notes/create.md +0 -94
  39. data/examples/complex-spec-app/docs/api/api/v1/notes/index.md +0 -10
  40. data/examples/complex-spec-app/docs/api/api/v1/notes/list.md +0 -61
  41. data/examples/complex-spec-app/docs/api/api/v1/orders/cancel.md +0 -69
  42. data/examples/complex-spec-app/docs/api/api/v1/orders/create.md +0 -150
  43. data/examples/complex-spec-app/docs/api/api/v1/orders/destroy.md +0 -37
  44. data/examples/complex-spec-app/docs/api/api/v1/orders/index.md +0 -13
  45. data/examples/complex-spec-app/docs/api/api/v1/orders/list.md +0 -78
  46. data/examples/complex-spec-app/docs/api/api/v1/orders/show.md +0 -83
  47. data/examples/complex-spec-app/docs/api/api/v1/products/index.md +0 -11
  48. data/examples/complex-spec-app/docs/api/api/v1/products/list.md +0 -83
  49. data/examples/complex-spec-app/docs/api/api/v1/products/search.md +0 -88
  50. data/examples/complex-spec-app/docs/api/api/v1/products/show.md +0 -71
  51. data/examples/complex-spec-app/docs/api/api/v1/profiles/index.md +0 -10
  52. data/examples/complex-spec-app/docs/api/api/v1/profiles/show.md +0 -61
  53. data/examples/complex-spec-app/docs/api/api/v1/profiles/update.md +0 -91
  54. data/examples/complex-spec-app/docs/api/api/v2/products/index.md +0 -9
  55. data/examples/complex-spec-app/docs/api/api/v2/products/list.md +0 -77
  56. data/examples/complex-spec-app/docs/api/curl/api/v1/admin/products/create.sh +0 -18
  57. data/examples/complex-spec-app/docs/api/curl/api/v1/admin/products/destroy.sh +0 -9
  58. data/examples/complex-spec-app/docs/api/curl/api/v1/admin/products/list.sh +0 -9
  59. data/examples/complex-spec-app/docs/api/curl/api/v1/auth/login.sh +0 -9
  60. data/examples/complex-spec-app/docs/api/curl/api/v1/cart-items/create.sh +0 -16
  61. data/examples/complex-spec-app/docs/api/curl/api/v1/cart-items/destroy.sh +0 -9
  62. data/examples/complex-spec-app/docs/api/curl/api/v1/carts/checkout.sh +0 -13
  63. data/examples/complex-spec-app/docs/api/curl/api/v1/carts/show.sh +0 -9
  64. data/examples/complex-spec-app/docs/api/curl/api/v1/home/list.sh +0 -8
  65. data/examples/complex-spec-app/docs/api/curl/api/v1/notes/create.sh +0 -15
  66. data/examples/complex-spec-app/docs/api/curl/api/v1/notes/list.sh +0 -9
  67. data/examples/complex-spec-app/docs/api/curl/api/v1/orders/cancel.sh +0 -9
  68. data/examples/complex-spec-app/docs/api/curl/api/v1/orders/create.sh +0 -29
  69. data/examples/complex-spec-app/docs/api/curl/api/v1/orders/destroy.sh +0 -9
  70. data/examples/complex-spec-app/docs/api/curl/api/v1/orders/list.sh +0 -9
  71. data/examples/complex-spec-app/docs/api/curl/api/v1/orders/show.sh +0 -9
  72. data/examples/complex-spec-app/docs/api/curl/api/v1/products/list.sh +0 -8
  73. data/examples/complex-spec-app/docs/api/curl/api/v1/products/search.sh +0 -8
  74. data/examples/complex-spec-app/docs/api/curl/api/v1/products/show.sh +0 -8
  75. data/examples/complex-spec-app/docs/api/curl/api/v1/profiles/show.sh +0 -9
  76. data/examples/complex-spec-app/docs/api/curl/api/v1/profiles/update.sh +0 -16
  77. data/examples/complex-spec-app/docs/api/curl/api/v2/products/list.sh +0 -8
  78. data/examples/complex-spec-app/docs/api/dataset.json +0 -5116
  79. data/examples/complex-spec-app/docs/api/postman/collection.json +0 -2519
  80. data/examples/complex-spec-app/generate.rb +0 -14
  81. data/examples/complex-spec-app/reqcord.yml +0 -28
  82. data/examples/complex-spec-app/spec/requests/admin_spec.rb +0 -67
  83. data/examples/complex-spec-app/spec/requests/cart_spec.rb +0 -61
  84. data/examples/complex-spec-app/spec/requests/home_and_auth_spec.rb +0 -49
  85. data/examples/complex-spec-app/spec/requests/orders_spec.rb +0 -124
  86. data/examples/complex-spec-app/spec/requests/products_spec.rb +0 -65
  87. data/examples/complex-spec-app/spec/spec_helper.rb +0 -17
  88. data/examples/complex-test-app/README.md +0 -40
  89. data/examples/complex-test-app/app.rb +0 -384
  90. data/examples/complex-test-app/docs/api/README.md +0 -104
  91. data/examples/complex-test-app/docs/api/api/v1/admin/products/create.md +0 -117
  92. data/examples/complex-test-app/docs/api/api/v1/admin/products/destroy.md +0 -51
  93. data/examples/complex-test-app/docs/api/api/v1/admin/products/index.md +0 -11
  94. data/examples/complex-test-app/docs/api/api/v1/admin/products/list.md +0 -106
  95. data/examples/complex-test-app/docs/api/api/v1/auth/index.md +0 -9
  96. data/examples/complex-test-app/docs/api/api/v1/auth/login.md +0 -76
  97. data/examples/complex-test-app/docs/api/api/v1/cart-items/create.md +0 -91
  98. data/examples/complex-test-app/docs/api/api/v1/cart-items/destroy.md +0 -51
  99. data/examples/complex-test-app/docs/api/api/v1/cart-items/index.md +0 -10
  100. data/examples/complex-test-app/docs/api/api/v1/carts/checkout.md +0 -102
  101. data/examples/complex-test-app/docs/api/api/v1/carts/index.md +0 -10
  102. data/examples/complex-test-app/docs/api/api/v1/carts/show.md +0 -73
  103. data/examples/complex-test-app/docs/api/api/v1/home/index.md +0 -9
  104. data/examples/complex-test-app/docs/api/api/v1/home/list.md +0 -47
  105. data/examples/complex-test-app/docs/api/api/v1/notes/create.md +0 -94
  106. data/examples/complex-test-app/docs/api/api/v1/notes/index.md +0 -10
  107. data/examples/complex-test-app/docs/api/api/v1/notes/list.md +0 -61
  108. data/examples/complex-test-app/docs/api/api/v1/orders/cancel.md +0 -69
  109. data/examples/complex-test-app/docs/api/api/v1/orders/create.md +0 -150
  110. data/examples/complex-test-app/docs/api/api/v1/orders/destroy.md +0 -37
  111. data/examples/complex-test-app/docs/api/api/v1/orders/index.md +0 -13
  112. data/examples/complex-test-app/docs/api/api/v1/orders/list.md +0 -71
  113. data/examples/complex-test-app/docs/api/api/v1/orders/show.md +0 -83
  114. data/examples/complex-test-app/docs/api/api/v1/products/index.md +0 -11
  115. data/examples/complex-test-app/docs/api/api/v1/products/list.md +0 -83
  116. data/examples/complex-test-app/docs/api/api/v1/products/search.md +0 -88
  117. data/examples/complex-test-app/docs/api/api/v1/products/show.md +0 -71
  118. data/examples/complex-test-app/docs/api/api/v1/profiles/index.md +0 -10
  119. data/examples/complex-test-app/docs/api/api/v1/profiles/show.md +0 -61
  120. data/examples/complex-test-app/docs/api/api/v1/profiles/update.md +0 -91
  121. data/examples/complex-test-app/docs/api/api/v2/products/index.md +0 -9
  122. data/examples/complex-test-app/docs/api/api/v2/products/list.md +0 -77
  123. data/examples/complex-test-app/docs/api/curl/api/v1/admin/products/create.sh +0 -18
  124. data/examples/complex-test-app/docs/api/curl/api/v1/admin/products/destroy.sh +0 -9
  125. data/examples/complex-test-app/docs/api/curl/api/v1/admin/products/list.sh +0 -9
  126. data/examples/complex-test-app/docs/api/curl/api/v1/auth/login.sh +0 -9
  127. data/examples/complex-test-app/docs/api/curl/api/v1/cart-items/create.sh +0 -16
  128. data/examples/complex-test-app/docs/api/curl/api/v1/cart-items/destroy.sh +0 -9
  129. data/examples/complex-test-app/docs/api/curl/api/v1/carts/checkout.sh +0 -13
  130. data/examples/complex-test-app/docs/api/curl/api/v1/carts/show.sh +0 -9
  131. data/examples/complex-test-app/docs/api/curl/api/v1/home/list.sh +0 -8
  132. data/examples/complex-test-app/docs/api/curl/api/v1/notes/create.sh +0 -15
  133. data/examples/complex-test-app/docs/api/curl/api/v1/notes/list.sh +0 -9
  134. data/examples/complex-test-app/docs/api/curl/api/v1/orders/cancel.sh +0 -9
  135. data/examples/complex-test-app/docs/api/curl/api/v1/orders/create.sh +0 -29
  136. data/examples/complex-test-app/docs/api/curl/api/v1/orders/destroy.sh +0 -9
  137. data/examples/complex-test-app/docs/api/curl/api/v1/orders/list.sh +0 -9
  138. data/examples/complex-test-app/docs/api/curl/api/v1/orders/show.sh +0 -9
  139. data/examples/complex-test-app/docs/api/curl/api/v1/products/list.sh +0 -8
  140. data/examples/complex-test-app/docs/api/curl/api/v1/products/search.sh +0 -8
  141. data/examples/complex-test-app/docs/api/curl/api/v1/products/show.sh +0 -8
  142. data/examples/complex-test-app/docs/api/curl/api/v1/profiles/show.sh +0 -9
  143. data/examples/complex-test-app/docs/api/curl/api/v1/profiles/update.sh +0 -16
  144. data/examples/complex-test-app/docs/api/curl/api/v2/products/list.sh +0 -8
  145. data/examples/complex-test-app/docs/api/dataset.json +0 -5109
  146. data/examples/complex-test-app/docs/api/postman/collection.json +0 -2568
  147. data/examples/complex-test-app/generate.rb +0 -14
  148. data/examples/complex-test-app/reqcord.yml +0 -28
  149. data/examples/complex-test-app/test/integration/admin_test.rb +0 -70
  150. data/examples/complex-test-app/test/integration/cart_test.rb +0 -64
  151. data/examples/complex-test-app/test/integration/home_and_auth_test.rb +0 -53
  152. data/examples/complex-test-app/test/integration/orders_test.rb +0 -127
  153. data/examples/complex-test-app/test/integration/products_test.rb +0 -66
  154. data/examples/spec-app/.rspec +0 -1
  155. data/examples/spec-app/README.md +0 -36
  156. data/examples/spec-app/app.rb +0 -209
  157. data/examples/spec-app/docs/api/README.md +0 -45
  158. data/examples/spec-app/docs/api/api/v1/customers/create.md +0 -95
  159. data/examples/spec-app/docs/api/api/v1/customers/index.md +0 -11
  160. data/examples/spec-app/docs/api/api/v1/customers/list.md +0 -67
  161. data/examples/spec-app/docs/api/api/v1/customers/show.md +0 -65
  162. data/examples/spec-app/docs/api/api/v1/tasks/complete.md +0 -51
  163. data/examples/spec-app/docs/api/api/v1/tasks/create.md +0 -90
  164. data/examples/spec-app/docs/api/api/v1/tasks/destroy.md +0 -35
  165. data/examples/spec-app/docs/api/api/v1/tasks/index.md +0 -14
  166. data/examples/spec-app/docs/api/api/v1/tasks/list.md +0 -65
  167. data/examples/spec-app/docs/api/api/v1/tasks/show.md +0 -65
  168. data/examples/spec-app/docs/api/api/v1/tasks/update.md +0 -74
  169. data/examples/spec-app/docs/api/api/v1/users/create.md +0 -95
  170. data/examples/spec-app/docs/api/api/v1/users/index.md +0 -11
  171. data/examples/spec-app/docs/api/api/v1/users/list.md +0 -67
  172. data/examples/spec-app/docs/api/api/v1/users/show.md +0 -65
  173. data/examples/spec-app/docs/api/curl/api/v1/customers/create.sh +0 -17
  174. data/examples/spec-app/docs/api/curl/api/v1/customers/list.sh +0 -9
  175. data/examples/spec-app/docs/api/curl/api/v1/customers/show.sh +0 -9
  176. data/examples/spec-app/docs/api/curl/api/v1/tasks/complete.sh +0 -8
  177. data/examples/spec-app/docs/api/curl/api/v1/tasks/create.sh +0 -15
  178. data/examples/spec-app/docs/api/curl/api/v1/tasks/destroy.sh +0 -8
  179. data/examples/spec-app/docs/api/curl/api/v1/tasks/list.sh +0 -8
  180. data/examples/spec-app/docs/api/curl/api/v1/tasks/show.sh +0 -8
  181. data/examples/spec-app/docs/api/curl/api/v1/tasks/update.sh +0 -14
  182. data/examples/spec-app/docs/api/curl/api/v1/users/create.sh +0 -17
  183. data/examples/spec-app/docs/api/curl/api/v1/users/list.sh +0 -9
  184. data/examples/spec-app/docs/api/curl/api/v1/users/show.sh +0 -9
  185. data/examples/spec-app/docs/api/dataset.json +0 -2402
  186. data/examples/spec-app/docs/api/postman/collection.json +0 -1290
  187. data/examples/spec-app/generate.rb +0 -14
  188. data/examples/spec-app/reqcord.yml +0 -27
  189. data/examples/spec-app/spec/requests/customers_spec.rb +0 -71
  190. data/examples/spec-app/spec/requests/tasks_spec.rb +0 -78
  191. data/examples/spec-app/spec/requests/users_spec.rb +0 -68
  192. data/examples/spec-app/spec/spec_helper.rb +0 -17
  193. data/examples/test-app/README.md +0 -57
  194. data/examples/test-app/app.rb +0 -212
  195. data/examples/test-app/docs/api/README.md +0 -45
  196. data/examples/test-app/docs/api/api/v1/customers/create.md +0 -95
  197. data/examples/test-app/docs/api/api/v1/customers/index.md +0 -11
  198. data/examples/test-app/docs/api/api/v1/customers/list.md +0 -67
  199. data/examples/test-app/docs/api/api/v1/customers/show.md +0 -65
  200. data/examples/test-app/docs/api/api/v1/tasks/complete.md +0 -51
  201. data/examples/test-app/docs/api/api/v1/tasks/create.md +0 -90
  202. data/examples/test-app/docs/api/api/v1/tasks/destroy.md +0 -35
  203. data/examples/test-app/docs/api/api/v1/tasks/index.md +0 -14
  204. data/examples/test-app/docs/api/api/v1/tasks/list.md +0 -65
  205. data/examples/test-app/docs/api/api/v1/tasks/show.md +0 -65
  206. data/examples/test-app/docs/api/api/v1/tasks/update.md +0 -74
  207. data/examples/test-app/docs/api/api/v1/users/create.md +0 -95
  208. data/examples/test-app/docs/api/api/v1/users/index.md +0 -11
  209. data/examples/test-app/docs/api/api/v1/users/list.md +0 -67
  210. data/examples/test-app/docs/api/api/v1/users/show.md +0 -65
  211. data/examples/test-app/docs/api/curl/api/v1/customers/create.sh +0 -17
  212. data/examples/test-app/docs/api/curl/api/v1/customers/list.sh +0 -9
  213. data/examples/test-app/docs/api/curl/api/v1/customers/show.sh +0 -9
  214. data/examples/test-app/docs/api/curl/api/v1/tasks/complete.sh +0 -8
  215. data/examples/test-app/docs/api/curl/api/v1/tasks/create.sh +0 -15
  216. data/examples/test-app/docs/api/curl/api/v1/tasks/destroy.sh +0 -8
  217. data/examples/test-app/docs/api/curl/api/v1/tasks/list.sh +0 -8
  218. data/examples/test-app/docs/api/curl/api/v1/tasks/show.sh +0 -8
  219. data/examples/test-app/docs/api/curl/api/v1/tasks/update.sh +0 -14
  220. data/examples/test-app/docs/api/curl/api/v1/users/create.sh +0 -17
  221. data/examples/test-app/docs/api/curl/api/v1/users/list.sh +0 -9
  222. data/examples/test-app/docs/api/curl/api/v1/users/show.sh +0 -9
  223. data/examples/test-app/docs/api/dataset.json +0 -2402
  224. data/examples/test-app/docs/api/postman/collection.json +0 -1290
  225. data/examples/test-app/generate.rb +0 -14
  226. data/examples/test-app/reqcord.yml +0 -28
  227. data/examples/test-app/test/integration/customers_test.rb +0 -72
  228. data/examples/test-app/test/integration/tasks_test.rb +0 -80
  229. data/examples/test-app/test/integration/users_test.rb +0 -72
@@ -1,384 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # A store API in one file: closer to a real application than test-app, so the
4
- # generated documentation shows the harder cases.
5
- #
6
- # GET /api/v1 root inside a namespace ("Home")
7
- # POST /api/v1/auth/login form-encoded, token in the response body
8
- # GET|PATCH /api/v1/profile singular resource, PATCH and PUT folded
9
- # GET /api/v1/products filter[category], sort, page, per_page
10
- # GET /api/v1/products/search collection action, 400 without q
11
- # GET /api/v1/orders/:id 403 for someone else's order
12
- # POST /api/v1/orders line_items[] and a nested address
13
- # POST /api/v1/orders/:id/cancel 409 once shipped
14
- # GET /api/v1/orders/:order_id/notes nested resource
15
- # GET /api/v1/cart singular resource with nested items
16
- # POST /api/v1/cart/items add by sku, DELETE /cart/items/:sku
17
- # POST /api/v1/cart/checkout payment_method: "card" | "bank_transfer"
18
- # */api/v1/admin/products X-Api-Key, 409 on a duplicate sku
19
- # GET /api/v2/products a second API version, cursor paging
20
- ENV["RAILS_ENV"] ||= "test"
21
-
22
- # Only needed because this example lives inside the Reqcord repository.
23
- # In your own application, `gem "reqcord"` in the Gemfile is enough.
24
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
25
-
26
- require "rails"
27
- require "action_controller/railtie"
28
- require "reqcord"
29
-
30
- class ComplexApp < Rails::Application
31
- config.root = __dir__
32
- config.eager_load = false
33
- config.logger = Logger.new(IO::NULL)
34
- config.secret_key_base = "a" * 64
35
- config.hosts.clear
36
- end
37
-
38
- Rails.application.initialize!
39
-
40
- module Store
41
- PRODUCTS = [
42
- { id: 1, sku: "TEA-001", name: "Earl Grey", category: "tea", price_cents: 1200, cost_cents: 500, tags: %w[black bergamot] },
43
- { id: 2, sku: "TEA-002", name: "Sencha", category: "tea", price_cents: 1500, cost_cents: 700, tags: %w[green] },
44
- { id: 3, sku: "MUG-001", name: "Stoneware Mug", category: "mugs", price_cents: 2400, cost_cents: 900, tags: %w[ceramic] },
45
- { id: 4, sku: "MUG-002", name: "Travel Mug", category: "mugs", price_cents: 3200, cost_cents: 1400, tags: %w[steel insulated] }
46
- ].freeze
47
-
48
- PUBLIC_PRODUCT = %i[id sku name category price_cents tags].freeze
49
-
50
- ORDERS = [
51
- { id: 1, user_id: 1, status: "pending", currency: "USD", total_cents: 2700 },
52
- { id: 2, user_id: 1, status: "shipped", currency: "USD", total_cents: 3200 },
53
- { id: 3, user_id: 2, status: "pending", currency: "EUR", total_cents: 1500 }
54
- ].freeze
55
-
56
- NOTES = [
57
- { id: 1, order_id: 1, body: "Leave at the door", author: "ada" },
58
- { id: 2, order_id: 1, body: "Gift wrap please", author: "ada" }
59
- ].freeze
60
-
61
- PROFILE = { id: 1, name: "Ada Lovelace", email: "ada@example.com", locale: "en" }.freeze
62
- LOCALES = %w[en tr].freeze
63
-
64
- CART_ITEMS = [
65
- { sku: "TEA-001", name: "Earl Grey", quantity: 2, unit_price_cents: 1200 },
66
- { sku: "MUG-001", name: "Stoneware Mug", quantity: 1, unit_price_cents: 2400 }
67
- ].freeze
68
- PAYMENT_METHODS = %w[card bank_transfer].freeze
69
- end
70
-
71
- module Api
72
- module V1
73
- class BaseController < ActionController::API
74
- private
75
-
76
- def authenticate!
77
- return if request.headers["Authorization"].present?
78
-
79
- render json: { error: "Unauthorized" }, status: :unauthorized
80
- end
81
-
82
- def not_found
83
- render json: { error: "Not Found" }, status: :not_found
84
- end
85
- end
86
-
87
- class HomeController < BaseController
88
- def index
89
- render json: { name: "Store API", version: "v1", links: { products: "/api/v1/products", orders: "/api/v1/orders" } }
90
- end
91
- end
92
-
93
- class AuthController < BaseController
94
- # A classic form login: the test posts a form, not JSON.
95
- def login
96
- if params[:email] == "ada@example.com" && params[:password] == "correct-horse-battery"
97
- render json: { token: "tok_live_9f8e7d6c", token_type: "Bearer", expires_in: 3600 }
98
- else
99
- render json: { error: "Invalid email or password" }, status: :unauthorized
100
- end
101
- end
102
- end
103
-
104
- class ProfilesController < BaseController
105
- before_action :authenticate!
106
-
107
- def show
108
- render json: Store::PROFILE
109
- end
110
-
111
- def update
112
- attributes = params.require(:profile).permit(:name, :locale).to_h.symbolize_keys
113
-
114
- if attributes.key?(:locale) && !Store::LOCALES.include?(attributes[:locale])
115
- return render(json: { errors: { locale: ["is not included in the list"] } }, status: :unprocessable_entity)
116
- end
117
-
118
- render json: Store::PROFILE.merge(attributes)
119
- end
120
- end
121
-
122
- class ProductsController < BaseController
123
- def index
124
- products = Store::PRODUCTS
125
- category = params.dig(:filter, :category)
126
- products = products.select { |product| product[:category] == category } if category.present?
127
- products = products.sort_by { |product| product[:price_cents] } if params[:sort] == "price_asc"
128
- products = products.sort_by { |product| -product[:price_cents] } if params[:sort] == "price_desc"
129
-
130
- page = params.fetch(:page, 1).to_i
131
- per_page = params.fetch(:per_page, 2).to_i
132
- slice = products.each_slice(per_page).to_a[page - 1] || []
133
-
134
- render json: {
135
- data: slice.map { |product| product.slice(*Store::PUBLIC_PRODUCT) },
136
- meta: { page: page, per_page: per_page, total: products.size }
137
- }
138
- end
139
-
140
- def show
141
- product = Store::PRODUCTS.find { |candidate| candidate[:id] == params[:id].to_i }
142
-
143
- return not_found unless product
144
-
145
- render json: product.slice(*Store::PUBLIC_PRODUCT)
146
- end
147
-
148
- def search
149
- query = params[:q].to_s.strip
150
-
151
- return render(json: { error: "q is required" }, status: :bad_request) if query.empty?
152
-
153
- matches = Store::PRODUCTS.select { |product| product[:name].downcase.include?(query.downcase) }
154
-
155
- render json: { query: query, results: matches.map { |product| product.slice(*Store::PUBLIC_PRODUCT) } }
156
- end
157
- end
158
-
159
- class OrdersController < BaseController
160
- before_action :authenticate!
161
- before_action :find_order, only: %i[show cancel destroy]
162
-
163
- def index
164
- orders = Store::ORDERS.select { |order| order[:user_id] == 1 }
165
- orders = orders.select { |order| order[:status] == params[:status] } if params[:status].present?
166
-
167
- render json: orders
168
- end
169
-
170
- def show
171
- return render(json: { error: "Forbidden" }, status: :forbidden) unless @order[:user_id] == 1
172
-
173
- render json: @order
174
- end
175
-
176
- def create
177
- attributes = params.require(:order)
178
- .permit(line_items: %i[sku quantity], shipping_address: %i[line1 city country])
179
- .to_h.deep_symbolize_keys
180
- items = Array(attributes[:line_items])
181
-
182
- return render(json: { errors: { line_items: ["can't be blank"] } }, status: :unprocessable_entity) if items.empty?
183
-
184
- lines = items.map do |item|
185
- product = Store::PRODUCTS.find { |candidate| candidate[:sku] == item[:sku] }
186
-
187
- return render(json: { errors: { sku: ["#{item[:sku]} is not a known product"] } }, status: :unprocessable_entity) unless product
188
-
189
- quantity = item[:quantity].to_i
190
- {
191
- sku: product[:sku], name: product[:name], quantity: quantity,
192
- unit_price_cents: product[:price_cents], subtotal_cents: product[:price_cents] * quantity
193
- }
194
- end
195
-
196
- render json: {
197
- id: 4, status: "pending", currency: "USD",
198
- total_cents: lines.sum { |line| line[:subtotal_cents] },
199
- line_items: lines,
200
- shipping_address: attributes[:shipping_address]
201
- }, status: :created
202
- end
203
-
204
- def cancel
205
- if @order[:status] == "shipped"
206
- return render(json: { error: "Shipped orders cannot be cancelled" }, status: :conflict)
207
- end
208
-
209
- render json: @order.merge(status: "cancelled")
210
- end
211
-
212
- def destroy
213
- head :no_content
214
- end
215
-
216
- private
217
-
218
- def find_order
219
- @order = Store::ORDERS.find { |order| order[:id] == params[:id].to_i }
220
-
221
- not_found unless @order
222
- end
223
- end
224
-
225
- # Nested under an order: /api/v1/orders/:order_id/notes
226
- class NotesController < BaseController
227
- before_action :authenticate!
228
- before_action :find_order
229
-
230
- def index
231
- render json: Store::NOTES.select { |note| note[:order_id] == @order[:id] }
232
- end
233
-
234
- def create
235
- body = params.dig(:note, :body).to_s
236
-
237
- return render(json: { errors: { body: ["can't be blank"] } }, status: :unprocessable_entity) if body.empty?
238
-
239
- render json: { id: 3, order_id: @order[:id], body: body, author: "ada" }, status: :created
240
- end
241
-
242
- private
243
-
244
- def find_order
245
- @order = Store::ORDERS.find { |order| order[:id] == params[:order_id].to_i }
246
-
247
- not_found unless @order
248
- end
249
- end
250
-
251
- # `resource :cart`: one cart per customer, no id in the path.
252
- class CartsController < BaseController
253
- before_action :authenticate!
254
-
255
- def show
256
- render json: cart
257
- end
258
-
259
- # Turns the cart into an order.
260
- def checkout
261
- method = params[:payment_method].to_s
262
-
263
- unless Store::PAYMENT_METHODS.include?(method)
264
- return render(json: { errors: { payment_method: ["is not included in the list"] } }, status: :unprocessable_entity)
265
- end
266
-
267
- render json: { id: 5, status: "pending", payment_method: method, total_cents: cart[:total_cents], line_items: cart[:items] },
268
- status: :created
269
- end
270
-
271
- private
272
-
273
- def cart
274
- { items: Store::CART_ITEMS, total_cents: Store::CART_ITEMS.sum { |item| item[:quantity] * item[:unit_price_cents] } }
275
- end
276
- end
277
-
278
- # Nested under the cart and addressed by sku: /api/v1/cart/items/:sku
279
- class CartItemsController < BaseController
280
- before_action :authenticate!
281
-
282
- def create
283
- attributes = params.require(:item).permit(:sku, :quantity).to_h.symbolize_keys
284
- product = Store::PRODUCTS.find { |candidate| candidate[:sku] == attributes[:sku] }
285
-
286
- return render(json: { errors: { sku: ["#{attributes[:sku]} is not a known product"] } }, status: :unprocessable_entity) unless product
287
-
288
- render json: { sku: product[:sku], name: product[:name], quantity: attributes[:quantity].to_i, unit_price_cents: product[:price_cents] },
289
- status: :created
290
- end
291
-
292
- def destroy
293
- return not_found unless Store::CART_ITEMS.any? { |item| item[:sku] == params[:sku] }
294
-
295
- head :no_content
296
- end
297
- end
298
-
299
- module Admin
300
- # A different credential (X-Api-Key) and a fuller view of the same data.
301
- class ProductsController < BaseController
302
- before_action :require_api_key
303
-
304
- def index
305
- render json: Store::PRODUCTS
306
- end
307
-
308
- def create
309
- attributes = params.require(:product).permit(:sku, :name, :category, :price_cents).to_h.symbolize_keys
310
-
311
- if attributes[:sku].to_s.empty? || attributes[:name].to_s.empty?
312
- return render(json: { errors: { sku: ["can't be blank"] } }, status: :unprocessable_entity)
313
- end
314
-
315
- if Store::PRODUCTS.any? { |product| product[:sku] == attributes[:sku] }
316
- return render(json: { error: "sku #{attributes[:sku]} already exists" }, status: :conflict)
317
- end
318
-
319
- render json: { id: 5, cost_cents: 0, tags: [], **attributes }, status: :created
320
- end
321
-
322
- def destroy
323
- return not_found unless Store::PRODUCTS.any? { |product| product[:id] == params[:id].to_i }
324
-
325
- head :no_content
326
- end
327
-
328
- private
329
-
330
- def require_api_key
331
- return if request.headers["X-Api-Key"].present?
332
-
333
- render json: { error: "API key required" }, status: :unauthorized
334
- end
335
- end
336
- end
337
- end
338
-
339
- module V2
340
- class ProductsController < ActionController::API
341
- # v2 pages by cursor instead of page numbers.
342
- def index
343
- start = params[:cursor].present? ? 2 : 0
344
- items = Store::PRODUCTS[start, 2].map { |product| product.slice(*Store::PUBLIC_PRODUCT).merge(price: format("%.2f", product[:price_cents] / 100.0)) }
345
-
346
- render json: { items: items, next_cursor: start.zero? ? "eyJpZCI6Mn0" : nil }
347
- end
348
- end
349
- end
350
- end
351
-
352
- Rails.application.routes.draw do
353
- namespace :api do
354
- namespace :v1 do
355
- root to: "home#index"
356
-
357
- post "auth/login", to: "auth#login"
358
-
359
- resource :profile, only: %i[show update]
360
-
361
- resources :products, only: %i[index show] do
362
- get :search, on: :collection
363
- end
364
-
365
- resources :orders, only: %i[index show create destroy] do
366
- post :cancel, on: :member
367
- resources :notes, only: %i[index create]
368
- end
369
-
370
- resource :cart, only: :show do
371
- post :checkout
372
- resources :items, only: %i[create destroy], controller: "cart_items", param: :sku
373
- end
374
-
375
- namespace :admin do
376
- resources :products, only: %i[index create destroy]
377
- end
378
- end
379
-
380
- namespace :v2 do
381
- resources :products, only: :index
382
- end
383
- end
384
- end
@@ -1,104 +0,0 @@
1
- # API Documentation
2
-
3
- Generated by Reqcord from the application's integration tests.
4
-
5
- Base URL: `http://localhost:3000`
6
-
7
- ## Placeholders
8
-
9
- | Header | Replace with |
10
- | --- | --- |
11
- | Authorization | `Bearer {{token}}` |
12
- | X-Api-Key | `{{api_key}}` |
13
-
14
- ## Products
15
-
16
- Namespace: `api/v1/admin`
17
-
18
- | Method | Path | Description |
19
- | --- | --- | --- |
20
- | `GET` | [`/api/v1/admin/products`](api/v1/admin/products/list.md) | List Products |
21
- | `POST` | [`/api/v1/admin/products`](api/v1/admin/products/create.md) | Create Product |
22
- | `DELETE` | [`/api/v1/admin/products/:id`](api/v1/admin/products/destroy.md) | Delete Product |
23
-
24
- ## Auth
25
-
26
- Namespace: `api/v1`
27
-
28
- | Method | Path | Description |
29
- | --- | --- | --- |
30
- | `POST` | [`/api/v1/auth/login`](api/v1/auth/login.md) | Login |
31
-
32
- ## Cart Items
33
-
34
- Namespace: `api/v1`
35
-
36
- | Method | Path | Description |
37
- | --- | --- | --- |
38
- | `POST` | [`/api/v1/cart/items`](api/v1/cart-items/create.md) | Create Cart Item |
39
- | `DELETE` | [`/api/v1/cart/items/:sku`](api/v1/cart-items/destroy.md) | Delete Cart Item |
40
-
41
- ## Carts
42
-
43
- Namespace: `api/v1`
44
-
45
- | Method | Path | Description |
46
- | --- | --- | --- |
47
- | `GET` | [`/api/v1/cart`](api/v1/carts/show.md) | Get Cart |
48
- | `POST` | [`/api/v1/cart/checkout`](api/v1/carts/checkout.md) | Checkout Cart |
49
-
50
- ## Home
51
-
52
- Namespace: `api/v1`
53
-
54
- | Method | Path | Description |
55
- | --- | --- | --- |
56
- | `GET` | [`/api/v1`](api/v1/home/list.md) | Home |
57
-
58
- ## Notes
59
-
60
- Namespace: `api/v1`
61
-
62
- | Method | Path | Description |
63
- | --- | --- | --- |
64
- | `GET` | [`/api/v1/orders/:order_id/notes`](api/v1/notes/list.md) | List Notes |
65
- | `POST` | [`/api/v1/orders/:order_id/notes`](api/v1/notes/create.md) | Create Note |
66
-
67
- ## Orders
68
-
69
- Namespace: `api/v1`
70
-
71
- | Method | Path | Description |
72
- | --- | --- | --- |
73
- | `GET` | [`/api/v1/orders`](api/v1/orders/list.md) | List Orders |
74
- | `POST` | [`/api/v1/orders`](api/v1/orders/create.md) | Create Order |
75
- | `DELETE` | [`/api/v1/orders/:id`](api/v1/orders/destroy.md) | Delete Order |
76
- | `GET` | [`/api/v1/orders/:id`](api/v1/orders/show.md) | Get Order |
77
- | `POST` | [`/api/v1/orders/:id/cancel`](api/v1/orders/cancel.md) | Cancel Order |
78
-
79
- ## Products
80
-
81
- Namespace: `api/v1`
82
-
83
- | Method | Path | Description |
84
- | --- | --- | --- |
85
- | `GET` | [`/api/v1/products`](api/v1/products/list.md) | List Products |
86
- | `GET` | [`/api/v1/products/:id`](api/v1/products/show.md) | Get Product |
87
- | `GET` | [`/api/v1/products/search`](api/v1/products/search.md) | Search Products |
88
-
89
- ## Profiles
90
-
91
- Namespace: `api/v1`
92
-
93
- | Method | Path | Description |
94
- | --- | --- | --- |
95
- | `GET` | [`/api/v1/profile`](api/v1/profiles/show.md) | Get Profile |
96
- | `PATCH` | [`/api/v1/profile`](api/v1/profiles/update.md) | Update Profile |
97
-
98
- ## Products
99
-
100
- Namespace: `api/v2`
101
-
102
- | Method | Path | Description |
103
- | --- | --- | --- |
104
- | `GET` | [`/api/v2/products`](api/v2/products/list.md) | List Products |
@@ -1,117 +0,0 @@
1
- # Create Product
2
-
3
- `POST /api/v1/admin/products`
4
-
5
- Namespace: `api/v1/admin`
6
-
7
- ## Headers
8
-
9
- | Header | Value |
10
- | --- | --- |
11
- | X-Api-Key | `{{api_key}}` |
12
- | Content-Type | `application/json` |
13
- | Accept | `application/json` |
14
-
15
- ## Body Parameters
16
-
17
- | Field | Type | Required | Values |
18
- | --- | --- | --- | --- |
19
- | `product.sku` | string | yes | `"TEA-003"` |
20
- | `product.name` | string | yes | `"Jasmine"` |
21
- | `product.category` | string | yes | `"tea"` |
22
- | `product.price_cents` | integer | yes | `1400` |
23
-
24
- ## Example Request
25
-
26
- ```json
27
- {
28
- "product": {
29
- "sku": "TEA-003",
30
- "name": "Jasmine",
31
- "category": "tea",
32
- "price_cents": 1400
33
- }
34
- }
35
- ```
36
-
37
- ## cURL
38
-
39
- ```bash
40
- curl --request POST \
41
- --url "http://localhost:3000/api/v1/admin/products" \
42
- --header "X-Api-Key: {{api_key}}" \
43
- --header "Content-Type: application/json" \
44
- --header "Accept: application/json" \
45
- --data '{
46
- "product": {
47
- "sku": "TEA-003",
48
- "name": "Jasmine",
49
- "category": "tea",
50
- "price_cents": 1400
51
- }
52
- }'
53
- ```
54
-
55
- ## Responses
56
-
57
- ### 201 Created
58
-
59
- #### Fields
60
-
61
- | Field | Type | Required | Values |
62
- | --- | --- | --- | --- |
63
- | `id` | integer | yes | `5` |
64
- | `cost_cents` | integer | yes | `0` |
65
- | `tags` | array | yes | - |
66
- | `sku` | string | yes | `"TEA-003"` |
67
- | `name` | string | yes | `"Jasmine"` |
68
- | `category` | string | yes | `"tea"` |
69
- | `price_cents` | integer | yes | `1400` |
70
-
71
- ```json
72
- {
73
- "id": 5,
74
- "cost_cents": 0,
75
- "tags": [],
76
- "sku": "TEA-003",
77
- "name": "Jasmine",
78
- "category": "tea",
79
- "price_cents": 1400
80
- }
81
- ```
82
-
83
- ### 409 Conflict
84
-
85
- #### Fields
86
-
87
- | Field | Type | Required | Values |
88
- | --- | --- | --- | --- |
89
- | `error` | string | yes | `"sku TEA-001 already exists"` |
90
-
91
- ```json
92
- {
93
- "error": "sku TEA-001 already exists"
94
- }
95
- ```
96
-
97
- ### 422 Unprocessable Content
98
-
99
- #### Fields
100
-
101
- | Field | Type | Required | Values |
102
- | --- | --- | --- | --- |
103
- | `errors.sku[]` | string | yes | `"can't be blank"` |
104
-
105
- ```json
106
- {
107
- "errors": {
108
- "sku": [
109
- "can't be blank"
110
- ]
111
- }
112
- }
113
- ```
114
-
115
- ---
116
-
117
- Resource: [Products](index.md)
@@ -1,51 +0,0 @@
1
- # Delete Product
2
-
3
- `DELETE /api/v1/admin/products/:id`
4
-
5
- Namespace: `api/v1/admin`
6
-
7
- ## Headers
8
-
9
- | Header | Value |
10
- | --- | --- |
11
- | X-Api-Key | `{{api_key}}` |
12
- | Accept | `application/json` |
13
-
14
- ## Path Parameters
15
-
16
- | Field | Type | Required | Values |
17
- | --- | --- | --- | --- |
18
- | `id` | string | yes | `"4"` |
19
-
20
- ## cURL
21
-
22
- ```bash
23
- curl --request DELETE \
24
- --url "http://localhost:3000/api/v1/admin/products/4" \
25
- --header "X-Api-Key: {{api_key}}" \
26
- --header "Accept: application/json"
27
- ```
28
-
29
- ## Responses
30
-
31
- ### 204 No Content
32
-
33
- _No response body._
34
-
35
- ### 404 Not Found
36
-
37
- #### Fields
38
-
39
- | Field | Type | Required | Values |
40
- | --- | --- | --- | --- |
41
- | `error` | string | yes | `"Not Found"` |
42
-
43
- ```json
44
- {
45
- "error": "Not Found"
46
- }
47
- ```
48
-
49
- ---
50
-
51
- Resource: [Products](index.md)
@@ -1,11 +0,0 @@
1
- # Products
2
-
3
- Namespace: `api/v1/admin`
4
-
5
- API version: `v1`
6
-
7
- | Method | Path | Description |
8
- | --- | --- | --- |
9
- | `GET` | [`/api/v1/admin/products`](list.md) | List Products |
10
- | `POST` | [`/api/v1/admin/products`](create.md) | Create Product |
11
- | `DELETE` | [`/api/v1/admin/products/:id`](destroy.md) | Delete Product |