Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Swagger generation #569

Merged
merged 10 commits into from
Dec 11, 2017
105 changes: 105 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,111 @@ If, for some complex cases, you need to generate/re-generate just part of the ca
use ``rake apipie:cache cache_part=index`` resp. ``rake apipie:cache cache_part=resources``
To generate it for different locations for further processing use ``rake apipie:cache OUT=/tmp/apipie_cache``.

====================================
Static Swagger (OpenAPI 2.0) files
====================================

To generate a static Swagger definition file from the api, run ``rake apipie:static_swagger_json``.
By default the documentation for the default API version is
used. You can specify the version with ``rake apipie:static_swagger_json[2.0]``. A swagger file will be
generated for each locale. The files will be generated in the same location as the static_json files, but
instead of being named ``schema_apipie[.locale].json``, they will be called ``schema_swagger[.locale].json``.

Specifying default values for parameters
-----------------------------------------
Swagger allows method definitions to include an indication of the the default value for each parameter. To include such
indications, use ``:default_value => <some value>`` in the parameter definition DSL. For example:

.. code:: ruby

param :do_something, Boolean, :desc => "take an action", :required => false, :default_value => false


Generated Warnings
-------------------
The help identify potential improvements to your documentation, the swagger generation process issues warnings if
it identifies various shortcomings of the DSL documentation. Each warning has a code to allow selective suppression
(see swagger-specific configuration below)

:100: missing short description for method
:101: added missing / at beginning of path
:102: no return codes specified for method
:103: a parameter is a generic Hash without an internal type specification
:104: a parameter is an 'in-path' parameter, but specified as 'not required' in the DSL
:105: a parameter is optional but does not have a default value specified
:106: a parameter was ommitted from the swagger output because it is a Hash without fields in a formData specification
:107: a path parameter is not described
:108: inferring that a parameter type is boolean because described as an enum with [false,true] values



Swagger-Specific Configuration Parameters
-------------------------------------------------

There are several configuration parameters that determine the structure of the generated swagger file:

``config.swagger_content_type_input``
If the value is ``:form_data`` - the swagger file will indicate that the server consumes the content types
``application/x-www-form-urlencoded`` and ``multipart/form-data``. Non-path parameters will have the
value ``"in": "formData"``. Note that parameters of type Hash that do not have any fields in them will *be ommitted*
from the resulting files, as there is no way to describe them in swagger.

If the value is ``:json`` - the swagger file will indicate that the server consumes the content type
``application/json``. All non-path parameters will be included in the schema of a single ``"in": "body"`` parameter
of type ``object``.

You can specify the value of this configuration parameter as an additional input to the rake command (e.g.,
``rake apipie:static_swagger_json[2.0,form_data]``).

``config.swagger_json_input_uses_refs``
This parameter is only relevant if ``swagger_content_type_input`` is ``:json``.

If ``true``: the schema of the ``"in": "body"`` parameter of each method is given its own entry in the ``definitions``
section, and is referenced using ``$ref`` from the method definition.

If ``false``: the body parameter definitions are inlined within the method definitions.

``config.swagger_include_warning_tags``
If ``true``: in addition to tagging methods with the name of the resource they belong to, methods for which warnings
have been issued will be tagged with.

``config.swagger_suppress_warnings``
If ``false``: no warnings will be suppressed

If ``true``: all warnings will be suppressed

If an array of values (e.g., ``[100,102,107]``), only the warnings identified by the numbers in the array will be suppressed.

``config.swagger_api_host``
The value to place in the swagger host field.

Default is ``localhost:3000``

If ``nil`` then then host field will not be included.



Known limitations of the current implementation
-------------------------------------------------
* There is currently no way to document the structure and content-type of the data returned from a method
* Recorded examples are currently not included in the generated swagger file
* The apipie ``formats`` value is ignored.
* It is not possible to specify the "consumed" content type on a per-method basis
* It is not possible to leverage all of the parameter type/format capabilities of swagger
* Only OpenAPI 2.0 is supported

====================================
Dynamic Swagger generation
====================================

To generate swagger dynamically, use ``http://localhost:3000/apipie.json?type=swagger``.

Note that authorization is not supported for dynamic swagger generation, so if ``config.authorize`` is defined,
dynamic swagger generation will be disabled.

Dynamically generated swagger is not cached, and is always generated on the fly.


===================
JSON checksums
===================
Expand Down
1 change: 1 addition & 0 deletions apipie-rails.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ Gem::Specification.new do |s|
s.add_development_dependency "RedCloth"
s.add_development_dependency "rake"
s.add_development_dependency "rdoc"
s.add_development_dependency "json-schema", "~> 2.8"
end
20 changes: 18 additions & 2 deletions app/controllers/apipie/apipies_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ def authenticate
end
end


def index
params[:version] ||= Apipie.configuration.default_version

get_format

