apipie-rails-jq 1.4.3.pre.beta.pre.jq.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build.yml +32 -0
  3. data/.github/workflows/rubocop-challenger.yml +26 -0
  4. data/.github/workflows/rubocop.yml +18 -0
  5. data/.gitignore +16 -0
  6. data/.rspec +2 -0
  7. data/.rubocop.yml +132 -0
  8. data/.rubocop_todo.yml +1967 -0
  9. data/.vscode/settings.json +3 -0
  10. data/APACHE-LICENSE-2.0 +202 -0
  11. data/CHANGELOG.md +693 -0
  12. data/Gemfile +19 -0
  13. data/MIT-LICENSE +20 -0
  14. data/NOTICE +4 -0
  15. data/PROPOSAL_FOR_RESPONSE_DESCRIPTIONS.md +244 -0
  16. data/README.md +2088 -0
  17. data/Rakefile +8 -0
  18. data/apipie-rails.gemspec +44 -0
  19. data/app/controllers/apipie/apipies_controller.rb +184 -0
  20. data/app/helpers/apipie_helper.rb +10 -0
  21. data/app/public/apipie/javascripts/apipie.js +6 -0
  22. data/app/public/apipie/javascripts/bundled/bootstrap-collapse.js +167 -0
  23. data/app/public/apipie/javascripts/bundled/bootstrap.js +2280 -0
  24. data/app/public/apipie/javascripts/bundled/jquery.js +2 -0
  25. data/app/public/apipie/javascripts/bundled/prettify.js +28 -0
  26. data/app/public/apipie/stylesheets/application.css +7 -0
  27. data/app/public/apipie/stylesheets/bundled/bootstrap-responsive.min.css +9 -0
  28. data/app/public/apipie/stylesheets/bundled/bootstrap.min.css +9 -0
  29. data/app/public/apipie/stylesheets/bundled/prettify.css +30 -0
  30. data/app/views/apipie/apipies/_deprecation.html.erb +16 -0
  31. data/app/views/apipie/apipies/_disqus.html.erb +13 -0
  32. data/app/views/apipie/apipies/_errors.html.erb +23 -0
  33. data/app/views/apipie/apipies/_headers.html.erb +26 -0
  34. data/app/views/apipie/apipies/_languages.erb +6 -0
  35. data/app/views/apipie/apipies/_metadata.erb +1 -0
  36. data/app/views/apipie/apipies/_method_detail.erb +63 -0
  37. data/app/views/apipie/apipies/_params.html.erb +49 -0
  38. data/app/views/apipie/apipies/_params_plain.html.erb +21 -0
  39. data/app/views/apipie/apipies/apipie_404.html.erb +17 -0
  40. data/app/views/apipie/apipies/apipie_checksum.json.erb +1 -0
  41. data/app/views/apipie/apipies/getting_started.html.erb +6 -0
  42. data/app/views/apipie/apipies/index.html.erb +56 -0
  43. data/app/views/apipie/apipies/method.html.erb +41 -0
  44. data/app/views/apipie/apipies/plain.html.erb +77 -0
  45. data/app/views/apipie/apipies/resource.html.erb +80 -0
  46. data/app/views/apipie/apipies/static.html.erb +103 -0
  47. data/app/views/layouts/apipie/apipie.html.erb +27 -0
  48. data/config/locales/de.yml +28 -0
  49. data/config/locales/en.yml +41 -0
  50. data/config/locales/es.yml +28 -0
  51. data/config/locales/fr.yml +31 -0
  52. data/config/locales/it.yml +41 -0
  53. data/config/locales/ja.yml +31 -0
  54. data/config/locales/ko.yml +32 -0
  55. data/config/locales/pl.yml +28 -0
  56. data/config/locales/pt-BR.yml +28 -0
  57. data/config/locales/ru.yml +28 -0
  58. data/config/locales/tr.yml +28 -0
  59. data/config/locales/zh-CN.yml +28 -0
  60. data/config/locales/zh-TW.yml +28 -0
  61. data/gemfiles/Gemfile.tools +9 -0
  62. data/images/screenshot-1.png +0 -0
  63. data/images/screenshot-2.png +0 -0
  64. data/lib/apipie/apipie_module.rb +83 -0
  65. data/lib/apipie/application.rb +499 -0
  66. data/lib/apipie/configuration.rb +196 -0
  67. data/lib/apipie/core_ext/route.rb +9 -0
  68. data/lib/apipie/dsl_definition.rb +630 -0
  69. data/lib/apipie/error_description.rb +46 -0
  70. data/lib/apipie/errors.rb +86 -0
  71. data/lib/apipie/extractor/collector.rb +116 -0
  72. data/lib/apipie/extractor/recorder.rb +193 -0
  73. data/lib/apipie/extractor/writer.rb +454 -0
  74. data/lib/apipie/extractor.rb +181 -0
  75. data/lib/apipie/generator/config.rb +12 -0
  76. data/lib/apipie/generator/generator.rb +2 -0
  77. data/lib/apipie/generator/swagger/computed_interface_id.rb +23 -0
  78. data/lib/apipie/generator/swagger/config.rb +80 -0
  79. data/lib/apipie/generator/swagger/context.rb +38 -0
  80. data/lib/apipie/generator/swagger/method_description/api_decorator.rb +20 -0
  81. data/lib/apipie/generator/swagger/method_description/api_schema_service.rb +89 -0
  82. data/lib/apipie/generator/swagger/method_description/decorator.rb +22 -0
  83. data/lib/apipie/generator/swagger/method_description/parameters_service.rb +139 -0
  84. data/lib/apipie/generator/swagger/method_description/response_schema_service.rb +46 -0
  85. data/lib/apipie/generator/swagger/method_description/response_service.rb +71 -0
  86. data/lib/apipie/generator/swagger/method_description.rb +2 -0
  87. data/lib/apipie/generator/swagger/operation_id.rb +51 -0
  88. data/lib/apipie/generator/swagger/param_description/builder.rb +114 -0
  89. data/lib/apipie/generator/swagger/param_description/composite.rb +119 -0
  90. data/lib/apipie/generator/swagger/param_description/description.rb +15 -0
  91. data/lib/apipie/generator/swagger/param_description/in.rb +37 -0
  92. data/lib/apipie/generator/swagger/param_description/name.rb +18 -0
  93. data/lib/apipie/generator/swagger/param_description/path_params_composite.rb +61 -0
  94. data/lib/apipie/generator/swagger/param_description/referenced_composite.rb +36 -0
  95. data/lib/apipie/generator/swagger/param_description/type.rb +132 -0
  96. data/lib/apipie/generator/swagger/param_description.rb +18 -0
  97. data/lib/apipie/generator/swagger/path_decorator.rb +36 -0
  98. data/lib/apipie/generator/swagger/referenced_definitions.rb +17 -0
  99. data/lib/apipie/generator/swagger/resource_description_collection.rb +30 -0
  100. data/lib/apipie/generator/swagger/resource_description_composite.rb +56 -0
  101. data/lib/apipie/generator/swagger/schema.rb +63 -0
  102. data/lib/apipie/generator/swagger/swagger.rb +2 -0
  103. data/lib/apipie/generator/swagger/type.rb +16 -0
  104. data/lib/apipie/generator/swagger/type_extractor.rb +51 -0
  105. data/lib/apipie/generator/swagger/warning.rb +74 -0
  106. data/lib/apipie/generator/swagger/warning_writer.rb +54 -0
  107. data/lib/apipie/helpers.rb +73 -0
  108. data/lib/apipie/markup.rb +52 -0
  109. data/lib/apipie/method_description/api.rb +12 -0
  110. data/lib/apipie/method_description/apis_service.rb +82 -0
  111. data/lib/apipie/method_description.rb +230 -0
  112. data/lib/apipie/middleware/checksum_in_headers.rb +35 -0
  113. data/lib/apipie/param_description/deprecation.rb +24 -0
  114. data/lib/apipie/param_description.rb +313 -0
  115. data/lib/apipie/railtie.rb +9 -0
  116. data/lib/apipie/resource_description.rb +152 -0
  117. data/lib/apipie/response_description.rb +157 -0
  118. data/lib/apipie/response_description_adapter.rb +202 -0
  119. data/lib/apipie/routes_formatter.rb +33 -0
  120. data/lib/apipie/routing.rb +16 -0
  121. data/lib/apipie/rspec/response_validation_helper.rb +194 -0
  122. data/lib/apipie/see_description.rb +39 -0
  123. data/lib/apipie/static_dispatcher.rb +75 -0
  124. data/lib/apipie/swagger_generator.rb +45 -0
  125. data/lib/apipie/tag_list_description.rb +11 -0
  126. data/lib/apipie/validator.rb +552 -0
  127. data/lib/apipie/version.rb +3 -0
  128. data/lib/apipie-rails.rb +60 -0
  129. data/lib/generators/apipie/install/README +6 -0
  130. data/lib/generators/apipie/install/install_generator.rb +25 -0
  131. data/lib/generators/apipie/install/templates/initializer.rb.erb +7 -0
  132. data/lib/generators/apipie/views_generator.rb +11 -0
  133. data/lib/tasks/apipie.rake +355 -0
  134. data/rel-eng/gem_release.ipynb +398 -0
  135. data/rel-eng/packages/.readme +3 -0
  136. data/rel-eng/packages/rubygem-apipie-rails +1 -0
  137. data/rel-eng/tito.props +5 -0
  138. data/spec/controllers/api/v1/architectures_controller_spec.rb +29 -0
  139. data/spec/controllers/api/v2/architectures_controller_spec.rb +19 -0
  140. data/spec/controllers/api/v2/empty_middle_controller_spec.rb +23 -0
  141. data/spec/controllers/api/v2/nested/resources_controller_spec.rb +27 -0
  142. data/spec/controllers/api/v2/sub/footguns_controller_spec.rb +19 -0
  143. data/spec/controllers/concerns_controller_spec.rb +42 -0
  144. data/spec/controllers/extended_controller_spec.rb +14 -0
  145. data/spec/controllers/included_param_group_controller_spec.rb +13 -0
  146. data/spec/controllers/pets_controller_spec.rb +98 -0
  147. data/spec/controllers/users_controller_spec.rb +794 -0
  148. data/spec/dummy/Rakefile +7 -0
  149. data/spec/dummy/app/controllers/api/base_controller.rb +4 -0
  150. data/spec/dummy/app/controllers/api/v1/architectures_controller.rb +43 -0
  151. data/spec/dummy/app/controllers/api/v1/base_controller.rb +11 -0
  152. data/spec/dummy/app/controllers/api/v2/architectures_controller.rb +31 -0
  153. data/spec/dummy/app/controllers/api/v2/base_controller.rb +17 -0
  154. data/spec/dummy/app/controllers/api/v2/empty_middle_controller.rb +14 -0
  155. data/spec/dummy/app/controllers/api/v2/nested/architectures_controller.rb +32 -0
  156. data/spec/dummy/app/controllers/api/v2/nested/resources_controller.rb +33 -0
  157. data/spec/dummy/app/controllers/api/v2/sub/footguns_controller.rb +30 -0
  158. data/spec/dummy/app/controllers/application_controller.rb +18 -0
  159. data/spec/dummy/app/controllers/concerns_controller.rb +8 -0
  160. data/spec/dummy/app/controllers/extended_controller.rb +14 -0
  161. data/spec/dummy/app/controllers/extending_concern.rb +10 -0
  162. data/spec/dummy/app/controllers/files_controller.rb +5 -0
  163. data/spec/dummy/app/controllers/included_param_group_controller.rb +19 -0
  164. data/spec/dummy/app/controllers/overridden_concerns_controller.rb +31 -0
  165. data/spec/dummy/app/controllers/pets_controller.rb +408 -0
  166. data/spec/dummy/app/controllers/pets_using_auto_views_controller.rb +73 -0
  167. data/spec/dummy/app/controllers/pets_using_self_describing_classes_controller.rb +95 -0
  168. data/spec/dummy/app/controllers/sample_controller.rb +39 -0
  169. data/spec/dummy/app/controllers/tagged_cats_controller.rb +32 -0
  170. data/spec/dummy/app/controllers/tagged_dogs_controller.rb +15 -0
  171. data/spec/dummy/app/controllers/twitter_example_controller.rb +307 -0
  172. data/spec/dummy/app/controllers/users_controller.rb +310 -0
  173. data/spec/dummy/app/helpers/random_param_group.rb +8 -0
  174. data/spec/dummy/app/views/layouts/application.html.erb +21 -0
  175. data/spec/dummy/components/test_engine/Gemfile +6 -0
  176. data/spec/dummy/components/test_engine/app/controllers/test_engine/application_controller.rb +4 -0
  177. data/spec/dummy/components/test_engine/app/controllers/test_engine/memes_controller.rb +37 -0
  178. data/spec/dummy/components/test_engine/config/routes.rb +3 -0
  179. data/spec/dummy/components/test_engine/db/.gitkeep +0 -0
  180. data/spec/dummy/components/test_engine/lib/test_engine.rb +7 -0
  181. data/spec/dummy/components/test_engine/test_engine.gemspec +11 -0
  182. data/spec/dummy/config/application.rb +47 -0
  183. data/spec/dummy/config/boot.rb +12 -0
  184. data/spec/dummy/config/database.yml +21 -0
  185. data/spec/dummy/config/environment.rb +8 -0
  186. data/spec/dummy/config/environments/development.rb +25 -0
  187. data/spec/dummy/config/environments/production.rb +49 -0
  188. data/spec/dummy/config/environments/test.rb +33 -0
  189. data/spec/dummy/config/initializers/apipie.rb +110 -0
  190. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  191. data/spec/dummy/config/initializers/inflections.rb +10 -0
  192. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  193. data/spec/dummy/config/initializers/secret_token.rb +8 -0
  194. data/spec/dummy/config/initializers/session_store.rb +8 -0
  195. data/spec/dummy/config/locales/en.yml +5 -0
  196. data/spec/dummy/config/routes.rb +61 -0
  197. data/spec/dummy/config.ru +4 -0
  198. data/spec/dummy/db/.gitkeep +0 -0
  199. data/spec/dummy/doc/apipie_examples.json +1 -0
  200. data/spec/dummy/doc/users/desc_from_file.md +1 -0
  201. data/spec/dummy/public/404.html +26 -0
  202. data/spec/dummy/public/422.html +26 -0
  203. data/spec/dummy/public/500.html +26 -0
  204. data/spec/dummy/public/favicon.ico +0 -0
  205. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  206. data/spec/dummy/script/rails +6 -0
  207. data/spec/lib/apipie/apipies_controller_spec.rb +345 -0
  208. data/spec/lib/apipie/application_spec.rb +62 -0
  209. data/spec/lib/apipie/configuration_spec.rb +38 -0
  210. data/spec/lib/apipie/extractor/collector_spec.rb +57 -0
  211. data/spec/lib/apipie/extractor/recorder/middleware_spec.rb +44 -0
  212. data/spec/lib/apipie/extractor/recorder_spec.rb +77 -0
  213. data/spec/lib/apipie/extractor/writer_spec.rb +112 -0
  214. data/spec/lib/apipie/extractor_spec.rb +9 -0
  215. data/spec/lib/apipie/file_handler_spec.rb +25 -0
  216. data/spec/lib/apipie/generator/swagger/config_spec.rb +19 -0
  217. data/spec/lib/apipie/generator/swagger/context_spec.rb +56 -0
  218. data/spec/lib/apipie/generator/swagger/method_description/api_schema_service_spec.rb +119 -0
  219. data/spec/lib/apipie/generator/swagger/method_description/response_schema_service_spec.rb +105 -0
  220. data/spec/lib/apipie/generator/swagger/method_description/response_service_spec.rb +62 -0
  221. data/spec/lib/apipie/generator/swagger/operation_id_spec.rb +63 -0
  222. data/spec/lib/apipie/generator/swagger/param_description/builder_spec.rb +245 -0
  223. data/spec/lib/apipie/generator/swagger/param_description/composite_spec.rb +95 -0
  224. data/spec/lib/apipie/generator/swagger/param_description/description_spec.rb +79 -0
  225. data/spec/lib/apipie/generator/swagger/param_description/in_spec.rb +86 -0
  226. data/spec/lib/apipie/generator/swagger/param_description/name_spec.rb +81 -0
  227. data/spec/lib/apipie/generator/swagger/param_description/type_spec.rb +210 -0
  228. data/spec/lib/apipie/generator/swagger/param_description_spec.rb +28 -0
  229. data/spec/lib/apipie/generator/swagger/path_decorator_spec.rb +57 -0
  230. data/spec/lib/apipie/generator/swagger/referenced_definitions_spec.rb +35 -0
  231. data/spec/lib/apipie/generator/swagger/resource_description_composite_spec.rb +37 -0
  232. data/spec/lib/apipie/generator/swagger/resource_descriptions_collection_spec.rb +57 -0
  233. data/spec/lib/apipie/generator/swagger/schema_spec.rb +89 -0
  234. data/spec/lib/apipie/generator/swagger/type_extractor_spec.rb +38 -0
  235. data/spec/lib/apipie/generator/swagger/warning_spec.rb +51 -0
  236. data/spec/lib/apipie/generator/swagger/warning_writer_spec.rb +71 -0
  237. data/spec/lib/apipie/method_description/apis_service_spec.rb +60 -0
  238. data/spec/lib/apipie/method_description_spec.rb +133 -0
  239. data/spec/lib/apipie/no_documented_method_spec.rb +17 -0
  240. data/spec/lib/apipie/param_description/deprecation_spec.rb +31 -0
  241. data/spec/lib/apipie/param_description_spec.rb +671 -0
  242. data/spec/lib/apipie/param_group_spec.rb +61 -0
  243. data/spec/lib/apipie/resource_description_spec.rb +91 -0
  244. data/spec/lib/apipie/response_description/response_object_spec.rb +22 -0
  245. data/spec/lib/apipie/response_description_spec.rb +56 -0
  246. data/spec/lib/apipie/response_does_not_match_swagger_schema_spec.rb +35 -0
  247. data/spec/lib/apipie/swagger_generator_spec.rb +94 -0
  248. data/spec/lib/apipie/validator_spec.rb +149 -0
  249. data/spec/lib/rake_spec.rb +69 -0
  250. data/spec/lib/swagger/openapi_2_0_schema.json +1614 -0
  251. data/spec/lib/swagger/rake_swagger_spec.rb +159 -0
  252. data/spec/lib/swagger/swagger_dsl_spec.rb +664 -0
  253. data/spec/lib/validators/array_validator_spec.rb +85 -0
  254. data/spec/spec_helper.rb +92 -0
  255. data/spec/support/custom_bool_validator.rb +17 -0
  256. data/spec/support/rake.rb +21 -0
  257. data/spec/test_engine/memes_controller_spec.rb +10 -0
  258. metadata +499 -0
