apipierails3 0.0.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 (171) hide show
  1. checksums.yaml +17 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +27 -0
  5. data/APACHE-LICENSE-2.0 +202 -0
  6. data/CHANGELOG.md +469 -0
  7. data/Gemfile +1 -0
  8. data/Gemfile.rails32 +6 -0
  9. data/Gemfile.rails41 +6 -0
  10. data/Gemfile.rails42 +11 -0
  11. data/Gemfile.rails50 +6 -0
  12. data/Gemfile.rails51 +7 -0
  13. data/MIT-LICENSE +20 -0
  14. data/NOTICE +4 -0
  15. data/PROPOSAL_FOR_RESPONSE_DESCRIPTIONS.md +244 -0
  16. data/README.rst +1874 -0
  17. data/Rakefile +13 -0
  18. data/apipierails3.gemspec +27 -0
  19. data/app/controllers/apipie/apipies_controller.rb +199 -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 +138 -0
  23. data/app/public/apipie/javascripts/bundled/bootstrap.js +1726 -0
  24. data/app/public/apipie/javascripts/bundled/jquery.js +5 -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 +12 -0
  28. data/app/public/apipie/stylesheets/bundled/bootstrap.min.css +689 -0
  29. data/app/public/apipie/stylesheets/bundled/prettify.css +30 -0
  30. data/app/views/apipie/apipies/_disqus.html.erb +13 -0
  31. data/app/views/apipie/apipies/_errors.html.erb +23 -0
  32. data/app/views/apipie/apipies/_headers.html.erb +26 -0
  33. data/app/views/apipie/apipies/_languages.erb +6 -0
  34. data/app/views/apipie/apipies/_metadata.erb +1 -0
  35. data/app/views/apipie/apipies/_method_detail.erb +61 -0
  36. data/app/views/apipie/apipies/_params.html.erb +42 -0
  37. data/app/views/apipie/apipies/_params_plain.html.erb +20 -0
  38. data/app/views/apipie/apipies/apipie_404.html.erb +17 -0
  39. data/app/views/apipie/apipies/apipie_checksum.json.erb +1 -0
  40. data/app/views/apipie/apipies/getting_started.html.erb +6 -0
  41. data/app/views/apipie/apipies/index.html.erb +56 -0
  42. data/app/views/apipie/apipies/method.html.erb +41 -0
  43. data/app/views/apipie/apipies/plain.html.erb +77 -0
  44. data/app/views/apipie/apipies/resource.html.erb +80 -0
  45. data/app/views/apipie/apipies/static.html.erb +103 -0
  46. data/app/views/layouts/apipie/apipie.html.erb +27 -0
  47. data/config/locales/de.yml +28 -0
  48. data/config/locales/en.yml +32 -0
  49. data/config/locales/es.yml +28 -0
  50. data/config/locales/fr.yml +31 -0
  51. data/config/locales/it.yml +31 -0
  52. data/config/locales/ja.yml +31 -0
  53. data/config/locales/pl.yml +28 -0
  54. data/config/locales/pt-BR.yml +28 -0
  55. data/config/locales/ru.yml +28 -0
  56. data/config/locales/tr.yml +28 -0
  57. data/config/locales/zh-CN.yml +28 -0
  58. data/config/locales/zh-TW.yml +28 -0
  59. data/images/screenshot-1.png +0 -0
  60. data/images/screenshot-2.png +0 -0
  61. data/lib/apipie/apipie_module.rb +83 -0
  62. data/lib/apipie/application.rb +462 -0
  63. data/lib/apipie/configuration.rb +186 -0
  64. data/lib/apipie/dsl_definition.rb +607 -0
  65. data/lib/apipie/error_description.rb +44 -0
  66. data/lib/apipie/errors.rb +86 -0
  67. data/lib/apipie/extractor.rb +177 -0
  68. data/lib/apipie/extractor/collector.rb +117 -0
  69. data/lib/apipie/extractor/recorder.rb +166 -0
  70. data/lib/apipie/extractor/writer.rb +454 -0
  71. data/lib/apipie/helpers.rb +73 -0
  72. data/lib/apipie/markup.rb +48 -0
  73. data/lib/apipie/method_description.rb +273 -0
  74. data/lib/apipie/middleware/checksum_in_headers.rb +35 -0
  75. data/lib/apipie/param_description.rb +280 -0
  76. data/lib/apipie/railtie.rb +9 -0
  77. data/lib/apipie/resource_description.rb +124 -0
  78. data/lib/apipie/response_description.rb +131 -0
  79. data/lib/apipie/response_description_adapter.rb +200 -0
  80. data/lib/apipie/routes_formatter.rb +33 -0
  81. data/lib/apipie/routing.rb +16 -0
  82. data/lib/apipie/rspec/response_validation_helper.rb +192 -0
  83. data/lib/apipie/see_description.rb +39 -0
  84. data/lib/apipie/static_dispatcher.rb +69 -0
  85. data/lib/apipie/swagger_generator.rb +707 -0
  86. data/lib/apipie/tag_list_description.rb +11 -0
  87. data/lib/apipie/validator.rb +526 -0
  88. data/lib/apipie/version.rb +3 -0
  89. data/lib/apipierails3.rb +25 -0
  90. data/lib/generators/apipie/install/README +6 -0
  91. data/lib/generators/apipie/install/install_generator.rb +25 -0
  92. data/lib/generators/apipie/install/templates/initializer.rb.erb +7 -0
  93. data/lib/generators/apipie/views_generator.rb +11 -0
  94. data/lib/tasks/apipie.rake +345 -0
  95. data/rel-eng/packages/.readme +3 -0
  96. data/rel-eng/packages/rubygem-apipie-rails +1 -0
  97. data/rel-eng/tito.props +5 -0
  98. data/spec/controllers/api/v1/architectures_controller_spec.rb +29 -0
  99. data/spec/controllers/api/v2/architectures_controller_spec.rb +12 -0
  100. data/spec/controllers/api/v2/nested/resources_controller_spec.rb +11 -0
  101. data/spec/controllers/apipies_controller_spec.rb +273 -0
  102. data/spec/controllers/concerns_controller_spec.rb +42 -0
  103. data/spec/controllers/extended_controller_spec.rb +11 -0
  104. data/spec/controllers/users_controller_spec.rb +740 -0
  105. data/spec/dummy/Rakefile +7 -0
  106. data/spec/dummy/app/controllers/api/base_controller.rb +4 -0
  107. data/spec/dummy/app/controllers/api/v1/architectures_controller.rb +43 -0
  108. data/spec/dummy/app/controllers/api/v1/base_controller.rb +11 -0
  109. data/spec/dummy/app/controllers/api/v2/architectures_controller.rb +30 -0
  110. data/spec/dummy/app/controllers/api/v2/base_controller.rb +11 -0
  111. data/spec/dummy/app/controllers/api/v2/nested/architectures_controller.rb +32 -0
  112. data/spec/dummy/app/controllers/api/v2/nested/resources_controller.rb +33 -0
  113. data/spec/dummy/app/controllers/application_controller.rb +18 -0
  114. data/spec/dummy/app/controllers/concerns/extending_concern.rb +11 -0
  115. data/spec/dummy/app/controllers/concerns/sample_controller.rb +41 -0
  116. data/spec/dummy/app/controllers/concerns_controller.rb +8 -0
  117. data/spec/dummy/app/controllers/extended_controller.rb +14 -0
  118. data/spec/dummy/app/controllers/files_controller.rb +5 -0
  119. data/spec/dummy/app/controllers/overridden_concerns_controller.rb +31 -0
  120. data/spec/dummy/app/controllers/pets_controller.rb +408 -0
  121. data/spec/dummy/app/controllers/pets_using_auto_views_controller.rb +73 -0
  122. data/spec/dummy/app/controllers/pets_using_self_describing_classes_controller.rb +95 -0
  123. data/spec/dummy/app/controllers/tagged_cats_controller.rb +32 -0
  124. data/spec/dummy/app/controllers/tagged_dogs_controller.rb +15 -0
  125. data/spec/dummy/app/controllers/twitter_example_controller.rb +307 -0
  126. data/spec/dummy/app/controllers/users_controller.rb +297 -0
  127. data/spec/dummy/app/views/layouts/application.html.erb +21 -0
  128. data/spec/dummy/config.ru +4 -0
  129. data/spec/dummy/config/application.rb +49 -0
  130. data/spec/dummy/config/boot.rb +10 -0
  131. data/spec/dummy/config/database.yml +21 -0
  132. data/spec/dummy/config/environment.rb +8 -0
  133. data/spec/dummy/config/environments/development.rb +28 -0
  134. data/spec/dummy/config/environments/production.rb +52 -0
  135. data/spec/dummy/config/environments/test.rb +38 -0
  136. data/spec/dummy/config/initializers/apipie.rb +110 -0
  137. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  138. data/spec/dummy/config/initializers/inflections.rb +10 -0
  139. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  140. data/spec/dummy/config/initializers/secret_token.rb +8 -0
  141. data/spec/dummy/config/initializers/session_store.rb +8 -0
  142. data/spec/dummy/config/locales/en.yml +5 -0
  143. data/spec/dummy/config/routes.rb +51 -0
  144. data/spec/dummy/db/.gitkeep +0 -0
  145. data/spec/dummy/doc/apipie_examples.json +1 -0
  146. data/spec/dummy/doc/users/desc_from_file.md +1 -0
  147. data/spec/dummy/public/404.html +26 -0
  148. data/spec/dummy/public/422.html +26 -0
  149. data/spec/dummy/public/500.html +26 -0
  150. data/spec/dummy/public/favicon.ico +0 -0
  151. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  152. data/spec/dummy/script/rails +6 -0
  153. data/spec/lib/application_spec.rb +49 -0
  154. data/spec/lib/extractor/extractor_spec.rb +9 -0
  155. data/spec/lib/extractor/middleware_spec.rb +44 -0
  156. data/spec/lib/extractor/writer_spec.rb +110 -0
  157. data/spec/lib/file_handler_spec.rb +18 -0
  158. data/spec/lib/method_description_spec.rb +98 -0
  159. data/spec/lib/param_description_spec.rb +345 -0
  160. data/spec/lib/param_group_spec.rb +60 -0
  161. data/spec/lib/rake_spec.rb +71 -0
  162. data/spec/lib/resource_description_spec.rb +48 -0
  163. data/spec/lib/swagger/openapi_2_0_schema.json +1607 -0
  164. data/spec/lib/swagger/rake_swagger_spec.rb +139 -0
  165. data/spec/lib/swagger/response_validation_spec.rb +104 -0
  166. data/spec/lib/swagger/swagger_dsl_spec.rb +658 -0
  167. data/spec/lib/validator_spec.rb +113 -0
  168. data/spec/lib/validators/array_validator_spec.rb +85 -0
  169. data/spec/spec_helper.rb +109 -0
  170. data/spec/support/rake.rb +21 -0
  171. metadata +415 -0
