apipierails3 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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>`_