class Apipie::SwaggerGenerator
Attributes
computed_interface_id[R]
Public Class Methods
new(apipie)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 14 def initialize(apipie) @apipie = apipie @issued_warnings = [] end
Public Instance Methods
add_headers_from_hash(swagger_params_array, headers)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 666 def add_headers_from_hash(swagger_params_array, headers) swagger_headers = headers.map do |header| header_hash = { name: header[:name], in: 'header', required: header[:options][:required], description: header[:description], type: header[:options][:type] || 'string' } header_hash[:default] = header[:options][:default] if header[:options][:default] header_hash end swagger_params_array.push(*swagger_headers) end
add_missing_params(method, path)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 434 def add_missing_params(method, path) param_names_from_method = method.params.map {|name, desc| name} missing = param_names_from_path(path) - param_names_from_method result = method.params missing.each do |name| warn_path_parameter_not_described(name, path) result[name.to_sym] = OpenStruct.new({ required: true, _gen_added_from_path: true, name: name, validator: Apipie::Validator::NumberValidator.new(nil), options: { in: "path" } }) end result end
add_params_from_hash(swagger_params_array, param_defs, prefix=nil, default_value_for_in=nil)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 682 def add_params_from_hash(swagger_params_array, param_defs, prefix=nil, default_value_for_in=nil) if default_value_for_in @default_value_for_param_in = default_value_for_in else if body_allowed_for_current_method @default_value_for_param_in = "formData" else @default_value_for_param_in = "query" end end param_defs.each do |name, desc| if !prefix.nil? name = "#{prefix}[#{name}]" end if swagger_param_type(desc) == "object" if desc.validator.params_ordered params_hash = Hash[desc.validator.params_ordered.map {|desc| [desc.name, desc]}] add_params_from_hash(swagger_params_array, params_hash, name) else warn_param_ignored_in_form_data(desc.name) end else param_entry = swagger_atomic_param(desc, false, name, false) if param_entry[:required] swagger_params_array.unshift(param_entry) else swagger_params_array.push(param_entry) end end end end
add_resource_description(resource_name, resource)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 185 def add_resource_description(resource_name, resource) if resource._full_description @tags << { name: tag_name_for_resource(resource), description: Apipie.app.translate(resource._full_description, @current_lang) } end end
add_resource_methods(resource_name, resource_defs)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 115 def add_resource_methods(resource_name, resource_defs) resource_defs._methods.each do |apipie_method_name, apipie_method_defs| add_ruby_method(@paths, apipie_method_defs) end end
add_resources(resources)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 108 def add_resources(resources) resources.each do |resource_name, resource_defs| add_resource_description(resource_name, resource_defs) add_resource_methods(resource_name, resource_defs) end end
add_ruby_method(paths, ruby_method)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 198 def add_ruby_method(paths, ruby_method) if @only_method return unless ruby_method.method == @only_method else return if !ruby_method.show end for api in ruby_method.apis do # controller: ruby_method.resource.controller.name, path = swagger_path(api.path) paths[path] ||= {} methods = paths[path] @current_method = ruby_method @warnings_issued = false responses = swagger_responses_hash_for_method(ruby_method) if include_warning_tags? warning_tags = @warnings_issued ? ['warnings issued'] : [] else warning_tags = [] end op_id = swagger_op_id_for_path(api.http_method, api.path) include_op_id_in_computed_interface_id(op_id) method_key = api.http_method.downcase @current_http_method = method_key methods[method_key] = { tags: [tag_name_for_resource(ruby_method.resource)] + warning_tags + ruby_method.tag_list.tags, consumes: params_in_body? ? ['application/json'] : ['application/x-www-form-urlencoded', 'multipart/form-data'], operationId: op_id, summary: Apipie.app.translate(api.short_description, @current_lang), parameters: swagger_params_array_for_method(ruby_method, api.path), responses: responses, description: ruby_method.full_description } if methods[method_key][:summary].nil? methods[method_key].delete(:summary) warn_missing_method_summary end end end
body_allowed_for_current_method()
click to toggle source
# File lib/apipie/swagger_generator.rb, line 626 def body_allowed_for_current_method !(['get', 'head'].include?(@current_http_method)) end
gen_referenced_block_from_params_array(name, params_array, allow_nulls=false)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 561 def gen_referenced_block_from_params_array(name, params_array, allow_nulls=false) return ref_to(:name) if @definitions.key(:name) schema_obj = json_schema_obj_from_params_array(params_array, allow_nulls) return nil if schema_obj.nil? @definitions[name.to_sym] = schema_obj ref_to(name.to_sym) end
generate_from_resources(version, resources, method_name, lang, clear_warnings=false)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 36 def generate_from_resources(version, resources, method_name, lang, clear_warnings=false) init_swagger_vars(version, lang, clear_warnings) @lang = lang @only_method = method_name add_resources(resources) @swagger[:info]["x-computed-id"] = @computed_interface_id if Apipie.configuration.swagger_generate_x_computed_id_field? return @swagger end
include_op_id_in_computed_interface_id(op_id)
click to toggle source
the @computed_interface_id is a number that is uniquely derived from the list of operations added to the swagger definition (in an order-dependent way). it can be used for regression testing, allowing some differentiation between changes that result from changes to the input and those that result from changes to the generation algorithms. note that at the moment, this only takes operation ids into account, and ignores parameter definitions, so it’s only partially useful.
# File lib/apipie/swagger_generator.rb, line 172 def include_op_id_in_computed_interface_id(op_id) @computed_interface_id = Zlib::crc32("#{@computed_interface_id} #{op_id}") if Apipie.configuration.swagger_generate_x_computed_id_field? end
info(msg)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 160 def info(msg) print "--- INFO: [#{ruby_name_for_method(@current_method)}] -- #{msg}\n" end
init_swagger_vars(version, lang, clear_warnings=false)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 52 def init_swagger_vars(version, lang, clear_warnings=false) # docs = { # :name => Apipie.configuration.app_name, # :info => Apipie.app_info(version, lang), # :copyright => Apipie.configuration.copyright, # :doc_url => Apipie.full_url(url_args), # :api_url => Apipie.api_base_url(version), # :resources => _resources # } @swagger = { swagger: '2.0', info: { title: "#{Apipie.configuration.app_name}", description: "#{Apipie.app_info(version, lang)}#{Apipie.configuration.copyright}", version: "#{version}", "x-copyright" => Apipie.configuration.copyright, }, basePath: Apipie.api_base_url(version), consumes: [], paths: {}, definitions: {}, schemes: Apipie.configuration.swagger_schemes, tags: [], securityDefinitions: Apipie.configuration.swagger_security_definitions, security: Apipie.configuration.swagger_global_security } if Apipie.configuration.swagger_api_host @swagger[:host] = Apipie.configuration.swagger_api_host end if params_in_body? @swagger[:consumes] = ['application/json'] @swagger[:info][:title] += " (params in:body)" else @swagger[:consumes] = ['application/x-www-form-urlencoded', 'multipart/form-data'] @swagger[:info][:title] += " (params in:formData)" end @paths = @swagger[:paths] @definitions = @swagger[:definitions] @tags = @swagger[:tags] @issued_warnings = [] if clear_warnings || @issued_warnings.nil? @computed_interface_id = 0 @current_lang = lang end
json_schema_for_method_response(method, return_code, allow_nulls)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 348 def json_schema_for_method_response(method, return_code, allow_nulls) @definitions = {} for response in method.returns if response.code.to_s == return_code.to_s schema = response_schema(response, allow_nulls) if response.code.to_s == return_code.to_s schema[:definitions] = @definitions if @definitions != {} return schema end end nil end
json_schema_for_self_describing_class(cls, allow_nulls)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 360 def json_schema_for_self_describing_class(cls, allow_nulls) adapter = ResponseDescriptionAdapter.from_self_describing_class(cls) response_schema(adapter, allow_nulls) end
json_schema_obj_from_params_array(params_array, allow_nulls = false)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 550 def json_schema_obj_from_params_array(params_array, allow_nulls = false) (param_defs, required_params) = json_schema_param_defs_from_params_array(params_array, allow_nulls) result = {type: "object"} result[:properties] = param_defs result[:additionalProperties] = false unless Apipie.configuration.swagger_allow_additional_properties_in_response result[:required] = required_params if required_params.length > 0 param_defs.length > 0 ? result : nil end
json_schema_param_defs_from_params_array(params_array, allow_nulls = false)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 571 def json_schema_param_defs_from_params_array(params_array, allow_nulls = false) param_defs = {} required_params = [] params_array ||= [] for param_desc in params_array if !param_desc.respond_to?(:required) # pp param_desc raise ("unexpected param_desc format") end required_params.push(param_desc.name.to_sym) if param_desc.required param_type = swagger_param_type(param_desc) if param_type == "object" && param_desc.validator.params_ordered schema = json_schema_obj_from_params_array(param_desc.validator.params_ordered, allow_nulls) if param_desc.additional_properties schema[:additionalProperties] = true end if param_desc.is_array? new_schema = { type: 'array', items: schema } schema = new_schema end if allow_nulls # ideally we would write schema[:type] = ["object", "null"] # but due to a bug in the json-schema gem, we need to use anyOf # see https://github.com/ruby-json-schema/json-schema/issues/404 new_schema = { anyOf: [schema, {type: "null"}] } schema = new_schema end param_defs[param_desc.name.to_sym] = schema if !schema.nil? else param_defs[param_desc.name.to_sym] = swagger_atomic_param(param_desc, true, nil, allow_nulls) end end [param_defs, required_params] end
lookup()
click to toggle source
# File lib/apipie/swagger_generator.rb, line 295 def lookup @lookup ||= { numeric: "number", hash: "object", array: "array", # see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types integer: SwaggerTypeWithFormat.new("integer", "int32"), long: SwaggerTypeWithFormat.new("integer", "int64"), number: SwaggerTypeWithFormat.new("number", nil), # here just for completeness float: SwaggerTypeWithFormat.new("number", "float"), double: SwaggerTypeWithFormat.new("number", "double"), string: SwaggerTypeWithFormat.new("string", nil), # here just for completeness byte: SwaggerTypeWithFormat.new("string", "byte"), binary: SwaggerTypeWithFormat.new("string", "binary"), boolean: SwaggerTypeWithFormat.new("boolean", nil), # here just for completeness date: SwaggerTypeWithFormat.new("string", "date"), dateTime: SwaggerTypeWithFormat.new("string", "date-time"), password: SwaggerTypeWithFormat.new("string", "password"), } end
param_names_from_path(path)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 428 def param_names_from_path(path) path.scan(/:(\w+)/).map do |ar| ar[0].to_sym end end
params_in_body?()
click to toggle source
# File lib/apipie/swagger_generator.rb, line 19 def params_in_body? Apipie.configuration.swagger_content_type_input == :json end
params_in_body_use_reference?()
click to toggle source
# File lib/apipie/swagger_generator.rb, line 23 def params_in_body_use_reference? Apipie.configuration.swagger_json_input_uses_refs end
ref_to(name)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 545 def ref_to(name) "#/definitions/#{name}" end
remove_colons(str)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 261 def remove_colons(str) str.gsub(":", "_") end
response_schema(response, allow_nulls=false)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 365 def response_schema(response, allow_nulls=false) begin # no need to warn about "missing default value for optional param" when processing response definitions prev_value = @disable_default_value_warning @disable_default_value_warning = true if responses_use_reference? && response.typename schema = {"$ref" => gen_referenced_block_from_params_array(swagger_id_for_typename(response.typename), response.params_ordered, allow_nulls)} else schema = json_schema_obj_from_params_array(response.params_ordered, allow_nulls) end ensure @disable_default_value_warning = prev_value end if response.is_array? && schema schema = { type: allow_nulls ? ["array","null"] : "array", items: schema } end if response.allow_additional_properties schema[:additionalProperties] = true end schema end
responses_use_reference?()
click to toggle source
# File lib/apipie/swagger_generator.rb, line 27 def responses_use_reference? Apipie.configuration.swagger_responses_use_refs? end
ruby_name_for_method(method)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 126 def ruby_name_for_method(method) return "<no method>" if method.nil? method.resource.controller.name + "#" + method.method end
save_field(entry, openapi_key, v, apipie_key=openapi_key, translate=false)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 461 def save_field(entry, openapi_key, v, apipie_key=openapi_key, translate=false) if v.key?(apipie_key) if translate entry[openapi_key] = Apipie.app.translate(v[apipie_key], @current_lang) else entry[openapi_key] = v[apipie_key] end end end
swagger_atomic_param(param_desc, in_schema, name, allow_nulls)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 460 def swagger_atomic_param(param_desc, in_schema, name, allow_nulls) def save_field(entry, openapi_key, v, apipie_key=openapi_key, translate=false) if v.key?(apipie_key) if translate entry[openapi_key] = Apipie.app.translate(v[apipie_key], @current_lang) else entry[openapi_key] = v[apipie_key] end end end swagger_def = {} swagger_def[:name] = name if !name.nil? swg_param_type = swagger_param_type(param_desc) swagger_def[:type] = swg_param_type.to_s if (swg_param_type.is_a? SwaggerTypeWithFormat) && !swg_param_type.str_format.nil? swagger_def[:format] = swg_param_type.str_format end if swagger_def[:type] == "array" array_of_validator_opts = param_desc.validator.param_description.options items_type = array_of_validator_opts[:of].to_s || array_of_validator_opts[:array_of].to_s if items_type == "Hash" ref_name = "#{swagger_op_id_for_path(param_desc.method_description.apis.first.http_method, param_desc.method_description.apis.first.path)}_param_#{param_desc.name}" swagger_def[:items] = {"$ref" => gen_referenced_block_from_params_array(ref_name, param_desc.validator.param_description.validator.params_ordered, allow_nulls)} else swagger_def[:items] = {type: "string"} end enum = param_desc.options.fetch(:in, []) swagger_def[:items][:enum] = enum if enum.any? end if swagger_def[:type] == "enum" swagger_def[:type] = "string" swagger_def[:enum] = param_desc.validator.values end if swagger_def[:type] == "object" # we only get here if there is no specification of properties for this object swagger_def[:additionalProperties] = true warn_hash_without_internal_typespec(param_desc.name) end if param_desc.is_array? new_swagger_def = { items: swagger_def, type: 'array' } swagger_def = new_swagger_def if allow_nulls swagger_def[:type] = [swagger_def[:type], "null"] end end if allow_nulls swagger_def[:type] = [swagger_def[:type], "null"] end if !in_schema # the "name" and "in" keys can only be set on root parameters (non-nested) swagger_def[:in] = @default_value_for_param_in if name.present? swagger_def[:required] = param_desc.required if param_desc.required end save_field(swagger_def, :description, param_desc.options, :desc, true) unless param_desc.options[:desc].nil? save_field(swagger_def, :default, param_desc.options, :default_value) if param_desc.respond_to?(:_gen_added_from_path) && !param_desc.required warn_optional_param_in_path(param_desc.name) swagger_def[:required] = true end if !swagger_def[:required] && !swagger_def.key?(:default) warn_optional_without_default_value(param_desc.name) unless @disable_default_value_warning end swagger_def end
swagger_id_for_typename(typename)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 269 def swagger_id_for_typename(typename) typename end
swagger_op_id_for_method(method)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 265 def swagger_op_id_for_method(method) remove_colons method.resource.controller.name + "::" + method.method end
swagger_op_id_for_path(http_method, path)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 273 def swagger_op_id_for_path(http_method, path) # using lowercase http method, because the 'swagger-codegen' tool outputs # strange method names if the http method is in uppercase http_method.downcase + path.gsub(/\//,'_').gsub(/:(\w+)/, '\1').gsub(/_$/,'') end
swagger_param_type(param_desc)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 318 def swagger_param_type(param_desc) if param_desc.nil? raise("problem") end v = param_desc.validator if v.nil? return "string" end if v.class == Apipie::Validator::EnumValidator || (v.respond_to?(:is_enum?) && v.is_enum?) if v.values - [true, false] == [] && [true, false] - v.values == [] warn_inferring_boolean(param_desc.name) return "boolean" else return "enum" end elsif v.class == Apipie::Validator::HashValidator # pp v end return lookup[v.expected_type.to_sym] || v.expected_type end
swagger_params_array_for_method(method, path)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 630 def swagger_params_array_for_method(method, path) swagger_result = [] all_params_hash = add_missing_params(method, path) body_param_defs_array = all_params_hash.map {|k, v| v if !param_names_from_path(path).include?(k)}.select{|v| !v.nil?} body_param_defs_hash = all_params_hash.select {|k, v| v if !param_names_from_path(path).include?(k)} path_param_defs_hash = all_params_hash.select {|k, v| v if param_names_from_path(path).include?(k)} path_param_defs_hash.each{|name,desc| desc.required = true} add_params_from_hash(swagger_result, path_param_defs_hash, nil, "path") if params_in_body? && body_allowed_for_current_method if params_in_body_use_reference? swagger_schema_for_body = {"$ref" => gen_referenced_block_from_params_array("#{swagger_op_id_for_method(method)}_input", body_param_defs_array)} else swagger_schema_for_body = json_schema_obj_from_params_array(body_param_defs_array) end swagger_body_param = { name: 'body', in: 'body', schema: swagger_schema_for_body } swagger_result.push(swagger_body_param) if !swagger_schema_for_body.nil? else add_params_from_hash(swagger_result, body_param_defs_hash) end add_headers_from_hash(swagger_result, method.headers) if method.headers.present? swagger_result end
swagger_path(str)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 250 def swagger_path(str) str = str.gsub(/:(\w+)/, '{\1}') str = str.gsub(/\/$/, '') if str[0] != '/' warn_added_missing_slash(str) str = '/' + str end str end
swagger_responses_hash_for_method(method)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 395 def swagger_responses_hash_for_method(method) result = {} for error in method.errors error_block = {description: Apipie.app.translate(error.description, @current_lang)} result[error.code] = error_block end for response in method.returns swagger_response_block = { description: response.description } schema = response_schema(response) swagger_response_block[:schema] = schema if schema result[response.code] = swagger_response_block end if result.length == 0 warn_no_return_codes_specified result[200] = {description: 'ok'} end result end
tag_name_for_resource(resource)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 180 def tag_name_for_resource(resource) # resource.controller resource._id end
warn(warning_num, msg)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 142 def warn(warning_num, msg) suppress = Apipie.configuration.swagger_suppress_warnings return if suppress == true return if suppress.is_a?(Array) && suppress.include?(warning_num) method_id = ruby_name_for_method(@current_method) warning_id = "#{method_id}#{warning_num}#{msg}" if @issued_warnings.include?(warning_id) # Already issued this warning for the current method return end print "WARNING (#{warning_num}): [#{method_id}] -- #{msg}\n" @issued_warnings.push(warning_id) @warnings_issued = true end
warn_added_missing_slash(path)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 133 def warn_added_missing_slash(path) warn 101,"added missing / at beginning of path: #{path}"; end
warn_hash_without_internal_typespec(param_name)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 135 def warn_hash_without_internal_typespec(param_name) warn 103,"the parameter :#{param_name} is a generic Hash without an internal type specification"; end
warn_inferring_boolean(name)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 140 def warn_inferring_boolean(name) warn 108,"the parameter [#{name}] is Enum with [true,false] values. Inferring 'boolean'"; end
warn_missing_method_summary()
click to toggle source
# File lib/apipie/swagger_generator.rb, line 132 def warn_missing_method_summary() warn 100, "missing short description for method"; end
warn_no_return_codes_specified()
click to toggle source
# File lib/apipie/swagger_generator.rb, line 134 def warn_no_return_codes_specified() warn 102,"no return codes ('errors') specified"; end
warn_optional_param_in_path(param_name)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 136 def warn_optional_param_in_path(param_name) warn 104, "the parameter :#{param_name} is 'in-path'. Ignoring 'not required' in DSL"; end
warn_optional_without_default_value(param_name)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 137 def warn_optional_without_default_value(param_name) warn 105,"the parameter :#{param_name} is optional but default value is not specified (use :default_value => ...)"; end
warn_param_ignored_in_form_data(param_name)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 138 def warn_param_ignored_in_form_data(param_name) warn 106,"ignoring param :#{param_name} -- cannot include Hash without fields in a formData specification"; end
warn_path_parameter_not_described(name,path)
click to toggle source
# File lib/apipie/swagger_generator.rb, line 139 def warn_path_parameter_not_described(name,path) warn 107,"the parameter :#{name} appears in the path #{path} but is not described"; end