if params[:type].to_s == 'swagger' && params[:format].to_s == 'json'
head :forbidden and return if Apipie.configuration.authorize
should_render_swagger = true
end

respond_to do |format|

if Apipie.configuration.use_cache?
Expand All @@ -31,9 +37,19 @@ def index
Apipie.load_documentation if Apipie.configuration.reload_controllers? || (Rails.version.to_i >= 4.0 && !Rails.application.config.eager_load)

I18n.locale = @language
@doc = Apipie.to_json(params[:version], params[:resource], params[:method], @language)

@doc = authorized_doc
if should_render_swagger
prev_warning_value = Apipie.configuration.swagger_suppress_warnings
begin
Apipie.configuration.swagger_suppress_warnings = true
@doc = Apipie.to_swagger_json(params[:version], params[:resource], params[:method], @language)
ensure
Apipie.configuration.swagger_suppress_warnings = prev_warning_value
end
else
@doc = Apipie.to_json(params[:version], params[:resource], params[:method], @language)
@doc = authorized_doc
end

format.json do
if @doc
Expand Down
1 change: 1 addition & 0 deletions lib/apipie-rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
require "apipie/railtie"
require 'apipie/extractor'
require "apipie/version"
require "apipie/swagger_generator"

if Rails.version.start_with?("3.0")
warn 'Warning: apipie-rails is not going to support Rails 3.0 anymore in future versions'
Expand Down
5 changes: 5 additions & 0 deletions lib/apipie/apipie_module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ def self.to_json(version = nil, resource_name = nil, method_name = nil, lang = n
app.to_json(version, resource_name, method_name, lang)
end

def self.to_swagger_json(version = nil, resource_name = nil, method_name = nil, lang = nil, clear_warnings=true)
version ||= Apipie.configuration.default_version
app.to_swagger_json(version, resource_name, method_name, lang, clear_warnings)
end

# all calls delegated to Apipie::Application instance
def self.method_missing(method, *args, &block)
app.respond_to?(method) ? app.send(method, *args, &block) : super
Expand Down
19 changes: 19 additions & 0 deletions lib/apipie/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ def init_env
@resource_descriptions ||= HashWithIndifferentAccess.new { |h, version| h[version] = {} }
@controller_to_resource_id ||= {}
@param_groups ||= {}
@swagger_generator = Apipie::SwaggerGenerator.new(self)

# what versions does the controller belong in (specified by resource_description)?
@controller_versions ||= Hash.new { |h, controller| h[controller.to_s] = [] }
Expand All @@ -253,6 +254,24 @@ def reload_examples
@recorded_examples = nil
end

def to_swagger_json(version, resource_name, method_name, lang, clear_warnings=false)
return unless valid_search_args?(version, resource_name, method_name)

# if resource_name is blank, take just resources which have some methods because
# we dont want to show eg ApplicationController as resource
# otherwise, take only the specified resource
_resources = resource_descriptions[version].inject({}) do |result, (k,v)|
if resource_name.blank?
result[k] = v unless v._methods.blank?
else
result[k] = v if k == resource_name
end
result
end

@swagger_generator.generate_from_resources(version,_resources, method_name, lang, clear_warnings)
end

def to_json(version, resource_name, method_name, lang)

return unless valid_search_args?(version, resource_name, method_name)
Expand Down
13 changes: 12 additions & 1 deletion lib/apipie/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ class Configuration
:validate, :validate_value, :validate_presence, :validate_key, :authenticate, :doc_path,
:show_all_examples, :process_params, :update_checksum, :checksum_path,
:link_extension, :record, :languages, :translate, :locale, :default_locale,
:persist_show_in_doc, :authorize
:persist_show_in_doc, :authorize,
:swagger_include_warning_tags, :swagger_content_type_input, :swagger_json_input_uses_refs,
:swagger_suppress_warnings, :swagger_api_host, :swagger_generate_x_computed_id_field

alias_method :validate?, :validate
alias_method :required_by_default?, :required_by_default
alias_method :namespaced_resources?, :namespaced_resources
alias_method :swagger_include_warning_tags?, :swagger_include_warning_tags
alias_method :swagger_json_input_uses_refs?, :swagger_json_input_uses_refs
alias_method :swagger_generate_x_computed_id_field?, :swagger_generate_x_computed_id_field

# matcher to be used in Dir.glob to find controllers to be reloaded e.g.
#
Expand Down Expand Up @@ -165,6 +170,12 @@ def initialize
@translate = lambda { |str, locale| str }
@persist_show_in_doc = false
@routes_formatter = RoutesFormatter.new
@swagger_content_type_input = :form_data # this can be :json or :form_data
@swagger_json_input_uses_refs = false
@swagger_include_warning_tags = false
@swagger_suppress_warnings = false #[105,100,102]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does #[105,100,102] mean?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nm: I looked at the readme and here we go :)

@swagger_api_host = "localhost:3000"
@swagger_generate_x_computed_id_field = false
end
end
end
Loading