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,363 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The store API of complex-test-app, byte for byte, documented from RSpec
4
- # request specs instead of Minitest. See ../complex-test-app/app.rb for the
5
- # tour of what each endpoint shows.
6
- ENV["RAILS_ENV"] ||= "test"
7
-
8
- # Only needed because this example lives inside the Reqcord repository.
9
- # In your own application, `gem "reqcord"` in the Gemfile is enough.
10
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
11
-
12
- require "rails"
13
- require "action_controller/railtie"
14
- require "reqcord"
15
-
16
- class ComplexApp < Rails::Application
17
- config.root = __dir__
18
- config.eager_load = false
19
- config.logger = Logger.new(IO::NULL)
20
- config.secret_key_base = "a" * 64
21
- config.hosts.clear
22
- end
23
-
24
- Rails.application.initialize!
25
-
26
- module Store
27
- PRODUCTS = [
28
- { id: 1, sku: "TEA-001", name: "Earl Grey", category: "tea", price_cents: 1200, cost_cents: 500, tags: %w[black bergamot] },
29
- { id: 2, sku: "TEA-002", name: "Sencha", category: "tea", price_cents: 1500, cost_cents: 700, tags: %w[green] },
30
- { id: 3, sku: "MUG-001", name: "Stoneware Mug", category: "mugs", price_cents: 2400, cost_cents: 900, tags: %w[ceramic] },
31
- { id: 4, sku: "MUG-002", name: "Travel Mug", category: "mugs", price_cents: 3200, cost_cents: 1400, tags: %w[steel insulated] }
32
- ].freeze
33
-
34
- PUBLIC_PRODUCT = %i[id sku name category price_cents tags].freeze
35
-
36
- ORDERS = [
37
- { id: 1, user_id: 1, status: "pending", currency: "USD", total_cents: 2700 },
38
- { id: 2, user_id: 1, status: "shipped", currency: "USD", total_cents: 3200 },
39
- { id: 3, user_id: 2, status: "pending", currency: "EUR", total_cents: 1500 }
40
- ].freeze
41
-
42
- NOTES = [
43
- { id: 1, order_id: 1, body: "Leave at the door", author: "ada" },
44
- { id: 2, order_id: 1, body: "Gift wrap please", author: "ada" }
45
- ].freeze
46
-
47
- PROFILE = { id: 1, name: "Ada Lovelace", email: "ada@example.com", locale: "en" }.freeze
48
- LOCALES = %w[en tr].freeze
49
-
50
- CART_ITEMS = [
51
- { sku: "TEA-001", name: "Earl Grey", quantity: 2, unit_price_cents: 1200 },
52
- { sku: "MUG-001", name: "Stoneware Mug", quantity: 1, unit_price_cents: 2400 }
53
- ].freeze
54
- PAYMENT_METHODS = %w[card bank_transfer].freeze
55
- end
56
-
57
- module Api
58
- module V1
59
- class BaseController < ActionController::API
60
- private
61
-
62
- def authenticate!
63
- return if request.headers["Authorization"].present?
64
-
65
- render json: { error: "Unauthorized" }, status: :unauthorized
66
- end
67
-
68
- def not_found
69
- render json: { error: "Not Found" }, status: :not_found
70
- end
71
- end
72
-
73
- class HomeController < BaseController
74
- def index
75
- render json: { name: "Store API", version: "v1", links: { products: "/api/v1/products", orders: "/api/v1/orders" } }
76
- end
77
- end
78
-
79
- class AuthController < BaseController
80
- def login
81
- if params[:email] == "ada@example.com" && params[:password] == "correct-horse-battery"
82
- render json: { token: "tok_live_9f8e7d6c", token_type: "Bearer", expires_in: 3600 }
83
- else
84
- render json: { error: "Invalid email or password" }, status: :unauthorized
85
- end
86
- end
87
- end
88
-
89
- class ProfilesController < BaseController
90
- before_action :authenticate!
91
-
92
- def show
93
- render json: Store::PROFILE
94
- end
95
-
96
- def update
97
- attributes = params.require(:profile).permit(:name, :locale).to_h.symbolize_keys
98
-
99
- if attributes.key?(:locale) && !Store::LOCALES.include?(attributes[:locale])
100
- return render(json: { errors: { locale: ["is not included in the list"] } }, status: :unprocessable_entity)
101
- end
102
-
103
- render json: Store::PROFILE.merge(attributes)
104
- end
105
- end
106
-
107
- class ProductsController < BaseController
108
- def index
109
- products = Store::PRODUCTS
110
- category = params.dig(:filter, :category)
111
- products = products.select { |product| product[:category] == category } if category.present?
112
- products = products.sort_by { |product| product[:price_cents] } if params[:sort] == "price_asc"
113
- products = products.sort_by { |product| -product[:price_cents] } if params[:sort] == "price_desc"
114
-
115
- page = params.fetch(:page, 1).to_i
116
- per_page = params.fetch(:per_page, 2).to_i
117
- slice = products.each_slice(per_page).to_a[page - 1] || []
118
-
119
- render json: {
120
- data: slice.map { |product| product.slice(*Store::PUBLIC_PRODUCT) },
121
- meta: { page: page, per_page: per_page, total: products.size }
122
- }
123
- end
124
-
125
- def show
126
- product = Store::PRODUCTS.find { |candidate| candidate[:id] == params[:id].to_i }
127
-
128
- return not_found unless product
129
-
130
- render json: product.slice(*Store::PUBLIC_PRODUCT)
131
- end
132
-
133
- def search
134
- query = params[:q].to_s.strip
135
-
136
- return render(json: { error: "q is required" }, status: :bad_request) if query.empty?
137
-
138
- matches = Store::PRODUCTS.select { |product| product[:name].downcase.include?(query.downcase) }
139
-
140
- render json: { query: query, results: matches.map { |product| product.slice(*Store::PUBLIC_PRODUCT) } }
141
- end
142
- end
143
-
144
- class OrdersController < BaseController
145
- before_action :authenticate!
146
- before_action :find_order, only: %i[show cancel destroy]
147
-
148
- def index
149
- orders = Store::ORDERS.select { |order| order[:user_id] == 1 }
150
- orders = orders.select { |order| order[:status] == params[:status] } if params[:status].present?
151
-
152
- render json: orders
153
- end
154
-
155
- def show
156
- return render(json: { error: "Forbidden" }, status: :forbidden) unless @order[:user_id] == 1
157
-
158
- render json: @order
159
- end
160
-
161
- def create
162
- attributes = params.require(:order)
163
- .permit(line_items: %i[sku quantity], shipping_address: %i[line1 city country])
164
- .to_h.deep_symbolize_keys
165
- items = Array(attributes[:line_items])
166
-
167
- return render(json: { errors: { line_items: ["can't be blank"] } }, status: :unprocessable_entity) if items.empty?
168
-
169
- lines = items.map do |item|
170
- product = Store::PRODUCTS.find { |candidate| candidate[:sku] == item[:sku] }
171
-
172
- return render(json: { errors: { sku: ["#{item[:sku]} is not a known product"] } }, status: :unprocessable_entity) unless product
173
-
174
- quantity = item[:quantity].to_i
175
- {
176
- sku: product[:sku], name: product[:name], quantity: quantity,
177
- unit_price_cents: product[:price_cents], subtotal_cents: product[:price_cents] * quantity
178
- }
179
- end
180
-
181
- render json: {
182
- id: 4, status: "pending", currency: "USD",
183
- total_cents: lines.sum { |line| line[:subtotal_cents] },
184
- line_items: lines,
185
- shipping_address: attributes[:shipping_address]
186
- }, status: :created
187
- end
188
-
189
- def cancel
190
- if @order[:status] == "shipped"
191
- return render(json: { error: "Shipped orders cannot be cancelled" }, status: :conflict)
192
- end
193
-
194
- render json: @order.merge(status: "cancelled")
195
- end
196
-
197
- def destroy
198
- head :no_content
199
- end
200
-
201
- private
202
-
203
- def find_order
204
- @order = Store::ORDERS.find { |order| order[:id] == params[:id].to_i }
205
-
206
- not_found unless @order
207
- end
208
- end
209
-
210
- class NotesController < BaseController
211
- before_action :authenticate!
212
- before_action :find_order
213
-
214
- def index
215
- render json: Store::NOTES.select { |note| note[:order_id] == @order[:id] }
216
- end
217
-
218
- def create
219
- body = params.dig(:note, :body).to_s
220
-
221
- return render(json: { errors: { body: ["can't be blank"] } }, status: :unprocessable_entity) if body.empty?
222
-
223
- render json: { id: 3, order_id: @order[:id], body: body, author: "ada" }, status: :created
224
- end
225
-
226
- private
227
-
228
- def find_order
229
- @order = Store::ORDERS.find { |order| order[:id] == params[:order_id].to_i }
230
-
231
- not_found unless @order
232
- end
233
- end
234
-
235
- class CartsController < BaseController
236
- before_action :authenticate!
237
-
238
- def show
239
- render json: cart
240
- end
241
-
242
- def checkout
243
- method = params[:payment_method].to_s
244
-
245
- unless Store::PAYMENT_METHODS.include?(method)
246
- return render(json: { errors: { payment_method: ["is not included in the list"] } }, status: :unprocessable_entity)
247
- end
248
-
249
- render json: { id: 5, status: "pending", payment_method: method, total_cents: cart[:total_cents], line_items: cart[:items] },
250
- status: :created
251
- end
252
-
253
- private
254
-
255
- def cart
256
- { items: Store::CART_ITEMS, total_cents: Store::CART_ITEMS.sum { |item| item[:quantity] * item[:unit_price_cents] } }
257
- end
258
- end
259
-
260
- class CartItemsController < BaseController
261
- before_action :authenticate!
262
-
263
- def create
264
- attributes = params.require(:item).permit(:sku, :quantity).to_h.symbolize_keys
265
- product = Store::PRODUCTS.find { |candidate| candidate[:sku] == attributes[:sku] }
266
-
267
- return render(json: { errors: { sku: ["#{attributes[:sku]} is not a known product"] } }, status: :unprocessable_entity) unless product
268
-
269
- render json: { sku: product[:sku], name: product[:name], quantity: attributes[:quantity].to_i, unit_price_cents: product[:price_cents] },
270
- status: :created
271
- end
272
-
273
- def destroy
274
- return not_found unless Store::CART_ITEMS.any? { |item| item[:sku] == params[:sku] }
275
-
276
- head :no_content
277
- end
278
- end
279
-
280
- module Admin
281
- class ProductsController < BaseController
282
- before_action :require_api_key
283
-
284
- def index
285
- render json: Store::PRODUCTS
286
- end
287
-
288
- def create
289
- attributes = params.require(:product).permit(:sku, :name, :category, :price_cents).to_h.symbolize_keys
290
-
291
- if attributes[:sku].to_s.empty? || attributes[:name].to_s.empty?
292
- return render(json: { errors: { sku: ["can't be blank"] } }, status: :unprocessable_entity)
293
- end
294
-
295
- if Store::PRODUCTS.any? { |product| product[:sku] == attributes[:sku] }
296
- return render(json: { error: "sku #{attributes[:sku]} already exists" }, status: :conflict)
297
- end
298
-
299
- render json: { id: 5, cost_cents: 0, tags: [], **attributes }, status: :created
300
- end
301
-
302
- def destroy
303
- return not_found unless Store::PRODUCTS.any? { |product| product[:id] == params[:id].to_i }
304
-
305
- head :no_content
306
- end
307
-
308
- private
309
-
310
- def require_api_key
311
- return if request.headers["X-Api-Key"].present?
312
-
313
- render json: { error: "API key required" }, status: :unauthorized
314
- end
315
- end
316
- end
317
- end
318
-
319
- module V2
320
- class ProductsController < ActionController::API
321
- def index
322
- start = params[:cursor].present? ? 2 : 0
323
- items = Store::PRODUCTS[start, 2].map { |product| product.slice(*Store::PUBLIC_PRODUCT).merge(price: format("%.2f", product[:price_cents] / 100.0)) }
324
-
325
- render json: { items: items, next_cursor: start.zero? ? "eyJpZCI6Mn0" : nil }
326
- end
327
- end
328
- end
329
- end
330
-
331
- Rails.application.routes.draw do
332
- namespace :api do
333
- namespace :v1 do
334
- root to: "home#index"
335
-
336
- post "auth/login", to: "auth#login"
337
-
338
- resource :profile, only: %i[show update]
339
-
340
- resources :products, only: %i[index show] do
341
- get :search, on: :collection
342
- end
343
-
344
- resources :orders, only: %i[index show create destroy] do
345
- post :cancel, on: :member
346
- resources :notes, only: %i[index create]
347
- end
348
-
349
- resource :cart, only: :show do
350
- post :checkout
351
- resources :items, only: %i[create destroy], controller: "cart_items", param: :sku
352
- end
353
-
354
- namespace :admin do
355
- resources :products, only: %i[index create destroy]
356
- end
357
- end
358
-
359
- namespace :v2 do
360
- resources :products, only: :index
361
- end
362
- end
363
- 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 |