data/Gemfile ADDED
@@ -0,0 +1 @@
1
+ ./Gemfile.rails50
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rails', '~> 3.2.0'
6
+ gem 'test-unit', '~> 3.0'
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rails', '~> 4.1.0'
6
+ gem 'mime-types', '~> 2.99.3'
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rails', '~> 4.2.5'
6
+ gem 'mime-types', '~> 2.99.3'
7
+
8
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.1.0')
9
+ gem 'nokogiri', '~> 1.6.8'
10
+ gem 'rdoc', '~> 4.2.2'
11
+ end
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rails', '~> 3.2.0'
6
+ gem 'test-unit', '~> 3.0'
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rails', '~> 5.1.0.rc1'
6
+ gem 'mime-types', '~> 2.99.3'
7
+ gem 'rails-controller-testing'
@@ -0,0 +1,20 @@
1
+ Copyright 2012 Pavel Pokorný
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/NOTICE ADDED
@@ -0,0 +1,4 @@
1
+ This gem is released under MIT license. Copy is included in file MIT-LICENSE.
2
+
3
+ Twitter Bootstrap and google-code-prettify are licensed under Apache License
4
+ 2.0. Copy is included in file APACHE-LICENSE-2.0.
@@ -0,0 +1,244 @@
1
+ # Proposal for supporting response descriptions in Apipie
2
+
3
+ ## Rationale
4
+
5
+ Swagger allows API authors to describe the structure of objects returned by REST API calls.
6
+ Client authors and code generators can use such descriptions for various purposes, such as verification,
7
+ autocompletion, and so forth.
8
+
9
+ The current Apipie DSL allows API authors to indicate returned error codes (using the `error` keyword),
10
+ but does not support descriptions of returned data objects. As such, swagger files
11
+ generated from the DSL do not include those, and are somewhat limited in their value.
12
+
13
+ This document proposes a minimalistic approach to extending the Apipie DSL to allow description of response
14
+ objects, and including those descriptions in generated swagger files.
15
+
16
+ ## Design Objectives
17
+
18
+ * Full backward compatibility with the existing DSL
19
+ * Minimal implementation effort
20
+ * Enough expressiveness to support common use cases
21
+ * Optional integration of the DSL with advanced JSON generators (such as Grape-Entity)
22
+ * Allowing developers to easily verify that actual responses match the response declarations
23
+
24
+ ## Approach
25
+
26
+ #### Add a `returns` keyword to the DSL, based on the existing `error` keyword
27
+
28
+ Currently, returned error codes are indicated using the `error` keyword, for example:
29
+ ```ruby
30
+ api :GET, "/users/:id", "Show user profile"
31
+ error :code => 401, :desc => "Unauthorized"
32
+ ```
33
+
34
+ The proposed approach is to add a `returns` keyword, that has the following syntax:
35
+ ```ruby
36
+ returns <type-identifier> [, :code => <number>] [, :desc => <response-description>]
37
+ ```
38
+
39
+ For example:
40
+ ```ruby
41
+ api :GET, "/users/:id", "Show user profile"
42
+ error :code => 401, :desc => "Unauthorized"
43
+ returns :SomeTypeIdentifier # :code is not specified, so it is assumed to be 200
44
+ ```
45
+
46
+
47
+ #### Leverage `param_group` for response object description
48
+
49
+ Apipie currently has a mechanism for describing complex objects using the `param_group` keyword.
50
+ It seems reasonable to leverage this mechanism as the basis of the response object description mechanism,
51
+ so that the `<type-identifier>` in the `returns` keyword will be the name of a param_group.
52
+
53
+ For example:
54
+ ```ruby
55
+ def_param_group :user do
56
+ param :user, Hash, :desc => "User info", :required => true, :action_aware => true do
57
+ param_group :credentials
58
+ param :membership, ["standard","premium"], :desc => "User membership", :allow_nil => false
59
+ end
60
+ end
61
+
62
+ api :GET, "/users/:id", "Get user record"
63
+ returns :user, "the requested record"
64
+ error :code => 404, :desc => "no user with the specified id"
65
+ ```
66
+
67
+ Implementation of this DSL extension would involve - as part of the implementation of the `returns` keyword -
68
+ the generation of a Apipie::ParamDescription object that has a Hash validator pointing to the param_group block.
69
+
70
+ #### Extend action-aware functionality to include 'response-only' parameters
71
+
72
+ In CRUD operations, it is common for `param_group` input definitions to be very similar to the
73
+ output of the API, with the exception of a very small number of fields (such as the `:id` field
74
+ which usually appears in the response, but is not described in the `param_group` because it is passed as a
75
+ path parameter).
76
+
77
+ To allow reuse of the `param_group`, it would be useful to its definition to describe parameters that are not passed
78
+ in the request but are returned in the response. This would be implementing by extending the DSL to
79
+ support a `:only_in => :response` option on `param` definitions. Similarly, params could be defined to be
80
+ `:only_in => :request` to indicate that they will not be included in the response.
81
+
82
+ For example:
83
+ ```ruby
84
+ # in the following group, the :id param is ignored in requests, but included in responses
85
+ def_param_group :user do
86
+ param :user, Hash, :desc => "User info", :required => true, :action_aware => true do
87
+ param :id, Integer, :only_in => :response
88
+ param :requested_id, Integer, :only_in => :request
89
+ param_group :credentials
90
+ param :membership, ["standard","premium"], :desc => "User membership", :allow_nil => false
91
+ end
92
+ end
93
+
94
+ api :GET, "/users/:id", "Get user record"
95
+ returns :user, :desc => "the requested record" # includes the :id field, because this is a response
96
+ error :code => 404, :desc => "no user with the specified id"
97
+ ```
98
+
99
+
100
+ #### Support `:array_of => <param_group-name>` in the `returns` keyword
101
+
102
+ Very often, a REST API call returns an array of some previously-defined object
103
+ (the most common example an `index` operation that returns an array of the same entity returned by a `show` request),
104
+ and it would be tedious to have to define a separate `param_group` for each one.
105
+
106
+ For added convenience, the `returns` keyword will also support an `:array_of =>` construct
107
+ to specify that an API call returns an array of some object type.
108
+
109
+ For example:
110
+ ```ruby
111
+ api :GET, "/users", "Get all user records"
112
+ returns :array_of => :user, :desc => "the requested user records"
113
+
114
+ api :GET, "/user/:id", "Get a single user record"
115
+ returns :user, :desc => "the requested user record"
116
+ ```
117
+
118
+ #### Integration with advanced JSON generators using an [adapter](https://en.wikipedia.org/wiki/Adapter_pattern) to `param_group`
119
+
120
+ While it makes sense for the sake of simplicity to leverage the `param_group` construct to describe
121
+ returned objects, it is likely that many developers will prefer to unify the
122
+ description of the response with the actual generation of the JSON.
123
+
124
+ Some JSON-generation libraries, such as [Grape-Entity](https://github.com/ruby-grape/grape-entity),
125
+ provide a declarative interface for describing an object model, allowing both runtime
126
+ generation of the response, as well as the ability to traverse the description to auto-generate
127
+ documentation.
128
+
129
+ Such libraries could be integrated with Apipie using adapters that wrap the library-specific
130
+ object description and expose an API that includes a `params_ordered` method that behaves in a
131
+ similar manner to [`Apipie::HashValidator.params_ordered`](https://github.com/Apipie/apipie-rails/blob/cfb42198bc39b5b30d953ba5a8b523bafdb4f897/lib/apipie/validator.rb#L315).
132
+ Such an adapter would make it possible to pass an externally-defined entity to the `returns` keyword
133
+ as if it were a `param_group`.
134
+
135
+ Such adapters can be created easily by having a class respond to `#describe_own_properties`
136
+ with an array of property description objects. When such a class is specified as the
137
+ parameter to a `returns` declaration, Apipie would query the class for its properties
138
+ by calling `<Class>#describe_own_properties`.
139
+
140
+ For example:
141
+ ```ruby
142
+ # here is a class that can describe itself to Apipie
143
+ class Animal
144
+ def self.describe_own_properties
145
+ [
146
+ Apipie::prop(:id, Integer, {:description => 'Name of pet', :required => false}),
147
+ Apipie::prop(:animal_type, 'string', {:description => 'Type of pet', :values => ["dog", "cat", "iguana", "kangaroo"]}),
148
+ Apipie::additional_properties(false)
149
+ ]
150
+ end
151
+
152
+ attr_accessor :id
153
+ attr_accessor :animal_type
154
+ end
155
+
156
+ # Here is an API defined as returning Animal objects.
157
+ # Apipie creates an internal adapter by querying Animal#describe_own_properties
158
+ api :GET, "/animals", "Get all records"
159
+ returns :array_of => Animal, :desc => "the requested records"
160
+ ```
161
+
162
+ The `#describe_own_properties` mechanism can also be used with reflection so that a
163
+ class would query its own properties and populate the response to `#describe_own_properties`
164
+ automatically. See [this gist](https://gist.github.com/elasti-ron/ac145b2c85547487ca33e5216a69f527)
165
+ for an example of how Grape::Entity classes can automatically describe itself to Apipie
166
+
167
+ #### Response validation
168
+
169
+ The swagger definitions created by Apipie can be used to auto-generate clients that access the
170
+ described APIs. Those clients will break if the responses returned from the API do not match
171
+ the declarations. As such, it is very important to include unit tests that validate the actual
172
+ responses against the swagger definitions.
173
+
174
+ The ~~proposed~~ implemented mechanism provides two ways to include such validations in RSpec unit tests:
175
+ manual (using an RSpec matcher) and automated (by injecting a test into the http operations 'get', 'post',
176
+ raising an error if there is no match).
177
+
178
+ Example of the manual mechanism:
179
+
180
+ ```ruby
181
+ require 'apipie/rspec/response_validation_helper'
182
+
183
+ RSpec.describe MyController, :type => :controller, :show_in_doc => true do
184
+
185
+ describe "GET stuff with response validation" do
186
+ render_views # this makes sure the 'get' operation will actually
187
+ # return the rendered view even though this is a Controller spec
188
+
189
+ it "does something" do
190
+ response = get :index, {format: :json}
191
+
192
+ # the following expectation will fail if the returned object
193
+ # does not match the 'returns' declaration in the Controller,
194
+ # or if there is no 'returns' declaration for the returned
195
+ # HTTP status code
196
+ expect(response).to match_declared_responses
197
+ end
198
+ end
199
+ ```
200
+
201
+
202
+ Example of the automated mechanism:
203
+ ```ruby
204
+ require 'apipie/rspec/response_validation_helper'
205
+
206
+ RSpec.describe MyController, :type => :controller, :show_in_doc => true do
207
+
208
+ describe "GET stuff with response validation" do
209
+ render_views
210
+ auto_validate_rendered_views
211
+
212
+ it "does something" do
213
+ get :index, {format: :json}
214
+ end
215
+ it "does something else" do
216
+ get :another_index, {format: :json}
217
+ end
218
+ end
219
+
220
+ describe "GET stuff without response validation" do
221
+ it "does something" do
222
+ get :index, {format: :json}
223
+ end
224
+ it "does something else" do
225
+ get :another_index, {format: :json}
226
+ end
227
+ end
228
+ ```
229
+
230
+ Explanation of the implementation approach:
231
+
232
+ The Apipie Swagger Generator is enhanced to allow extraction of the JSON schema of the response object
233
+ for any controller#action[http-status]. When validation is required, the validator receives the
234
+ actual response object (along with information about the controller, action and http status code),
235
+ queries the swagger generator to get the schema, and uses the json-schema validator (gem) to validate
236
+ one against the other.
237
+
238
+ Note that there is a slight complication here: while supported by JSON-shema, the Swagger 2.0
239
+ specification does not support a mechanism to declare that fields in the response could be null.
240
+ As such, for a response that contains `null` fields, if the exact same schema used in the swagger def
241
+ is passed to the json-schema validator, the validation fails. To work around this issue, when asked
242
+ to provide the schema for the purpose of response validation (i.e., not for inclusion in the swagger),
243
+ the Apipie Swagger Generator creates a slightly modified schema which declares null values to be valid.
244
+
@@ -0,0 +1,1874 @@
1
+ ========================
2
+ API Documentation Tool
3
+ ========================
4
+
5
+ .. image:: https://travis-ci.org/Apipie/apipie-rails.svg?branch=master
6
+ :target: https://travis-ci.org/Apipie/apipie-rails
7
+ .. image:: https://codeclimate.com/github/Apipie/apipie-rails.svg
8
+ :target: https://codeclimate.com/github/Apipie/apipie-rails
9
+ .. image:: https://badges.gitter.im/Apipie/apipie-rails.svg
10
+ :alt: Join the chat at https://gitter.im/Apipie/apipie-rails
11
+ :target: https://gitter.im/Apipie/apipie-rails?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
12
+ .. image:: https://img.shields.io/gem/v/apipie-rails.svg
13
+ :alt: Latest release
14
+ :target: https://rubygems.org/gems/apipie-rails
15
+
16
+ Apipie-rails is a DSL and Rails engine for documenting your RESTful
17
+ API. Instead of traditional use of ``#comments``, Apipie lets you
18
+ describe the code, through the code. This brings advantages like:
19
+
20
+ * No need to learn yet another syntax, you already know Ruby, right?
21
+ * Possibility of reusing the docs for other purposes (such as validation)
22
+ * Easier to extend and maintain (no string parsing involved)
23
+ * Possibility of reusing other sources for documentation purposes (such as
24
+ routes etc.)
25
+
26
+ The documentation is available from within your app (by default under the
27
+ ``/apipie`` path.) In development mode, you can see the changes as you
28
+ go. It's markup language agnostic, and even provides an API for reusing
29
+ the documentation data in JSON.
30
+
31
+ Getting started
32
+ ---------------
33
+
34
+ The easiest way to get Apipie up and running with your app is:
35
+
36
+ .. code:: sh
37
+
38
+ echo "gem 'apipierails3'" >> Gemfile
39
+ bundle install
40
+ rails g apipie:install
41
+
42
+ Now you can start documenting your resources and actions (see
43
+ `DSL Reference`_ for more info):
44
+
45
+ .. code:: ruby
46
+
47
+ api :GET, '/users/:id'
48
+ param :id, :number, desc: 'id of the requested user'
49
+ def show
50
+ # ...
51
+ end
52
+
53
+
54
+ Run your application and see the result at
55
+ ``http://localhost:3000/apipie``. For further processing, you can
56
+ use ``http://localhost:3000/apipie.json``.
57
+
58
+ For a more comprehensive getting started guide, see
59
+ `this demo <https://github.com/iNecas/apipie-demo>`_, which includes
60
+ features such as generating documentation from tests, recording examples etc.
61
+
62
+ Screenshots
63
+ -----------
64
+
65
+ .. image:: https://github.com/Apipie/apipie-rails/blob/master/images/screenshot-1.png
66
+ .. image:: https://github.com/Apipie/apipie-rails/blob/master/images/screenshot-2.png
67
+
68
+ Authors
69
+ -------
70
+
71
+ `Pajk <https://github.com/Pajk>`_ and `iNecas <https://github.com/iNecas>`_
72
+
73
+ Contributors
74
+ ------------
75
+
76
+ See `Contributors page <https://github.com/Apipie/apipie-rails/graphs/contributors>`_. Special thanks to all of them!
77
+
78
+ License
79
+ -------
80
+
81
+ Apipie-rails is released under the `MIT License <http://opensource.org/licenses/MIT>`_
82
+
83
+ ===============
84
+ Documentation
85
+ ===============
86
+
87
+ .. contents:: `Table Of Contents`
88
+ :depth: 2
89
+
90
+ ===============
91
+ DSL Reference
92
+ ===============
93
+
94
+ Resource Description
95
+ --------------------
96
+
97
+ You can describe a resource on the controller level. The description is introduced by calling
98
+ ``resource_description do ... end``.
99
+
100
+ Inheritance is supported, so you can specify common params for group of controllers in their parent
101
+ class.
102
+
103
+ The following keywords are available (all are optional):
104
+
105
+ resource_id
106
+ How the resource will be referenced in Apipie (paths, ``see`` command etc.); by default `controller_name.downcase` is used.
107
+
108
+ name
109
+ Human readable name of resource. By default ``class.name.humanize`` is used.
110
+
111
+ short (also short_description)
112
+ Short description of the resource (it's shown on both the list of resources, and resource details)
113
+
114
+ desc (also description and full_description)
115
+ Full description of the resource (shown only in resource details)
116
+
117
+ param
118
+ Common params for all methods defined in controller/child controllers.
119
+
120
+ returns
121
+ Common responses for all methods defined in controller/child controllers.
122
+
123
+ api_base_url
124
+ What URL is the resource available under.
125
+
126
+ api_versions (also api_version)
127
+ What versions does the controller define the resource. (See `Versioning`_ for details.)
128
+
129
+ formats
130
+ Request / response formats.
131
+
132
+ error
133
+ Describe every possible error that can happen when calling all
134
+ methods defined in controller. HTTP response code and description can be provided.
135
+
136
+ app_info
137
+ In case of versioning, this sets app info description on a per_version basis.
138
+
139
+ meta
140
+ Hash or array with custom metadata.
141
+
142
+ deprecated
143
+ Boolean value indicating if the resource is marked as deprecated. (Default false)
144
+
145
+ Example:
146
+ ~~~~~~~~
147
+
148
+ .. code:: ruby
149
+
150
+ resource_description do
151
+ short 'Site members'
152
+ formats ['json']
153
+ param :id, Fixnum, :desc => "User ID", :required => false
154
+ param :resource_param, Hash, :desc => 'Param description for all methods' do
155
+ param :ausername, String, :desc => "Username for login", :required => true
156
+ param :apassword, String, :desc => "Password for login", :required => true
157
+ end
158
+ api_version "development"
159
+ error 404, "Missing"
160
+ error 500, "Server crashed for some <%= reason %>", :meta => {:anything => "you can think of"}
161
+ error :unprocessable_entity, "Could not save the entity."
162
+ returns :code => 403 do
163
+ property :reason, String, :desc => "Why this was forbidden"
164
+ end
165
+ meta :author => {:name => 'John', :surname => 'Doe'}
166
+ deprecated false
167
+ description <<-EOS
168
+ == Long description
169
+ Example resource for rest api documentation
170
+ These can now be accessed in <tt>shared/header</tt> with:
171
+ Headline: <%= headline %>
172
+ First name: <%= person.first_name %>
173
+
174
+ If you need to find out whether a certain local variable has been
175
+ assigned a value in a particular render call, you need to use the
176
+ following pattern:
177
+
178
+ <% if local_assigns.has_key? :headline %>
179
+ Headline: <%= headline %>
180
+ <% end %>
181
+
182
+ Testing using <tt>defined? headline</tt> will not work. This is an
183
+ implementation restriction.
184
+
185
+ === Template caching
186
+
187
+ By default, Rails will compile each template to a method in order
188
+ to render it. When you alter a template, Rails will check the
189
+ file's modification time and recompile it in development mode.
190
+ EOS
191
+ end
192
+
193
+
194
+ Method Description
195
+ ------------------
196
+
197
+ Then describe methods available to your API.
198
+
199
+ api
200
+ Describe how this method is exposed, and provide a short description.
201
+ The first parameter is HTTP method (one of :GET/:POST/:PUT/:DELETE).
202
+ The second parameter is the relative URL path which is mapped to this
203
+ method. The last parameter is the methods short description.
204
+ You can use this +api+ method more than once per method. It could
205
+ be useful when there are more routes mapped to it.
206
+
207
+ When providing just one argument (description), or no argument at all,
208
+ the paths will be loaded from the routes.rb file.
209
+
210
+ api!
211
+ Provide a short description and additional option.
212
+ The last parameter is the methods short description.
213
+ The paths will be loaded from routes.rb file. See
214
+ `Rails Routes Integration`_ for more details.
215
+
216
+ api_versions (also api_version)
217
+ What version(s) does the action belong to. (See `Versioning`_ for details.)
218
+
219
+ param
220
+ Look at `Parameter description`_ section for details.
221
+
222
+ returns
223
+ Look at `Response description`_ section for details.
224
+
225
+ tags
226
+ Adds tags for grouping operations together in Swagger outputs. See `swagger`_
227
+ for more details. You can also provide tags in the `Resource Description`_
228
+ block so that they are automatically prepended to all action tags in the
229
+ controller.
230
+
231
+ formats
232
+ Method level request / response formats.
233
+
234
+ error
235
+ Describe each possible error that can happen while calling this
236
+ method. HTTP response code and description can be provided.
237
+
238
+ description
239
+ Full method description, which will be converted into HTML by the
240
+ chosen markup language processor.
241
+
242
+ example
243
+ Provide an example of the server response; whole communication or response type.
244
+ It will be formatted as code.
245
+
246
+ see
247
+ Provide reference to another method, this has to be a string with
248
+ controller_name#method_name.
249
+
250
+ meta
251
+ Hash or array with custom metadata.
252
+
253
+ show
254
+ Resource is hidden from documentation when set to false (true by default)
255
+
256
+ Example:
257
+ ~~~~~~~~
258
+
259
+ .. code:: ruby
260
+
261
+ # The simplest case: just load the paths from routes.rb
262
+ api!
263
+ def index
264
+ end
265
+
266
+ # More complex example
267
+ api :GET, "/users/:id", "Show user profile"
268
+ show false
269
+ error :code => 401, :desc => "Unauthorized"
270
+ error :code => 404, :desc => "Not Found", :meta => {:anything => "you can think of"}
271
+ param :session, String, :desc => "user is logged in", :required => true
272
+ param :regexp_param, /^[0-9]* years/, :desc => "regexp param"
273
+ param :array_param, [100, "one", "two", 1, 2], :desc => "array validator"
274
+ param :boolean_param, [true, false], :desc => "array validator with boolean"
275
+ param :proc_param, lambda { |val|
276
+ val == "param value" ? true : "The only good value is 'param value'."
277
+ }, :desc => "proc validator"
278
+ param :param_with_metadata, String, :desc => "", :meta => [:your, :custom, :metadata]
279
+ returns :code => 200, :desc => "a successful response" do
280
+ property :value1, String, :desc => "A string value"
281
+ property :value2, Integer, :desc => "An integer value"
282
+ property :value3, Hash, :desc => "An object" do
283
+ property :enum1, ['v1', 'v2'], :desc => "One of 2 possible string values"
284
+ end
285
+ end
286
+ tags %w[profiles logins]
287
+ tags 'more', 'related', 'resources'
288
+ description "method description"
289
+ formats ['json', 'jsonp', 'xml']
290
+ meta :message => "Some very important info"
291
+ example " 'user': {...} "
292
+ see "users#showme", "link description"
293
+ see :link => "users#update", :desc => "another link description"
294
+ def show
295
+ #...
296
+ end
297
+
298
+ Parameter Description
299
+ ---------------------
300
+
301
+ Use ``param`` to describe every possible parameter. You can use the Hash validator
302
+ in conjunction with a block given to the param method to describe nested parameters.
303
+
304
+ name
305
+ The first argument is the parameter name as a symbol.
306
+
307
+ validator
308
+ Second parameter is the parameter validator, choose one from section `Validators`_
309
+
310
+ desc
311
+ Parameter description.
312
+
313
+ required
314
+ Set this true/false to make it required/optional. Default is optional
315
+
316
+ allow_nil
317
+ Setting this to true means that ``nil`` can be passed.
318
+
319
+ allow_blank
320
+ Like ``allow_nil``, but for blank values. ``false``, ``""``, ``' '``, ``nil``, ``[]``, and ``{}`` are all blank.
321
+
322
+ as
323
+ Used by the processing functionality to change the name of a key params.
324
+
325
+ meta
326
+ Hash or array with custom metadata.
327
+
328
+ show
329
+ Parameter is hidden from documentation when set to false (true by default)
330
+
331
+ missing_message
332
+ Specify the message to be returned if the parameter is missing as a string or Proc.
333
+ Defaults to ``Missing parameter #{name}`` if not specified.
334
+
335
+ only_in
336
+ This can be set to ``:request`` or ``:response``.
337
+ Setting to ``:response`` causes the param to be ignored when used as part of a request description.
338
+ Setting to ``:request`` causes this param to be ignored when used as part of a response description.
339
+ If ``only_in`` is not specified, the param definition is used for both requests and responses.
340
+ (Note that the keyword ``property`` is similar to ``param``, but it has a ``:only_in => :response`` default).
341
+
342
+ Example:
343
+ ~~~~~~~~
344
+
345
+ .. code:: ruby
346
+
347
+ param :user, Hash, :desc => "User info" do
348
+ param :username, String, :desc => "Username for login", :required => true
349
+ param :password, String, :desc => "Password for login", :required => true
350
+ param :membership, ["standard","premium"], :desc => "User membership"
351
+ param :admin_override, String, :desc => "Not shown in documentation", :show => false
352
+ param :ip_address, String, :desc => "IP address", :required => true, :missing_message => lambda { I18n.t("ip_address.required") }
353
+ end
354
+ def create
355
+ #...
356
+ end
357
+
358
+ DRY with param_group
359
+ --------------------
360
+
361
+ Often, params occur together in more actions. Typically, most of the
362
+ params for ``create`` and ``update`` actions are shared between them.
363
+
364
+ These params can be extracted with ``def_param_group`` and
365
+ ``param_group`` keywords.
366
+
367
+ The definition is looked up in the scope of the controller. If the
368
+ group is defined in a different controller, it might be referenced by
369
+ specifying the second argument.
370
+
371
+ Example:
372
+ ~~~~~~~~
373
+
374
+ .. code:: ruby
375
+
376
+ # v1/users_controller.rb
377
+ def_param_group :address do
378
+ param :street, String
379
+ param :number, Integer
380
+ param :zip, String
381
+ end
382
+
383
+ def_param_group :user do
384
+ param :user, Hash do
385
+ param :name, String, "Name of the user"
386
+ param_group :address
387
+ end
388
+ end
389
+
390
+ api :POST, "/users", "Create an user"
391
+ param_group :user
392
+ def create
393
+ # ...
394
+ end
395
+
396
+ api :PUT, "/users/:id", "Update an user"
397
+ param_group :user
398
+ def update
399
+ # ...
400
+ end
401
+
402
+ # v2/users_controller.rb
403
+ api :POST, "/users", "Create an user"
404
+ param_group :user, V1::UsersController
405
+ def create
406
+ # ...
407
+ end
408
+
409
+ Action Aware params
410
+ -------------------
411
+
412
+ In CRUD operations, this pattern occurs quite often - params that need
413
+ to be set are:
414
+
415
+ * for create action: ``required => true`` and ``allow_nil => false``
416
+ * for update action: ``required => false`` and ``allow_nil => false``
417
+
418
+ This makes it hard to share the param definitions across theses
419
+ actions. Therefore, you can make the description a bit smarter by
420
+ setting ``:action_aware => true``.
421
+
422
+ You can specify explicitly how the param group should be evaluated
423
+ with ``:as`` option (either :create or :update)
424
+
425
+ Example
426
+ ~~~~~~~
427
+
428
+ .. code:: ruby
429
+
430
+ def_param_group :user do
431
+ param :user, Hash, :action_aware => true do
432
+ param :name, String, :required => true
433
+ param :description, String
434
+ end
435
+ end
436
+
437
+ api :POST, "/users", "Create an user"
438
+ param_group :user
439
+ def create
440
+ # ...
441
+ end
442
+
443
+ api :PUT, "/users/admin", "Create an admin"
444
+ param_group :user, :as => :create
445
+ def create_admin
446
+ # ...
447
+ end
448
+
449
+ api :PUT, "/users/:id", "Update an user"
450
+ param_group :user
451
+ def update
452
+ # ...
453
+ end
454
+
455
+ In this case, ``user[name]`` will be not be allowed nil for all
456
+ actions and required only for ``create`` and ``create_admin``. Params
457
+ with ``allow_nil`` set explicitly don't have this value changed.
458
+
459
+ Action awareness is inherited from ancestors (in terms of
460
+ nested params).
461
+
462
+
463
+ Response Description
464
+ --------------------
465
+
466
+ The response from an API call can be documented by adding a ``returns`` statement to the method
467
+ description. This is especially useful when using Apipie to auto-generate a machine-readable Swagger
468
+ definition of your API (see the `swagger`_ section for more details).
469
+
470
+ A ``returns`` statement has several possible formats:
471
+
472
+ .. code:: ruby
473
+
474
+ # format #1: reference to a param-group
475
+ returns <param-group-name> [, :code => <number>|<http-response-code-symbol>] [, :desc => <human-readable description>]
476
+
477
+ # format #2: inline response definition
478
+ returns :code => <number>|<http-response-code-symbol> [, :desc => <human-readable description>] do
479
+ # property ...
480
+ # property ...
481
+ # param_group ...
482
+ end
483
+
484
+ # format #3: describing an array-of-objects response
485
+ returns :array_of => <param-group-name> [, :code => <number>|<http-response-code-symbol>] [, :desc => <human-readable description>]
486
+
487
+
488
+ If the ``:code`` argument is ommitted, ``200`` is used.
489
+
490
+
491
+ Example
492
+ ~~~~~~~
493
+
494
+ .. code:: ruby
495
+
496
+ # ------------------------------------------------
497
+ # Example of format #1 (reference to param-group):
498
+ # ------------------------------------------------
499
+ # the param_group :pet is defined here to describe the output returned by the method below.
500
+ def_param_group :pet do
501
+ property :pet_name, String, :desc => "Name of pet"
502
+ property :animal_type, ['dog','cat','iguana','kangaroo'], :desc => "Type of pet"
503
+ end
504
+
505
+ api :GET, "/pets/:id", "Get a pet record"
506
+ returns :pet, :desc => "The pet"
507
+ def show_detailed
508
+ render JSON({:pet_name => "Skippie", :animal_type => "kangaroo"})
509
+ end
510
+
511
+ # ------------------------------------------------
512
+ # Example of format #2 (inline):
513
+ # ------------------------------------------------
514
+ api :GET, "/pets/:id/with-extra-details", "Get a detailed pet record"
515
+ returns :code => 200, :desc => "Detailed info about the pet" do
516
+ param_group :pet
517
+ property :num_legs, Integer, :desc => "How many legs the pet has"
518
+ end
519
+ def show
520
+ render JSON({:pet_name => "Barkie", :animal_type => "iguana", :legs => 4})
521
+ end
522
+
523
+ # ------------------------------------------------
524
+ # Example of format #3 (array response):
525
+ # ------------------------------------------------
526
+ api :GET, "/pets", "Get all pet records"
527
+ returns :array_of => :pet, :code => 200, :desc => "All pets"
528
+ def index
529
+ render JSON([ {:pet_name => "Skippie", :animal_type => "kangaroo"},
530
+ {:pet_name => "Woofie", :animal_type => "cat"} ])
531
+ end
532
+
533
+
534
+ Note the use of the ``property`` keyword rather than ``param``. This is the
535
+ preferred mechanism for documenting response-only fields.
536
+
537
+
538
+ The Property keyword
539
+ ::::::::::::::::::::::::::::::::::::::::::::::::
540
+
541
+ ``property`` is very similar to ``param`` with the following differences:
542
+
543
+ * a ``property`` is ``:only_in => :response`` by default
544
+
545
+ * a ``property`` is ``:required => :true`` by default
546
+
547
+ * a ``property`` can be an ``:array_of`` objects
548
+
549
+ Example
550
+ _______
551
+ .. code:: ruby
552
+
553
+ property :example, :array_of => Hash do
554
+ property :number1, Integer
555
+ property :number2, Integer
556
+ end
557
+
558
+
559
+ Describing multiple return codes
560
+ ::::::::::::::::::::::::::::::::::::::::::::::::
561
+
562
+ To describe multiple possible return codes, the ``:returns`` keyword can be repeated as many times as necessary
563
+ (once for each return code). Each one of the ``:returns`` entries can specify a different response format.
564
+
565
+ Example
566
+ _______
567
+
568
+ .. code:: ruby
569
+
570
+ api :GET, "/pets/:id/extra_info", "Get extra information about a pet"
571
+ returns :desc => "Found a pet" do
572
+ param_group :pet
573
+ property 'pet_history', Hash do
574
+ param_group :pet_history
575
+ end
576
+ end
577
+ returns :code => :unprocessable_entity, :desc => "Fleas were discovered on the pet" do
578
+ param_group :pet
579
+ property :num_fleas, Integer, :desc => "Number of fleas on this pet"
580
+ end
581
+ def show_extra_info
582
+ # ... implementation here
583
+ end
584
+
585
+
586
+
587
+ Reusing a param_group to describe inputs and outputs
588
+ ::::::::::::::::::::::::::::::::::::::::::::::::::::
589
+
590
+ In many cases (such as CRUD implementations), the output from certain API calls is very similar - but not
591
+ identical - to the inputs of the same or other API calls.
592
+
593
+ If you already have a ``:param_group`` that defines the input to a `create` or `update` routine, it would be quite
594
+ frustrating to have to define a completely separate ``:param_group`` to describe the output of the `show` routine.
595
+
596
+ To address such situations, it is possible to define a single ``:param_group`` which combines ``param`` and ``property``
597
+ statements (as well as ``:only_in => :request`` / ``:only_in => :response``) to differentiate between fields that are
598
+ only expected in the request, only included in the response, or common to both.
599
+
600
+ This is somewhat analogous to the way `Action Aware params`_ work.
601
+
602
+ Example
603
+ _______
604
+
605
+ .. code:: ruby
606
+
607
+ def_param_group :user_record
608
+ param :name, String # this is commong to both the request and the response
609
+ param :force_update, [true, false], :only_in => :request # this does not show up in responses
610
+ property :last_login, String # this shows up only in the response
611
+ end
612
+
613
+ api :POST, "/users", "Create a user"
614
+ param_group :user_record # the :last_login field is not expected here, but :force_update is
615
+ def create
616
+ # ...
617
+ end
618
+
619
+ api :GET, "/users", "Create a user"
620
+ returns :array_of => :user_record # the :last_login field will be included in the response, but :force_update will not
621
+ def index
622
+ # ...
623
+ end
624
+
625
+
626
+ Embedded response descriptions
627
+ ::::::::::::::::::::::::::::::
628
+
629
+ If the code creating JSON responses is encapsulated within dedicated classes, it can be more convenient to
630
+ place the response descriptions outside of the controller and embed them within the response generator.
631
+
632
+ To support such use cases, Apipie allows any class to provide a `describe_own_properties` class method which
633
+ returns a description of the properties such a class would expose. It is then possible to specify that
634
+ class in the `returns` statement instead of a `param_group`.
635
+
636
+ The `describe_own_properties` method is expected to return an array of `Apipie::prop` objects, each one
637
+ describing a single property.
638
+
639
+ Example
640
+ _______
641
+
642
+ .. code:: ruby
643
+
644
+ class Pet
645
+ # this method is automatically called by Apipie when Pet is specified as the returned object type
646
+ def self.describe_own_properties
647
+ [
648
+ Apipie::prop(:pet_name, 'string', {:description => 'Name of pet', :required => false}),
649
+ Apipie::prop(:animal_type, 'string', {:description => 'Type of pet', :values => ["dog", "cat", "iguana", "kangaroo"]}),
650
+ Apipie::additional_properties(false) # this indicates that :pet_name and :animal_type are the only properties in the response
651
+ ]
652
+ end
653
+
654
+ # this method w
655
+ def json
656
+ JSON({:pet_name => @name, :animal_type => @type })
657
+ end
658
+ end
659
+
660
+
661
+ class PetsController
662
+ api :GET, "/index", "Get all pets"
663
+ returns :array_of => Pet # Pet is a 'self-describing-class'
664
+ def index
665
+ # ...
666
+ end
667
+ end
668
+
669
+
670
+ A use case where this is very useful is when JSON generation is done using a reflection mechanism or some
671
+ other sort of declarative mechanism.
672
+
673
+
674
+
675
+
676
+ The `Apipie::prop` function expects the following inputs:
677
+
678
+ .. code:: ruby
679
+
680
+ Apipie::prop(<property-name>, <property-type>, <options-hash> [, <array of sub-properties>])
681
+
682
+ # property-name should be a symbol
683
+ #
684
+ # property-type can be any of the following strings:
685
+ # "integer": maps to a swagger "integer" with an "int32" format
686
+ # "long": maps to a swagger "integer" with an "int64" format
687
+ # "number": maps to a swagger "number"(no format specifier)
688
+ # "float": maps to a swagger "number" with a "float" format
689
+ # "double": maps to a swagger "number" with a "double" format
690
+ # "string": maps to a swagger "string" (no format specifier)
691
+ # "byte": maps to a swagger "string" with a "byte" format
692
+ # "binary": maps to a swagger "string" with a "binary" format
693
+ # "boolean": maps to a swagger "boolean" (no format specifier)
694
+ # "date": maps to a swagger "string" with a "date" format
695
+ # "dateTime": maps to a swagger "string" with a "date-time" format
696
+ # "password": maps to a swagger "string" with a "password" format
697
+ # "object": the property has sub-properties. include <array of sub-properties> in the call.
698
+ # (see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more information
699
+ # about the mapped swagger types)
700
+ #
701
+ # options-hash can include any of the options fields allowed in a :returns statement.
702
+ # additionally, it can include the ':is_array => true', in which case the property is understood to be
703
+ # an array of the described type.
704
+
705
+
706
+
707
+ To describe an embedded object:
708
+
709
+ .. code:: ruby
710
+
711
+
712
+ #
713
+ # PetWithMeasurements is a self-describing class with an embedded object
714
+ #
715
+ class PetWithMeasurements
716
+ def self.describe_own_properties
717
+ [
718
+ Apipie::prop(:pet_name, 'string', {:description => 'Name of pet', :required => false}),
719
+ Apipie::prop('animal_type', 'string', {:description => 'Type of pet', :values => ["dog", "cat", "iguana", "kangaroo"]}),
720
+ Apipie::prop(:pet_measurements, 'object', {}, [
721
+ Apipie::prop(:weight, 'number', {:description => "Weight in pounds" }),
722
+ Apipie::prop(:height, 'number', {:description => "Height in inches" }),
723
+ Apipie::prop(:num_legs, 'number', {:description => "Number of legs", :required => false }),
724
+ Apipie::additional_properties(false)
725
+ ])
726
+ ]
727
+ end
728
+ end
729
+
730
+ #
731
+ # PetWithManyMeasurements is a self-describing class with an embedded array of objects
732
+ #
733
+ class PetWithManyMeasurements
734
+ def self.describe_own_properties
735
+ [
736
+ Apipie::prop(:pet_name, 'string', {:description => 'Name of pet', :required => false}),
737
+ Apipie::prop(:many_pet_measurements, 'object', {is_array: true}, [
738
+ Apipie::prop(:weight, 'number', {:description => "Weight in pounds" }),
739
+ Apipie::prop(:height, 'number', {:description => "Height in inches" }),
740
+ ])
741
+ ]
742
+ end
743
+ end
744
+
745
+
746
+
747
+ Concerns
748
+ --------
749
+
750
+ Sometimes, the actions are not defined in the controller class
751
+ directly but included from a module instead. You can load the Apipie
752
+ DSL into the module by extending it with ``Apipie::DSL::Concern``.
753
+
754
+ The module can be used in more controllers. Therefore there is a way to
755
+ substitute parts of the documentation in the module with controller
756
+ specific values. These substitutions can be stated explicitly with
757
+ ``apipie_concern_subst(:key => "value")`` (needs to be called before
758
+ the module is included to take effect). The substitutions are
759
+ performed in the paths and descriptions of APIs and names and descriptions
760
+ of params.
761
+
762
+ There are some default substitutions available:
763
+
764
+ :controller_path
765
+ value of ``controller.controller_path``, e.g. ``api/users`` for
766
+ ``Api::UsersController``. Only if not using the ``api!`` keyword.
767
+
768
+ :resource_id
769
+ Apipie identifier of the resource, e.g. ``users`` for
770
+ ``Api::UsersController`` or set by ``resource_id``
771
+
772
+ Example
773
+ ~~~~~~~
774
+
775
+ .. code:: ruby
776
+
777
+ # users_module.rb
778
+ module UsersModule
779
+ extend Apipie::DSL::Concern
780
+
781
+ api :GET, '/:controller_path', 'List :resource_id'
782
+ def index
783
+ # ...
784
+ end
785
+
786
+ api! 'Show a :resource'
787
+ def show
788
+ # ...
789
+ end
790
+
791
+ api :POST, '/:resource_id', "Create a :resource"
792
+ param :concern, Hash, :required => true
793
+ param :name, String, 'Name of a :resource'
794
+ param :resource_type, ['standard','vip']
795
+ end
796
+ def create
797
+ # ...
798
+ end
799
+
800
+ api :GET, '/:resource_id/:custom_subst'
801
+ def custom
802
+ # ...
803
+ end
804
+ end
805
+
806
+ # users_controller.rb
807
+ class UsersController < ApplicationController
808
+
809
+ resource_description { resource_id 'customers' }
810
+
811
+ apipie_concern_subst(:custom_subst => 'custom', :resource => 'customer')
812
+ include UsersModule
813
+
814
+ # the following paths are documented
815
+ # api :GET, '/users'
816
+ # api :GET, '/customers/:id', 'Show a customer'
817
+ # api :POST, '/customers', 'Create a customer'
818
+ # param :customer, :required => true do
819
+ # param :name, String, 'Name of a customer'
820
+ # param :customer_type, ['standard', 'vip']
821
+ # end
822
+ # api :GET, '/customers/:custom'
823
+ end
824
+
825
+
826
+ Sometimes, it's needed to extend an existing controller method with additional
827
+ parameters (usually when extending exiting API from plugins/rails engines).
828
+ The concern can be also used for this purposed, using `update_api` method.
829
+ The params defined in this block are merged with the params of the original method
830
+ in the controller this concern is included to.
831
+
832
+ Example
833
+ ~~~~~~~
834
+
835
+ .. code:: ruby
836
+
837
+ module Concerns
838
+ module OauthConcern
839
+ extend Apipie::DSL::Concern
840
+
841
+ update_api(:create, :update) do
842
+ param :user, Hash do
843
+ param :oauth, String, :desc => 'oauth param'
844
+ end
845
+ end
846
+ end
847
+ end
848
+
849
+ The concern needs to be included to the controller after the methods are defined
850
+ (either at the end of the class, or by using
851
+ ``Controller.send(:include, Concerns::OauthConcern)``.
852
+
853
+
854
+ Response validation
855
+ -------------------
856
+
857
+ The swagger definitions created by Apipie can be used to auto-generate clients that access the
858
+ described APIs. Those clients will break if the responses returned from the API do not match
859
+ the declarations. As such, it is very important to include unit tests that validate the actual
860
+ responses against the swagger definitions.
861
+
862
+ The implemented mechanism provides two ways to include such validations in RSpec unit tests:
863
+ manual (using an RSpec matcher) and automated (by injecting a test into the http operations 'get', 'post',
864
+ raising an error if there is no match).
865
+
866
+ Example of the manual mechanism:
867
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
868
+
869
+ .. code:: ruby
870
+
871
+ require 'apipie/rspec/response_validation_helper'
872
+
873
+ RSpec.describe MyController, :type => :controller, :show_in_doc => true do
874
+
875
+ describe "GET stuff with response validation" do
876
+ render_views # this makes sure the 'get' operation will actually
877
+ # return the rendered view even though this is a Controller spec
878
+
879
+ it "does something" do
880
+ response = get :index, {format: :json}
881
+
882
+ # the following expectation will fail if the returned object
883
+ # does not match the 'returns' declaration in the Controller,
884
+ # or if there is no 'returns' declaration for the returned
885
+ # HTTP status code
886
+ expect(response).to match_declared_responses
887
+ end
888
+ end
889
+ end
890
+
891
+
892
+ Example of the automated mechanism:
893
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
894
+
895
+ .. code:: ruby
896
+
897
+ require 'apipie/rspec/response_validation_helper'
898
+
899
+ RSpec.describe MyController, :type => :controller, :show_in_doc => true do
900
+
901
+ describe "GET stuff with response validation" do
902
+ render_views
903
+ auto_validate_rendered_views
904
+
905
+ it "does something" do
906
+ get :index, {format: :json}
907
+ end
908
+ it "does something else" do
909
+ get :another_index, {format: :json}
910
+ end
911
+ end
912
+
913
+ describe "GET stuff without response validation" do
914
+ it "does something" do
915
+ get :index, {format: :json}
916
+ end
917
+ it "does something else" do
918
+ get :another_index, {format: :json}
919
+ end
920
+ end
921
+ end
922
+
923
+
924
+ =========================
925
+ Configuration Reference
926
+ =========================
927
+
928
+ Create a configuration file in e.g. ``/config/initializers/apipie.rb``.
929
+ You can set the application name, footer text, API and documentation base URL
930
+ and turn off validations. You can also choose your favorite markup language
931
+ for full descriptions.
932
+
933
+ app_name
934
+ Name of your application; used in breadcrumbs navigation.
935
+
936
+ copyright
937
+ Copyright information (shown in page footer).
938
+
939
+ compress_examples
940
+ If ``true`` recorded examples are compressed using ``Zlib``. Useful for big test-suits.
941
+
942
+ doc_base_url
943
+ Documentation frontend base url.
944
+
945
+ api_base_url
946
+ Base url for default version of your API. To set it for specific version use ``config.api_base_url[version] = url``.
947
+
948
+ default_version
949
+ Default API version to be used (1.0 by default)
950
+
951
+ validate
952
+ Parameters validation is turned off when set to false. When set to
953
+ ``:explicitly``, you must invoke parameter validation yourself by calling
954
+ controller method ``apipie_validations`` (typically in a before_filter).
955
+ When set to ``:implicitly`` (or just true), your controller's action
956
+ methods are wrapped with generated methods which call ``apipie_validations``,
957
+ and then call the action method. (``:implicitly`` by default)
958
+
959
+ validate_value
960
+ Check the value of params against specified validators (true by
961
+ default)
962
+
963
+ validate_presence
964
+ Check the params presence against the documentation.
965
+
966
+ validate_key
967
+ Check the received params to ensure they are defined in the API. (false by default)
968
+
969
+ process_params
970
+ Process and extract the parameter defined from the params of the request
971
+ to the api_params variable
972
+
973
+ app_info
974
+ Application long description.
975
+
976
+ reload_controllers
977
+ Set to enable/disable reloading controllers (and the documentation with it). Enabled by default in development.
978
+
979
+ api_controllers_matcher
980
+ For reloading to work properly you need to specify where your API controllers are. Can be an array if multiple paths are needed
981
+
982
+ api_routes
983
+ Set if your application uses a custom API router, different from the Rails
984
+ default
985
+
986
+ routes_formatter
987
+ An object providing the translation from the Rails routes to the
988
+ format usable in the documentation when using the `api!` keyword. By
989
+ default, the ``Apipie::RoutesFormatter`` is used.
990
+
991
+ markup
992
+ You can choose markup language for descriptions of your application,
993
+ resources and methods. RDoc is the default but you can choose from
994
+ Apipie::Markup::Markdown.new or Apipie::Markup::Textile.new.
995
+ In order to use Markdown you need Maruku gem and for Textile you
996
+ need RedCloth. Add those to your gemfile and run bundle if you
997
+ want to use them. You can also add any other markup language
998
+ processor.
999
+
1000
+ layout
1001
+ Name of a layout template to use instead of Apipie's layout. You can use
1002
+ Apipie.include_stylesheets and Apipie.include_javascripts helpers to include
1003
+ Apipie's stylesheets and javascripts.
1004
+
1005
+ ignored
1006
+ An array of controller names (strings) (might include actions as well)
1007
+ to be ignored when generationg the documentation
1008
+ e.g. ``%w[Api::CommentsController Api::PostsController#post]``
1009
+
1010
+ namespaced_resources
1011
+ Use controller paths instead of controller names as resource id.
1012
+ This prevents same named controllers overwriting each other.
1013
+
1014
+ authenticate
1015
+ Pass a proc in order to authenticate user. Pass nil for
1016
+ no authentication (by default).
1017
+
1018
+ authorize
1019
+ Pass a proc in order to authorize controllers and methods. The Proc is evaluated in the controller context.
1020
+
1021
+ show_all_examples
1022
+ Set this to true to set show_in_doc=1 in all recorded examples
1023
+
1024
+ link_extension
1025
+ The extension to use for API pages ('.html' by default). Link extensions
1026
+ in static API docs cannot be changed from '.html'.
1027
+
1028
+ languages
1029
+ List of languages the API documentation should be translated into. Empty by default.
1030
+
1031
+ default_locale
1032
+ Locale used for generating documentation when no specific locale is set.
1033
+ Set to 'en' by default.
1034
+
1035
+ locale
1036
+ Pass locale setter/getter
1037
+
1038
+ .. code:: ruby
1039
+
1040
+ config.locale = lambda { |loc| loc ? FastGettext.set_locale(loc) : FastGettext.locale }
1041
+
1042
+ translate
1043
+ Pass proc to translate strings using the localization library your project uses.
1044
+ For example see `Localization`_
1045
+
1046
+ Example:
1047
+
1048
+ .. code:: ruby
1049
+
1050
+ Apipie.configure do |config|
1051
+ config.app_name = "Test app"
1052
+ config.copyright = "&copy; 2012 Pavel Pokorny"
1053
+ config.doc_base_url = "/apidoc"
1054
+ config.api_base_url = "/api"
1055
+ config.validate = false
1056
+ config.markup = Apipie::Markup::Markdown.new
1057
+ config.reload_controllers = Rails.env.development?
1058
+ config.api_controllers_matcher = File.join(Rails.root, "app", "controllers", "**","*.rb")
1059
+ config.api_routes = Rails.application.routes
1060
+ config.app_info["1.0"] = "
1061
+ This is where you can inform user about your application and API
1062
+ in general.
1063
+ "
1064
+ config.authenticate = Proc.new do
1065
+ authenticate_or_request_with_http_basic do |username, password|
1066
+ username == "test" && password == "supersecretpassword"
1067
+ end
1068
+ end
1069
+ config.authorize = Proc.new do |controller, method, doc|
1070
+ !method # show all controller doc, but no method docs.
1071
+ end
1072
+ end
1073
+
1074
+ checksum_path
1075
+ Used in ChecksumInHeaders middleware (see `JSON checksums`_ for more info). It contains path prefix(es) where the header with checksum is added. If set to nil, checksum is added in headers in every response. e.g. ``%w[/api /apipie]``
1076
+
1077
+ update_checksum
1078
+ If set to true, the checksum is recalculated with every documentation_reload call
1079
+
1080
+ ========================
1081
+ Rails Routes Integration
1082
+ ========================
1083
+
1084
+ Apipie is able to load the information about the paths based on the
1085
+ routes defined in the Rails application, by using the `api!` keyword
1086
+ in the DSL.
1087
+
1088
+ It should be usable out of box, however, one might want
1089
+ to do some customization (such as omitting some implicit parameters in
1090
+ the path etc.). For this kind of customizations one can create a new
1091
+ formatter and pass as the ``Apipie.configuration.routes_formatter``
1092
+ option, like this:
1093
+
1094
+ .. code:: ruby
1095
+
1096
+ class MyFormatter < Apipie::RoutesFormatter
1097
+ def format_path(route)
1098
+ super.gsub(/\(.*?\)/, '').gsub('//','') # hide all implicit parameters
1099
+ end
1100
+ end
1101
+
1102
+ Apipie.configure do |config|
1103
+ ...
1104
+ config.routes_formatter = MyFormatter.new
1105
+ ...
1106
+ end
1107
+
1108
+ A similar way can be used to influence things like order, or a description
1109
+ of the loaded APIs, even omitting some paths if needed.
1110
+
1111
+ ============
1112
+ Processing
1113
+ ============
1114
+
1115
+ The goal is to extract and pre-process parameters of the request.
1116
+
1117
+ For example Rails, by default, transforms an empty array to nil value. Perhaps
1118
+ you want to transform it again into an empty array. Or you
1119
+ want to support an enumeration type (comma separated values) and
1120
+ you want to automatically transform this string into an array.
1121
+
1122
+ To use it, set the ``process_params`` configuration variable to true.
1123
+
1124
+ Also by using ``as`` you can separate your API parameter
1125
+ names from the names you are using inside your code.
1126
+
1127
+ To implement it, you just have to write a process_value
1128
+ function in your validator:
1129
+
1130
+ For an enumeration type:
1131
+
1132
+ .. code:: ruby
1133
+
1134
+ def process_value(value)
1135
+ value ? value.split(',') : []
1136
+ end
1137
+
1138
+ ============
1139
+ Validators
1140
+ ============
1141
+
1142
+ Every parameter needs to have an associated validator. For now there are some
1143
+ basic validators. You can always provide your own to achieve complex
1144
+ results.
1145
+
1146
+ If validations are enabled (default state) the parameters of every
1147
+ request are validated. If the value is wrong an +ArgumentError+ exception
1148
+ is raised and can be rescued and processed. It contains a description
1149
+ of the parameter value expectations. Validations can be turned off
1150
+ in the configuration file.
1151
+
1152
+ Parameter validation normally happens after before_filters, just before
1153
+ your controller method is invoked. If you prefer to control when parameter
1154
+ validation occurs, set the configuration parameter ``validate`` to ``:explicitly``.
1155
+ You must then call the ``apipie_validations`` method yourself, e.g.:
1156
+
1157
+ .. code:: ruby
1158
+
1159
+ before_filter :apipie_validations
1160
+
1161
+ This is useful if you have before_filters which use parameter values: just add them
1162
+ after the ``apipie_validations`` before_filter.
1163
+
1164
+ TypeValidator
1165
+ -------------
1166
+ Check the parameter type. Only String, Hash and Array are supported
1167
+ for the sake of simplicity. Read more to find out how to add
1168
+ your own validator.
1169
+
1170
+ .. code:: ruby
1171
+
1172
+ param :session, String, :desc => "user is logged in", :required => true
1173
+ param :facts, Hash, :desc => "Additional optional facts about the user"
1174
+
1175
+
1176
+ RegexpValidator
1177
+ ---------------
1178
+ Check parameter value against given regular expression.
1179
+
1180
+ .. code:: ruby
1181
+
1182
+ param :regexp_param, /^[0-9]* years/, :desc => "regexp param"
1183
+
1184
+
1185
+ EnumValidator
1186
+ --------------
1187
+
1188
+ Check if parameter value is included in the given array.
1189
+
1190
+ .. code:: ruby
1191
+
1192
+ param :enum_param, [100, "one", "two", 1, 2], :desc => "enum validator"
1193
+
1194
+
1195
+ ProcValidator
1196
+ -------------
1197
+
1198
+ If you need more complex validation and you know you won't reuse it, you
1199
+ can use the Proc/lambda validator. Provide your own Proc, taking the value
1200
+ of the parameter as the only argument. Return true if value passes validation
1201
+ or return some text about what is wrong otherwise. _Don't use the keyword *return*
1202
+ if you provide an instance of Proc (with lambda it is ok), just use the last
1203
+ statement return property of ruby.
1204
+
1205
+ .. code:: ruby
1206
+
1207
+ param :proc_param, lambda { |val|
1208
+ val == "param value" ? true : "The only good value is 'param value'."
1209
+ }, :desc => "proc validator"
1210
+
1211
+
1212
+ HashValidator
1213
+ -------------
1214
+
1215
+ You can describe hash parameters in depth if you provide a block with a
1216
+ description of nested values.
1217
+
1218
+ .. code:: ruby
1219
+
1220
+ param :user, Hash, :desc => "User info" do
1221
+ param :username, String, :desc => "Username for login", :required => true
1222
+ param :password, String, :desc => "Password for login", :required => true
1223
+ param :membership, ["standard","premium"], :desc => "User membership"
1224
+ end
1225
+
1226
+
1227
+ NilValidator
1228
+ ------------
1229
+
1230
+ In fact there isn't any NilValidator, but setting it to nil can be used to
1231
+ override parameters described on the resource level.
1232
+
1233
+ .. code:: ruby
1234
+
1235
+ param :user, nil
1236
+ def destroy
1237
+ #...
1238
+ end
1239
+
1240
+ NumberValidator
1241
+ ---------------
1242
+
1243
+ Check if the parameter is a positive integer number or zero
1244
+
1245
+ .. code:: ruby
1246
+
1247
+ param :product_id, :number, :desc => "Identifier of the product", :required => true
1248
+ param :quantity, :number, :desc => "Number of products to order", :required => true
1249
+
1250
+ DecimalValidator
1251
+ --------------
1252
+
1253
+ Check if the parameter is a decimal number
1254
+
1255
+ .. code:: ruby
1256
+
1257
+ param :latitude, :decimal, :desc => "Geographic latitude", :required => true
1258
+ param :longitude, :decimal, :desc => "Geographic longitude", :required => true
1259
+
1260
+ ArrayValidator
1261
+ --------------
1262
+
1263
+ Check if the parameter is an array
1264
+
1265
+ Additional options
1266
+ ~~~~~~~~~~~~~~~~~
1267
+
1268
+ of
1269
+ Specify the type of items. If not given it accepts an array of any item type
1270
+
1271
+ in
1272
+ Specify an array of valid item values.
1273
+
1274
+ Examples
1275
+ ~~~~~~~~
1276
+
1277
+ Assert `things` is an array of any items
1278
+
1279
+ .. code:: ruby
1280
+
1281
+ param :things, Array
1282
+
1283
+ Assert `hits` must be an array of integer values
1284
+
1285
+ .. code:: ruby
1286
+
1287
+ param :hits, Array, of: Integer
1288
+
1289
+ Assert `colors` must be an array of valid string values
1290
+
1291
+ .. code:: ruby
1292
+
1293
+ param :colors, Array, in: ["red", "green", "blue"]
1294
+
1295
+
1296
+ The retrieving of valid items can be deferred until needed using a lambda. It is evaluated only once
1297
+
1298
+ .. code:: ruby
1299
+
1300
+ param :colors, Array, in: -> { Color.all.pluck(:name) }
1301
+
1302
+
1303
+ NestedValidator
1304
+ -------------
1305
+
1306
+ You can describe nested parameters in depth if you provide a block with a
1307
+ description of nested values.
1308
+
1309
+ .. code:: ruby
1310
+
1311
+ param :comments, Array, :desc => "User comments" do
1312
+ param :name, String, :desc => "Name of the comment", :required => true
1313
+ param :comment, String, :desc => "Full comment", :required => true
1314
+ end
1315
+
1316
+
1317
+
1318
+ Adding custom validator
1319
+ -----------------------
1320
+
1321
+ Only basic validators are included but it is really easy to add your own.
1322
+ Create a new initializer with a subclass of Apipie::Validator::BaseValidator.
1323
+ Two methods are required to implement this - instance method
1324
+ :code:`validate(value)` and class method
1325
+ :code:`build(param_description, argument, options, block)`.
1326
+
1327
+ When searching for the validator +build+ method, every subclass of
1328
+ Apipie::Validator::BaseValidator is called. The first one that returns the
1329
+ constructed validator object is used.
1330
+
1331
+ Example: Adding IntegerValidator
1332
+
1333
+ We want to check if the parameter value is an integer like this:
1334
+
1335
+ .. code:: ruby
1336
+
1337
+ param :id, Integer, :desc => "Company ID"
1338
+
1339
+ So we create apipie_validators.rb initializer with this content:
1340
+
1341
+ .. code:: ruby
1342
+
1343
+ class IntegerValidator < Apipie::Validator::BaseValidator
1344
+
1345
+ def initialize(param_description, argument)
1346
+ super(param_description)
1347
+ @type = argument
1348
+ end
1349
+
1350
+ def validate(value)
1351
+ return false if value.nil?
1352
+ !!(value.to_s =~ /^[-+]?[0-9]+$/)
1353
+ end
1354
+
1355
+ def self.build(param_description, argument, options, block)
1356
+ if argument == Integer || argument == Fixnum
1357
+ self.new(param_description, argument)
1358
+ end
1359
+ end
1360
+
1361
+ def description
1362
+ "Must be #{@type}."
1363
+ end
1364
+ end
1365
+
1366
+ Parameters of the build method:
1367
+
1368
+ param_description
1369
+ Instance of Apipie::ParamDescription contains all
1370
+ given information about the validated parameter.
1371
+
1372
+ argument
1373
+ Specified validator; in our example it is +Integer+
1374
+
1375
+ options
1376
+ Hash with specified options, for us just ``{:desc => "Company ID"}``
1377
+
1378
+ block
1379
+ Block converted into Proc, use it as you desire. In this example nil.
1380
+
1381
+
1382
+ ============
1383
+ Versioning
1384
+ ============
1385
+
1386
+ Every resource/method can belong to one or more versions. The version is
1387
+ specified with the `api_version` DSL keyword. When not specified,
1388
+ the resource belongs to `config.default_version` ("1.0" by default)
1389
+
1390
+ .. code:: ruby
1391
+
1392
+ resource_description do
1393
+ api_versions "1", "2"
1394
+ end
1395
+
1396
+ api :GET, "/api/users/", "List: users"
1397
+ api_version "1"
1398
+ def index
1399
+ # ...
1400
+ end
1401
+
1402
+ api :GET, "/api/users/", "List: users", :deprecated => true
1403
+
1404
+ In the example above we say the whole controller/resource is defined
1405
+ for versions "1" and "2", but we override this by explicitly saying
1406
+ `index` belongs only to version "1". Also, inheritance works (therefore
1407
+ we can specify the api_version for the parent controller, and all
1408
+ children will know about that). Routes can be flagged as deprecated,
1409
+ and an annotation will be added to them when viewing in the API
1410
+ documentation.
1411
+
1412
+ From the Apipie API perspective, the resources belong to the version.
1413
+ With versioning, there are paths like this provided by apipie:
1414
+
1415
+ .. code::
1416
+
1417
+ /apipie/1/users/index
1418
+ /apipie/2/users/index
1419
+
1420
+ When not specifying the version explicitly in the path (or in DSL),
1421
+ default version (`Apipie.configuration.default_version`) is used
1422
+ instead ("1.0" by default). Therefore, an application that doesn't
1423
+ need versioning should work as before.
1424
+
1425
+ The static page generator takes a version parameter (or uses default).
1426
+
1427
+ You can specify the versions for the examples, with the `versions`
1428
+ keyword. It specifies the versions the example is used for. When not
1429
+ specified, it's shown in all versions with the given method.
1430
+
1431
+ When referencing or quering the resource/method descripion, this
1432
+ format should be used: "version#resource#method". When not specified,
1433
+ the default version is used instead.
1434
+
1435
+
1436
+ ========
1437
+ Markup
1438
+ ========
1439
+
1440
+ The default markup language is `RDoc
1441
+ <https://rdoc.github.io/rdoc/RDoc/Markup.html>`_. It can be changed in
1442
+ the config file (``config.markup=``) to one of these:
1443
+
1444
+ Markdown
1445
+ Use Apipie::Markup::Markdown.new. You need Maruku gem.
1446
+
1447
+ Textile
1448
+ Use Apipie::Markup::Textile.new. You need RedCloth gem.
1449
+
1450
+ Or provide you own object with a ``to_html(text)`` method.
1451
+ For inspiration, this is how Textile markup usage is implemented:
1452
+
1453
+ .. code:: ruby
1454
+
1455
+ class Textile
1456
+ def initialize
1457
+ require 'RedCloth'
1458
+ end
1459
+ def to_html(text)
1460
+ RedCloth.new(text).to_html
1461
+ end
1462
+ end
1463
+
1464
+ ============
1465
+ Localization
1466
+ ============
1467
+
1468
+ Apipie has support for localized API documentation in both formats (JSON and HTML).
1469
+ Apipie uses the library I18n for localization of itself.
1470
+ Check ``config/locales`` directory for available translations.
1471
+
1472
+ A major part of strings in the documentation comes from the API.
1473
+ As preferences regarding localization libraries differ amongst project, Apipie needs to know how to set the locale for your project,
1474
+ and how to translate a string using the library your project uses. That can be done using lambdas in configuration.
1475
+
1476
+ Sample configuration when your project uses FastGettext
1477
+
1478
+
1479
+ .. code:: ruby
1480
+
1481
+ Apipie.configure do |config|
1482
+ ...
1483
+ config.languages = ['en', 'cs']
1484
+ config.default_locale = 'en'
1485
+ config.locale = lambda { |loc| loc ? FastGettext.set_locale(loc) : FastGettext.locale }
1486
+ config.translate = lambda do |str, loc|
1487
+ old_loc = FastGettext.locale
1488
+ FastGettext.set_locale(loc)
1489
+ trans = _(str)
1490
+ FastGettext.set_locale(old_loc)
1491
+ trans
1492
+ end
1493
+ end
1494
+
1495
+ And the strings in the API documentation need to be marked with the ``N_()`` function
1496
+
1497
+ .. code:: ruby
1498
+
1499
+ api :GET, "/users/:id", N_("Show user profile")
1500
+ param :session, String, :desc => N_("user is logged in"), :required => true
1501
+
1502
+
1503
+
1504
+ When your project use I18n, localization related configuration could appear as follows
1505
+
1506
+ .. code:: ruby
1507
+
1508
+ Apipie.configure do |config|
1509
+ ...
1510
+ config.languages = ['en', 'cs']
1511
+ config.default_locale = 'en'
1512
+ config.locale = lambda { |loc| loc ? I18n.locale = loc : I18n.locale }
1513
+ config.translate = lambda do |str, loc|
1514
+ return '' if str.blank?
1515
+ I18n.t str, locale: loc, scope: 'doc'
1516
+ end
1517
+ end
1518
+
1519
+ And the strings in the API documentation needs to be in the form of translation keys
1520
+
1521
+ .. code:: ruby
1522
+
1523
+ api :GET, "/users/:id", "show_user_profile"
1524
+ param :session, String, :desc => "user_is_logged_in", :required => true
1525
+
1526
+
1527
+ The localized versions of the documentation are distinguished by language in the filename.
1528
+ E.g. ``doc/apidoc/apidoc.cs.html`` is static documentation in the Czech language.
1529
+ If the language is missing, e.g. ``doc/apidoc/apidoc.html``,
1530
+ the documentation is localized with the ``default_locale``.
1531
+
1532
+ The dynamic documentation follows the same schema. The ``http://localhost:3000/apidoc/v1.cs.html`` is documentation for version '1' of the API in the Czech language. For JSON descriptions, the API applies the same format: ``http://localhost:3000/apidoc/v1.cs.json``
1533
+
1534
+
1535
+ ================
1536
+ Modifying Views
1537
+ ================
1538
+
1539
+ To modify the views of your documentation, run ``rails g apipie:views``.
1540
+ This will copy the Apipie views to ``app/views/apipie/apipies`` and
1541
+ ``app/views/layouts/apipie``.
1542
+
1543
+
1544
+ ==============
1545
+ Static files
1546
+ ==============
1547
+
1548
+ To generate a static version of documentation (perhaps to put it on
1549
+ your project site or something), run the ``rake apipie:static`` task. It will
1550
+ create a set of HTML files (multi-pages, single-page, plain) in your doc
1551
+ directory. If you prefer a JSON version run ``rake apipie:static_json``.
1552
+ By default the documentation for the default API version is
1553
+ used. You can specify the version with ``rake apipie:static[2.0]``
1554
+
1555
+ When you want to avoid any unnecessary computation in production mode,
1556
+ you can generate a cache with ``rake apipie:cache`` and configure the
1557
+ app to use it in production with ``config.use_cache = Rails.env.production?``
1558
+
1559
+ Default cache dir is ``File.join(Rails.root, "public", "apipie-cache")``,
1560
+ you can change it to where you want, example: ``config.cache_dir = File.join(Rails.root, "doc", "apidoc")``.
1561
+
1562
+ If, for some complex cases, you need to generate/re-generate just part of the cache
1563
+ use ``rake apipie:cache cache_part=index`` resp. ``rake apipie:cache cache_part=resources``
1564
+ To generate it for different locations for further processing use ``rake apipie:cache OUT=/tmp/apipie_cache``.
1565
+
1566
+ .. _Swagger:
1567
+
1568
+ ====================================
1569
+ Static Swagger (OpenAPI 2.0) files
1570
+ ====================================
1571
+
1572
+ To generate a static Swagger definition file from the api, run ``rake apipie:static_swagger_json``.
1573
+ By default the documentation for the default API version is
1574
+ used. You can specify the version with ``rake apipie:static_swagger_json[2.0]``. A swagger file will be
1575
+ generated for each locale. The files will be generated in the same location as the static_json files, but
1576
+ instead of being named ``schema_apipie[.locale].json``, they will be called ``schema_swagger[.locale].json``.
1577
+
1578
+ Specifying default values for parameters
1579
+ -----------------------------------------
1580
+ Swagger allows method definitions to include an indication of the the default value for each parameter. To include such
1581
+ indications, use ``:default_value => <some value>`` in the parameter definition DSL. For example:
1582
+
1583
+ .. code:: ruby
1584
+
1585
+ param :do_something, Boolean, :desc => "take an action", :required => false, :default_value => false
1586
+
1587
+
1588
+ Generated Warnings
1589
+ -------------------
1590
+ The help identify potential improvements to your documentation, the swagger generation process issues warnings if
1591
+ it identifies various shortcomings of the DSL documentation. Each warning has a code to allow selective suppression
1592
+ (see swagger-specific configuration below)
1593
+
1594
+ :100: missing short description for method
1595
+ :101: added missing / at beginning of path
1596
+ :102: no return codes specified for method
1597
+ :103: a parameter is a generic Hash without an internal type specification
1598
+ :104: a parameter is an 'in-path' parameter, but specified as 'not required' in the DSL
1599
+ :105: a parameter is optional but does not have a default value specified
1600
+ :106: a parameter was ommitted from the swagger output because it is a Hash without fields in a formData specification
1601
+ :107: a path parameter is not described
1602
+ :108: inferring that a parameter type is boolean because described as an enum with [false,true] values
1603
+
1604
+
1605
+
1606
+ Swagger-Specific Configuration Parameters
1607
+ -------------------------------------------------
1608
+
1609
+ There are several configuration parameters that determine the structure of the generated swagger file:
1610
+
1611
+ ``config.swagger_content_type_input``
1612
+ If the value is ``:form_data`` - the swagger file will indicate that the server consumes the content types
1613
+ ``application/x-www-form-urlencoded`` and ``multipart/form-data``. Non-path parameters will have the
1614
+ value ``"in": "formData"``. Note that parameters of type Hash that do not have any fields in them will *be ommitted*
1615
+ from the resulting files, as there is no way to describe them in swagger.
1616
+
1617
+ If the value is ``:json`` - the swagger file will indicate that the server consumes the content type
1618
+ ``application/json``. All non-path parameters will be included in the schema of a single ``"in": "body"`` parameter
1619
+ of type ``object``.
1620
+
1621
+ You can specify the value of this configuration parameter as an additional input to the rake command (e.g.,
1622
+ ``rake apipie:static_swagger_json[2.0,form_data]``).
1623
+
1624
+ ``config.swagger_json_input_uses_refs``
1625
+ This parameter is only relevant if ``swagger_content_type_input`` is ``:json``.
1626
+
1627
+ If ``true``: the schema of the ``"in": "body"`` parameter of each method is given its own entry in the ``definitions``
1628
+ section, and is referenced using ``$ref`` from the method definition.
1629
+
1630
+ If ``false``: the body parameter definitions are inlined within the method definitions.
1631
+
1632
+ ``config.swagger_include_warning_tags``
1633
+ If ``true``: in addition to tagging methods with the name of the resource they belong to, methods for which warnings
1634
+ have been issued will be tagged with.
1635
+
1636
+ ``config.swagger_suppress_warnings``
1637
+ If ``false``: no warnings will be suppressed
1638
+
1639
+ If ``true``: all warnings will be suppressed
1640
+
1641
+ If an array of values (e.g., ``[100,102,107]``), only the warnings identified by the numbers in the array will be suppressed.
1642
+
1643
+ ``config.swagger_api_host``
1644
+ The value to place in the swagger host field.
1645
+
1646
+ Default is ``localhost:3000``
1647
+
1648
+ If ``nil`` then then host field will not be included.
1649
+
1650
+ ``config.swagger_allow_additional_properties_in_response``
1651
+ If ``false`` (default): response descriptions in the generated swagger will include an ``additional-properties: false``
1652
+ field
1653
+
1654
+ If ``true``: the ``additional-properties: false`` field will not be included in response object descriptions
1655
+
1656
+
1657
+ Known limitations of the current implementation
1658
+ -------------------------------------------------
1659
+ * There is currently no way to document the structure and content-type of the data returned from a method
1660
+ * Recorded examples are currently not included in the generated swagger file
1661
+ * The apipie ``formats`` value is ignored.
1662
+ * It is not possible to specify the "consumed" content type on a per-method basis
1663
+ * It is not possible to leverage all of the parameter type/format capabilities of swagger
1664
+ * Only OpenAPI 2.0 is supported
1665
+ * Responses are defined inline and not as a $ref
1666
+
1667
+ ====================================
1668
+ Dynamic Swagger generation
1669
+ ====================================
1670
+
1671
+ To generate swagger dynamically, use ``http://localhost:3000/apipie.json?type=swagger``.
1672
+
1673
+ Note that authorization is not supported for dynamic swagger generation, so if ``config.authorize`` is defined,
1674
+ dynamic swagger generation will be disabled.
1675
+
1676
+ Dynamically generated swagger is not cached, and is always generated on the fly.
1677
+
1678
+
1679
+ ===================
1680
+ JSON checksums
1681
+ ===================
1682
+
1683
+ If the API client needs to be sure that the JSON didn't changed, add
1684
+ the ``ApipieChecksumInHeaders`` middleware in your rails app.
1685
+ It can add a checksum of the entire JSON document in the response headers.
1686
+
1687
+ .. code::
1688
+
1689
+ "Apipie-Checksum"=>"fb81460e7f4e78d059f826624bdf9504"
1690
+
1691
+ `Apipie bindings <https://github.com/Apipie/apipie-bindings>`_ uses this feature to refresh its JSON cache.
1692
+
1693
+ To set it up add the following to your ``application.rb``
1694
+
1695
+ .. code::
1696
+
1697
+ require 'apipie/middleware/checksum_in_headers'
1698
+ # Add JSON checksum in headers for smarter caching
1699
+ config.middleware.use "Apipie::Middleware::ChecksumInHeaders"
1700
+
1701
+ And in your apipie initializer allow checksum calculation
1702
+
1703
+ .. code::
1704
+
1705
+ Apipie.configuration.update_checksum = true
1706
+
1707
+
1708
+ By default the header is added to responses for ``config.doc_base_url`` and ``/api``.
1709
+ It can be changed in configuration (see `Configuration Reference`_ for details).
1710
+
1711
+ The checksum calculation is lazy, and done with the first request. If you run with ``use_cache = true``,
1712
+ do not forget to run the rake task ``apipie:cache``.
1713
+
1714
+
1715
+ ===================
1716
+ Tests Integration
1717
+ ===================
1718
+
1719
+ Apipie integrates with automated testing in two ways. *Documentation
1720
+ bootstrapping* and *examples recording*.
1721
+
1722
+ Documentation Bootstrapping
1723
+ ---------------------------
1724
+
1725
+ Let's say you have an application without REST API documentation.
1726
+ However you have a set of tests that are run against this API. A lot
1727
+ of information is already included in these tests, it just needs to be
1728
+ extracted somehow. Luckily, Apipie provides such a feature.
1729
+
1730
+ When running the tests, set the ``APIPIE_RECORD=params`` environment
1731
+ variable or call ``Apipie.record('params')`` from specs starter. You can either use it with functional tests:
1732
+
1733
+ .. code::
1734
+
1735
+ APIPIE_RECORD=params rake test:functionals
1736
+
1737
+ or you can run your server with this param, in case you run the tests
1738
+ against running server:
1739
+
1740
+ .. code::
1741
+
1742
+ APIPIE_RECORD=params rails server
1743
+
1744
+ When the process quits, the data from requests/responses are used to
1745
+ determine the documentation. It's quite raw, but it makes the initial
1746
+ phase much easier.
1747
+
1748
+ Examples Recording
1749
+ ------------------
1750
+
1751
+ You can also use the tests to generate up-to-date examples for your
1752
+ code. Similar to the bootstrapping process, you can use it with functional
1753
+ tests or a running server, setting ``APIPIE_RECORD=examples`` or calling ``Apipie.record('examples')`` in your specs starter.
1754
+
1755
+ .. code::
1756
+
1757
+ APIPIE_RECORD=examples rake test:functionals
1758
+ APIPIE_RECORD=examples rails server
1759
+
1760
+ The data is written into ``doc/apipie_examples.yml``. By default,
1761
+ only the first example is shown for each action. You can customize
1762
+ this by setting the ``show_in_doc`` attribute at each example.
1763
+
1764
+ You can add a title to the examples (useful when showing more than
1765
+ one example per method) by adding a 'title' attribute.
1766
+
1767
+ .. code::
1768
+
1769
+ --- !omap
1770
+ - announcements#index:
1771
+ - !omap
1772
+ - title: This is a custom title for this example
1773
+ - verb: :GET
1774
+ - path: /api/blabla/1
1775
+ - versions:
1776
+ - '1.0'
1777
+ - query:
1778
+ - request_data:
1779
+ - response_data:
1780
+ ...
1781
+ - code: 200
1782
+ - show_in_doc: 1 # If 1, show. If 0, do not show.
1783
+ - recorded: true
1784
+
1785
+ In RSpec you can add metadata to examples. We can use that feature
1786
+ to mark selected examples - the ones that perform the requests that we want to
1787
+ show as examples in the documentation.
1788
+
1789
+ For example, we can add ``show_in_doc`` to examples, like this:
1790
+
1791
+ .. code:: ruby
1792
+
1793
+ describe "This is the correct path" do
1794
+ it "some test", :show_in_doc do
1795
+ ....
1796
+ end
1797
+ end
1798
+
1799
+ context "These are edge cases" do
1800
+ it "Can't authenticate" do
1801
+ ....
1802
+ end
1803
+
1804
+ it "record not found" do
1805
+ ....
1806
+ end
1807
+ end
1808
+
1809
+ And then configure RSpec in this way:
1810
+
1811
+ .. code:: ruby
1812
+
1813
+ RSpec.configure do |config|
1814
+ config.treat_symbols_as_metadata_keys_with_true_values = true
1815
+ config.filter_run :show_in_doc => true if ENV['APIPIE_RECORD']
1816
+ end
1817
+
1818
+ This way, when running in recording mode, only the tests that have been marked with the
1819
+ ``:show_in_doc`` metadata will be run, and hence only those will be used as examples.
1820
+
1821
+ Caveats
1822
+ -------
1823
+
1824
+ Make sure to enable ``config.render_views`` in your ``config/rails_helper.rb`` or
1825
+ ``config/spec_helper.rb`` if you're using jbuilder, or you will get back empty results
1826
+
1827
+ ====================
1828
+ Bindings Generator
1829
+ ====================
1830
+
1831
+ In earlier versions (<= 0.0.13), there was a simple client generator
1832
+ as a part of Apipie gem. As more features and users came to Apipie,
1833
+ there was a greater need for changes on a per project basis. It's
1834
+ hard (or even impossible) to provide a generic solution for the client
1835
+ code. We also don't want to tell you what's the right way to do it
1836
+ (what gems to use, how the API should look like etc.).
1837
+
1838
+ Therefore you can't generate client code directly by a rake task in
1839
+ further versions.
1840
+
1841
+ There is, however, an even better and more flexible way to reuse your API
1842
+ documentation for this purpose: using the API the Apipie
1843
+ provides in the generator code. Check out our sister project
1844
+ `apipie-bindings <https://github.com/Apipie/apipie-bindings>`_, as they
1845
+ use exactly this approach. You also don't need to run the service,
1846
+ provided it uses Apipie as a backend.
1847
+
1848
+ And if you write one on your own, don't hesitate to share it with us!
1849
+
1850
+
1851
+ ====================
1852
+ Disqus Integration
1853
+ ====================
1854
+
1855
+ You can setup `Disqus <http://www.disqus.com>`_ discussion within
1856
+ your documentation. Just set the credentials in the Apipie
1857
+ configuration:
1858
+
1859
+ .. code:: ruby
1860
+
1861
+ config.disqus_shortname = "MyProjectDoc"
1862
+
1863
+ =====================
1864
+ External References
1865
+ =====================
1866
+
1867
+ * `Getting started tutorial <https://github.com/iNecas/apipie-demo>`_ -
1868
+ including examples of using the tests integration and versioning.
1869
+
1870
+ * `Real-world application usage <https://github.com/Katello/katello>`_
1871
+
1872
+ * `Read-world application usage with versioning <https://github.com/theforeman/foreman>`_
1873
+
1874
+ * `Using Apipie API to generate bindings <https://github.com/Apipie/apipie-bindings>`_