@@ -0,0 +1,86 @@
1
+ module Apipie
2
+
3
+ class Error < StandardError
4
+ end
5
+
6
+ class ParamError < Error
7
+ end
8
+
9
+ class UnknownCode < Error
10
+ end
11
+
12
+ class ReturnsMultipleDefinitionError < Error
13
+ def to_s
14
+ "a 'returns' statement cannot indicate both array_of and type"
15
+ end
16
+ end
17
+
18
+ # abstract
19
+ class DefinedParamError < ParamError
20
+ attr_accessor :param
21
+
22
+ def initialize(param)
23
+ @param = param
24
+ end
25
+ end
26
+
27
+ class ParamMultipleMissing < ParamError
28
+ attr_accessor :params
29
+
30
+ def initialize(params)
31
+ @params = params
32
+ end
33
+
34
+ def to_s
35
+ params.map do |param|
36
+ ParamMissing.new(param).to_s
37
+ end.join("\n")
38
+ end
39
+ end
40
+
41
+ class ParamMissing < DefinedParamError
42
+ def to_s
43
+ unless @param.options[:missing_message].nil?
44
+ if @param.options[:missing_message].kind_of?(Proc)
45
+ @param.options[:missing_message].call
46
+ else
47
+ @param.options[:missing_message].to_s
48
+ end
49
+ else
50
+ "Missing parameter #{@param.name}"
51
+ end
52
+ end
53
+ end
54
+
55
+ class UnknownParam < DefinedParamError
56
+ def to_s
57
+ "Unknown parameter #{@param}"
58
+ end
59
+ end
60
+
61
+ class ParamInvalid < DefinedParamError
62
+ attr_accessor :value, :error
63
+
64
+ def initialize(param, value, error)
65
+ super(param)
66
+ @value = value
67
+ @error = error
68
+ end
69
+
70
+ def to_s
71
+ "Invalid parameter '#{@param}' value #{@value.inspect}: #{@error}"
72
+ end
73
+ end
74
+
75
+ class ResponseDoesNotMatchSwaggerSchema < Error
76
+ def initialize(controller_name, method_name, response_code, error_messages, schema, returned_object)
77
+ super("Response does not match swagger schema (#{controller_name}##{method_name} #{response_code}): #{error_messages}\nSchema: #{JSON(schema)}\nReturned object: #{returned_object}")
78
+ end
79
+ end
80
+
81
+ class NoDocumentedMethod < Error
82
+ def initialize(controller_name, method_name)
83
+ super("There is no documented method #{controller_name}##{method_name}")
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,116 @@
1
+ module Apipie
2
+ module Extractor
3
+ class Collector
4
+ attr_reader :descriptions, :records
5
+
6
+ def initialize
7
+ @api_controllers_paths = Apipie.api_controllers_paths
8
+ @ignored = Apipie.configuration.ignored_by_recorder
9
+ @descriptions = Hash.new do |h, k|
10
+ h[k] = {:params => {}, :errors => Set.new}
11
+ end
12
+ @records = Hash.new { |h,k| h[k] = [] }
13
+ end
14
+
15
+ def controller_full_path(controller)
16
+ Apipie::Extractor.controller_path controller.controller_path
17
+ end
18
+
19
+ def ignore_call?(record)
20
+ return true unless record[:controller]
21
+ return true if @ignored.include?(record[:controller].name)
22
+ return true if @ignored.include?("#{Apipie.get_resource_id(record[:controller].name)}##{record[:action]}")
23
+ return true unless @api_controllers_paths.include?(controller_full_path(record[:controller]))
24
+ end
25
+
26
+ def handle_record(record)
27
+ if ignore_call?(record)
28
+ Extractor.logger.info("REST_API: skipping #{record_to_s(record)}")
29
+ else
30
+ add_to_records(record)
31
+ refine_description(record)
32
+ end
33
+ end
34
+
35
+ def add_to_records(record)
36
+ key = "#{Apipie.get_resource_id(record[:controller])}##{record[:action]}"
37
+ @records[key] << record
38
+ end
39
+
40
+ def refine_description(record)
41
+ description = @descriptions["#{record[:controller].name}##{record[:action]}"]
42
+ description[:controller] ||= record[:controller]
43
+ description[:action] ||= record[:action]
44
+
45
+ refine_errors_description(description, record)
46
+ refine_params_description(description[:params], record[:params])
47
+ end
48
+
49
+ def refine_errors_description(description, record)
50
+ if record[:code].to_i >= 300 && !description[:errors].any? { |e| e[:code].to_i == record[:code].to_i }
51
+ description[:errors] << {:code => record[:code]}
52
+ end
53
+ end
54
+
55
+ def refine_params_description(params_desc, recorded_params)
56
+ recorded_params.each do |key, value|
57
+ params_desc[key] ||= {}
58
+ param_desc = params_desc[key]
59
+
60
+ if value.nil?
61
+ param_desc[:allow_nil] = true
62
+ else
63
+ # we specify what type it might be. At the end the first type
64
+ # that left is taken as the more general one
65
+ unless param_desc[:type]
66
+ param_desc[:type] = [:bool, :boolean, :number]
67
+ param_desc[:type] << Hash if value.is_a? Hash
68
+ param_desc[:type] << :undef
69
+ end
70
+
71
+ if [:boolean, :bool].include?(param_desc[:type].first) && (! [true, false, 1, 0].include?(value))
72
+ param_desc[:type].shift
73
+ end
74
+
75
+ if param_desc[:type].first == :number && (key.to_s !~ /id$/ || !Apipie::Validator::NumberValidator.validate(value))
76
+ param_desc[:type].shift
77
+ end
78
+
79
+ if param_desc[:type].first == :decimal && (key.to_s !~ /id$/ || !Apipie::Validator::DecimalValidator.validate(value))
80
+ param_desc[:type].shift
81
+ end
82
+ end
83
+
84
+ if value.is_a? Hash
85
+ param_desc[:nested] ||= {}
86
+ refine_params_description(param_desc[:nested], value)
87
+ end
88
+ end
89
+ end
90
+
91
+ def finalize_descriptions
92
+ @descriptions.each_value do |desc|
93
+ add_routes_info(desc)
94
+ end
95
+ return @descriptions
96
+ end
97
+
98
+ def add_routes_info(desc)
99
+ api_prefix = Apipie.api_base_url.sub(%r{/$},"")
100
+ desc[:api] = Apipie::Extractor.apis_from_routes[[desc[:controller].name, desc[:action]]]
101
+ if desc[:api]
102
+ desc[:params].each do |name, param|
103
+ if desc[:api].all? { |a| a[:path].include?(":#{name}") }
104
+ param[:required] = true
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ def record_to_s(record)
111
+ "#{record[:controller]}##{record[:action]}"
112
+ end
113
+
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,193 @@
1
+ module Apipie
2
+ module Extractor
3
+ class Recorder
4
+ MULTIPART_BOUNDARY = 'APIPIE_RECORDER_EXAMPLE_BOUNDARY'.freeze
5
+
6
+ def initialize
7
+ @ignored_params = [:controller, :action]
8
+ end
9
+
10
+ def analyse_env(env)
11
+ @verb = env["REQUEST_METHOD"].to_sym
12
+ @path = env["PATH_INFO"].sub(%r{^/*},"/")
13
+ @query = env["QUERY_STRING"] unless env["QUERY_STRING"].blank?
14
+ @params = Rack::Utils.parse_nested_query(@query)
15
+ @params.merge!(env["action_dispatch.request.request_parameters"] || {})
16
+ rack_input = env["rack.input"]
17
+ if data = parse_data(rack_input&.read)
18
+ @request_data = data
19
+ elsif form_hash = env["rack.request.form_hash"]
20
+ @request_data = reformat_multipart_data(form_hash)
21
+ end
22
+ rack_input&.rewind
23
+ end
24
+
25
+ def analyse_controller(controller)
26
+ @controller = controller.class
27
+ @action = Apipie.configuration.api_action_matcher.call(controller)
28
+ end
29
+
30
+ def analyse_response(response)
31
+ if response.last.respond_to?(:body) && data = parse_data(response.last.body)
32
+ @response_data = if response[1]['Content-Disposition'].to_s.start_with?('attachment')
33
+ '<STREAMED ATTACHMENT FILE>'
34
+ else
35
+ data
36
+ end
37
+ end
38
+ @code = response.first
39
+ end
40
+
41
+ def analyze_functional_test(test_context)
42
+ request, response = test_context.request, test_context.response
43
+ @verb = request.request_method.to_sym
44
+ @path = request.path
45
+ @params = request.request_parameters
46
+ if [:POST, :PUT, :PATCH, :DELETE].include?(@verb)
47
+ @request_data = request.content_type == "multipart/form-data" ? reformat_multipart_data(@params) : @params
48
+ else
49
+ @query = request.query_string
50
+ end
51
+ if response.media_type != 'application/pdf'
52
+ @response_data = parse_data(response.body)
53
+ end
54
+ @code = response.code
55
+ end
56
+
57
+ def parse_data(data)
58
+ return nil if data.strip.blank?
59
+ JSON.parse(data)
60
+ rescue StandardError
61
+ data
62
+ end
63
+
64
+ def reformat_multipart_data(form)
65
+ form.empty? and return ''
66
+ lines = ["Content-Type: multipart/form-data; boundary=#{MULTIPART_BOUNDARY}",'']
67
+ boundary = "--#{MULTIPART_BOUNDARY}"
68
+ form.each do |key, attrs|
69
+ if attrs.is_a?(String) # rubocop:disable Style/CaseLikeIf
70
+ lines << boundary << content_disposition(key) << "Content-Length: #{attrs.size}" << '' << attrs
71
+ elsif attrs.is_a?(Rack::Test::UploadedFile) || attrs.is_a?(ActionDispatch::Http::UploadedFile)
72
+ reformat_uploaded_file(boundary, attrs, key, lines)
73
+ elsif attrs.is_a?(Array)
74
+ reformat_array(boundary, attrs, key, lines)
75
+ elsif attrs.is_a?(TrueClass) || attrs.is_a?(FalseClass)
76
+ reformat_boolean(boundary, attrs, key, lines)
77
+ else
78
+ reformat_hash(boundary, attrs, lines)
79
+ end
80
+ end
81
+ lines << "#{boundary}--"
82
+ lines.join("\n")
83
+ end
84
+
85
+ def reformat_hash(boundary, attrs, lines)
86
+ if head = attrs[:head]
87
+ lines << boundary
88
+ lines.concat(head.split("\r\n"))
89
+ # To avoid large and/or binary file bodies, simply indicate the contents in the output.
90
+ lines << '' << %{... contents of "#{attrs[:name]}" ...}
91
+ else
92
+ # Look for subelements that contain a part.
93
+ attrs.each_value { |v| v.is_a?(Hash) and reformat_hash(boundary, v, lines) }
94
+ end
95
+ end
96
+
97
+ def reformat_boolean(boundary, attrs, key, lines)
98
+ lines << boundary << content_disposition(key)
99
+ lines << '' << attrs.to_s
100
+ end
101
+
102
+ def reformat_array(boundary, attrs, key, lines)
103
+ attrs.each do |item|
104
+ lines << boundary << content_disposition("#{key}[]")
105
+ lines << '' << item
106
+ end
107
+ end
108
+
109
+ def reformat_uploaded_file(boundary, file, key, lines)
110
+ lines << boundary << %{#{content_disposition(key)}; filename="#{file.original_filename}"}
111
+ lines << "Content-Length: #{file.size}" << "Content-Type: #{file.content_type}" << "Content-Transfer-Encoding: binary"
112
+ lines << '' << %{... contents of "#{key}" ...}
113
+ end
114
+
115
+ def content_disposition(name)
116
+ %{Content-Disposition: form-data; name="#{name}"}
117
+ end
118
+
119
+ def reformat_data(data)
120
+ parsed = parse_data(data)
121
+ case parsed
122
+ when nil
123
+ nil
124
+ when String
125
+ parsed
126
+ else
127
+ JSON.pretty_generate().gsub(/: \[\s*\]/,": []").gsub(/\{\s*\}/,"{}")
128
+ end
129
+ end
130
+
131
+ def record
132
+ if @controller
133
+ {:controller => @controller,
134
+ :action => @action,
135
+ :verb => @verb,
136
+ :path => @path,
137
+ :params => @params,
138
+ :query => @query,
139
+ :request_data => @request_data,
140
+ :response_data => @response_data,
141
+ :code => @code}
142
+ else
143
+ nil
144
+ end
145
+ end
146
+
147
+ protected
148
+
149
+ def api_description
150
+ end
151
+
152
+ class Middleware
153
+ def initialize(app)
154
+ @app = app
155
+ end
156
+
157
+ def call(env)
158
+ if Apipie.configuration.record
159
+ analyze(env) do
160
+ @app.call(env)
161
+ end
162
+ else
163
+ @app.call(env)
164
+ end
165
+ end
166
+
167
+ def analyze(env, &block)
168
+ Apipie::Extractor.call_recorder.analyse_env(env)
169
+ response = block.call
170
+ Apipie::Extractor.call_recorder.analyse_response(response)
171
+ Apipie::Extractor.call_finished
172
+ response
173
+ ensure
174
+ Apipie::Extractor.clean_call_recorder
175
+ end
176
+ end
177
+
178
+ module FunctionalTestRecording
179
+ def process(*) # action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
180
+ ret = super
181
+ if Apipie.configuration.record
182
+ Apipie::Extractor.call_recorder.analyze_functional_test(self)
183
+ Apipie::Extractor.call_finished
184
+ end
185
+ ret
186
+ ensure
187
+ Apipie::Extractor.clean_call_recorder
188
+ end
189
+ ruby2_keywords :process if respond_to?(:ruby2_keywords, true)
190
+ end
191
+ end
192
+ end
193
+ end