From 6ef89b4e5cc4668248d6bc8bb3ce92b44b0d5ec0 Mon Sep 17 00:00:00 2001 From: Aaron Renner Date: Tue, 27 May 2014 22:56:50 -0600 Subject: [PATCH 1/5] Extracted out Swagger ApiDeclarationFile into it's own class --- lib/swagger/docs.rb | 1 + lib/swagger/docs/api_declaration_file.rb | 84 +++++++++++++++++++ lib/swagger/docs/generator.rb | 15 +--- .../swagger/docs/api_declaration_file_spec.rb | 73 ++++++++++++++++ 4 files changed, 162 insertions(+), 11 deletions(-) create mode 100644 lib/swagger/docs/api_declaration_file.rb create mode 100644 spec/lib/swagger/docs/api_declaration_file_spec.rb diff --git a/lib/swagger/docs.rb b/lib/swagger/docs.rb index 92129d2..15a3b3d 100644 --- a/lib/swagger/docs.rb +++ b/lib/swagger/docs.rb @@ -1,5 +1,6 @@ require "swagger/docs/config" require "swagger/docs/dsl" +require "swagger/docs/api_declaration_file" require "swagger/docs/generator" require "swagger/docs/impotent_methods" require "swagger/docs/methods" diff --git a/lib/swagger/docs/api_declaration_file.rb b/lib/swagger/docs/api_declaration_file.rb new file mode 100644 index 0000000..f38a6f8 --- /dev/null +++ b/lib/swagger/docs/api_declaration_file.rb @@ -0,0 +1,84 @@ +module Swagger + module Docs + class ApiDeclarationFile + attr_reader :path, :apis, :models, :controller_base_path, :root + + def initialize(path, apis, models, controller_base_path, root) + @path = path + @apis = apis + @models = models + @controller_base_path = controller_base_path + @root = root + end + + def generate_resource + resource = build_resource_root_hash + camelize_keys_deep!(resource) + # Add the already-normalized models to the resource. + resource[:resource_file_path] = resource_file_path + resource = resource.merge({:models => models}) if models.present? + resource + end + + def base_path + root[:base_path] + end + + def swagger_version + root[:swagger_version] + end + + def api_version + root[:api_version] + end + + def resource_path + demod + end + + def resource_file_path + trim_leading_slash(debased_path.to_s.underscore) + end + + private + + def build_resource_root_hash + { + "apiVersion" => api_version, + "swaggerVersion" => swagger_version, + "basePath" => base_path, + "resourcePath" => resource_path, + "apis" => apis, + } + end + + + def demod + "#{debased_path.to_s.camelize}".demodulize.camelize.underscore + end + + def debased_path + path.gsub("#{controller_base_path}", "") + end + + def trim_leading_slash(str) + return str if !str + str.gsub(/\A\/+/, '') + end + + def camelize_keys_deep!(h) + h.keys.each do |k| + ks = k.to_s.camelize(:lower) + h[ks] = h.delete k + camelize_keys_deep! h[ks] if h[ks].kind_of? Hash + if h[ks].kind_of? Array + h[ks].each do |a| + next unless a.kind_of? Hash + camelize_keys_deep! a + end + end + end + end + end + end +end diff --git a/lib/swagger/docs/generator.rb b/lib/swagger/docs/generator.rb index 50b47c5..fff4c50 100644 --- a/lib/swagger/docs/generator.rb +++ b/lib/swagger/docs/generator.rb @@ -62,7 +62,7 @@ def generate_doc(api_version, settings, config) ret = process_path(path, root, config, settings) results[ret[:action]] << ret if ret[:action] == :processed - resources << generate_resource(ret[:path], ret[:apis], ret[:models], settings, root, config) + resources << generate_resource(ret[:path], ret[:apis], ret[:models], settings, root) debased_path = get_debased_path(ret[:path], settings[:controller_base_path]) resource_api = { path: "#{Config.transform_path(trim_leading_slash(debased_path))}.{format}", @@ -133,16 +133,9 @@ def process_path(path, root, config, settings) {action: :processed, path: path, apis: apis, models: models, klass: klass} end - def generate_resource(path, apis, models, settings, root, config) - debased_path = get_debased_path(path, settings[:controller_base_path]) - demod = "#{debased_path.to_s.camelize}".demodulize.camelize.underscore - resource_path = trim_leading_slash(debased_path.to_s.underscore) - resource = root.merge({:resource_path => "#{demod}", :apis => apis}) - camelize_keys_deep!(resource) - # Add the already-normalized models to the resource. - resource = resource.merge({:models => models}) if models.present? - resource[:resource_file_path] = resource_path - resource + def generate_resource(path, apis, models, settings, root) + declaration = ApiDeclarationFile.new(path, apis, models, settings[:controller_base_path], root) + declaration.generate_resource end def get_route_path_apis(path, route, klass, settings, config) diff --git a/spec/lib/swagger/docs/api_declaration_file_spec.rb b/spec/lib/swagger/docs/api_declaration_file_spec.rb new file mode 100644 index 0000000..cbd4c00 --- /dev/null +++ b/spec/lib/swagger/docs/api_declaration_file_spec.rb @@ -0,0 +1,73 @@ +require "spec_helper" + +describe Swagger::Docs::ApiDeclarationFile do + describe "#generate_resource" do + + it "generates the appropriate response" do + path = "api/v1/sample" + apis =[ + { + :path=>"sample/{id}", + :operations=>[ + { + :summary=>"Updates an existing User", + :parameters=>[ + { :param_type=>:path, :name=>:id, :type=>:integer, :description=>"User Id", :required=>true}, + {:param_type=>:form, :name=>:first_name, :type=>:string, :description=>"First name", :required=>false}, + {:param_type=>:form, :name=>:last_name, :type=>:string, :description=>"Last name", :required=>false}, + {:param_type=>:form, :name=>:email, :type=>:string, :description=>"Email address", :required=>false}, + {:param_type=>:form, :name=>:tag, :type=>:Tag, :description=>"Tag object", :required=>true} + ], + :response_messages=>[ + {:code=>401, :message=>"Unauthorized"}, + {:code=>404, :message=>"Not Found"}, + {:code=>406, :message=>"Not Acceptable"} + ], + :notes=>"Only the given fields are updated.", + :method=>:put, + :nickname=>"Api::V1::Sample#update" + } + ] + } + ] + + models = { + :Tag=> + { + :id=>:Tag, + :required=>[:id], + :properties=> + { + :id=>{:type=>:integer, :description=>"User Id"}, + :name=>{:type=>:string, :description=>"Name", :foo=>"test"} + }, + :description=>"A Tag object." + } + } + + controller_base_path = "" + + root = { + :api_version=>"1.0", + :swagger_version=>"1.2", + :base_path=>"http://api.no.where/", + :apis=>[] + } + + + declaration = described_class.new(path, apis, models, controller_base_path, root) + + + expected_response = { + "apiVersion"=> declaration.api_version, + "swaggerVersion"=> declaration.swagger_version, + "basePath"=> declaration.base_path, + "apis"=> declaration.apis, + "resourcePath"=> declaration.resource_path, + :models=> declaration.models, + :resource_file_path=> declaration.resource_file_path + } + expect(declaration.generate_resource).to eq(expected_response) + end + end +end From e3bc8f50cb4c7ed0a6f48f8a7ea3d47d125a1d32 Mon Sep 17 00:00:00 2001 From: Aaron Renner Date: Wed, 28 May 2014 00:17:41 -0600 Subject: [PATCH 2/5] Reworked #generate_doc so it doesn't require a call to camelize_keys_deep --- lib/swagger/docs/api_declaration_file.rb | 9 +- lib/swagger/docs/generator.rb | 5 +- .../swagger/docs/api_declaration_file_spec.rb | 119 +++++++++++------- 3 files changed, 79 insertions(+), 54 deletions(-) diff --git a/lib/swagger/docs/api_declaration_file.rb b/lib/swagger/docs/api_declaration_file.rb index f38a6f8..6c271da 100644 --- a/lib/swagger/docs/api_declaration_file.rb +++ b/lib/swagger/docs/api_declaration_file.rb @@ -14,22 +14,22 @@ def initialize(path, apis, models, controller_base_path, root) def generate_resource resource = build_resource_root_hash camelize_keys_deep!(resource) + camelize_keys_deep!(models) # Add the already-normalized models to the resource. - resource[:resource_file_path] = resource_file_path resource = resource.merge({:models => models}) if models.present? resource end def base_path - root[:base_path] + root["basePath"] end def swagger_version - root[:swagger_version] + root["swaggerVersion"] end def api_version - root[:api_version] + root["apiVersion"] end def resource_path @@ -49,6 +49,7 @@ def build_resource_root_hash "basePath" => base_path, "resourcePath" => resource_path, "apis" => apis, + "resource_file_path" => resource_file_path } end diff --git a/lib/swagger/docs/generator.rb b/lib/swagger/docs/generator.rb index fff4c50..e4302d8 100644 --- a/lib/swagger/docs/generator.rb +++ b/lib/swagger/docs/generator.rb @@ -54,7 +54,7 @@ def generate_docs(apis=nil) end def generate_doc(api_version, settings, config) - root = { :api_version => api_version, :swagger_version => "1.2", :base_path => settings[:base_path] + "/", :apis => []} + root = { "apiVersion" => api_version, "swaggerVersion" => "1.2", "basePath" => settings[:base_path] + "/", :apis => []} results = {:processed => [], :skipped => []} resources = [] @@ -71,8 +71,7 @@ def generate_doc(api_version, settings, config) root[:apis] << resource_api end end - root[:resources] = resources - camelize_keys_deep!(root) + root['resources'] = resources results[:root] = root results end diff --git a/spec/lib/swagger/docs/api_declaration_file_spec.rb b/spec/lib/swagger/docs/api_declaration_file_spec.rb index cbd4c00..85f6c2b 100644 --- a/spec/lib/swagger/docs/api_declaration_file_spec.rb +++ b/spec/lib/swagger/docs/api_declaration_file_spec.rb @@ -1,60 +1,64 @@ require "spec_helper" describe Swagger::Docs::ApiDeclarationFile do - describe "#generate_resource" do - - it "generates the appropriate response" do - path = "api/v1/sample" - apis =[ + let(:path) { "api/v1/sample" } + let(:apis) do [ + { + :path=>"sample/{id}", + :operations=>[ { - :path=>"sample/{id}", - :operations=>[ - { - :summary=>"Updates an existing User", - :parameters=>[ - { :param_type=>:path, :name=>:id, :type=>:integer, :description=>"User Id", :required=>true}, - {:param_type=>:form, :name=>:first_name, :type=>:string, :description=>"First name", :required=>false}, - {:param_type=>:form, :name=>:last_name, :type=>:string, :description=>"Last name", :required=>false}, - {:param_type=>:form, :name=>:email, :type=>:string, :description=>"Email address", :required=>false}, - {:param_type=>:form, :name=>:tag, :type=>:Tag, :description=>"Tag object", :required=>true} - ], - :response_messages=>[ - {:code=>401, :message=>"Unauthorized"}, - {:code=>404, :message=>"Not Found"}, - {:code=>406, :message=>"Not Acceptable"} - ], - :notes=>"Only the given fields are updated.", - :method=>:put, - :nickname=>"Api::V1::Sample#update" - } - ] + :summary=>"Updates an existing User", + :parameters=>[ + { :param_type=>:path, :name=>:id, :type=>:integer, :description=>"User Id", :required=>true}, + {:param_type=>:form, :name=>:first_name, :type=>:string, :description=>"First name", :required=>false}, + {:param_type=>:form, :name=>:last_name, :type=>:string, :description=>"Last name", :required=>false}, + {:param_type=>:form, :name=>:email, :type=>:string, :description=>"Email address", :required=>false}, + {:param_type=>:form, :name=>:tag, :type=>:Tag, :description=>"Tag object", :required=>true} + ], + :response_messages=>[ + {:code=>401, :message=>"Unauthorized"}, + {:code=>404, :message=>"Not Found"}, + {:code=>406, :message=>"Not Acceptable"} + ], + :notes=>"Only the given fields are updated.", + :method=>:put, + :nickname=>"Api::V1::Sample#update" } ] + } + ] + end - models = { - :Tag=> - { - :id=>:Tag, - :required=>[:id], - :properties=> - { - :id=>{:type=>:integer, :description=>"User Id"}, - :name=>{:type=>:string, :description=>"Name", :foo=>"test"} - }, - :description=>"A Tag object." - } - } + let(:models) do + { + :Tag=> + { + :id=>:Tag, + :required=>[:id], + :properties=> + { + :id=>{:type=>:integer, :description=>"User Id"}, + :name=>{:type=>:string, :description=>"Name", :foo=>"test"} + }, + :description=>"A Tag object." + } + } + end - controller_base_path = "" + let(:controller_base_path) { "" } - root = { - :api_version=>"1.0", - :swagger_version=>"1.2", - :base_path=>"http://api.no.where/", - :apis=>[] - } + let(:root) do + { + :api_version=>"1.0", + :swagger_version=>"1.2", + :base_path=>"http://api.no.where/", + :apis=>[] + } + end + describe "#generate_resource" do + it "generates the appropriate response" do declaration = described_class.new(path, apis, models, controller_base_path, root) @@ -65,9 +69,30 @@ "apis"=> declaration.apis, "resourcePath"=> declaration.resource_path, :models=> declaration.models, - :resource_file_path=> declaration.resource_file_path + "resourceFilePath" => declaration.resource_file_path } expect(declaration.generate_resource).to eq(expected_response) end end + + describe "#base_path" do + it "returns root['basePath']" do + declaration = described_class.new(path, apis, models, controller_base_path, root) + expect(declaration.base_path).to eq(root['basePath']) + end + end + + describe "#swagger_version" do + it "returns root['swaggerVersion']" do + declaration = described_class.new(path, apis, models, controller_base_path, root) + expect(declaration.swagger_version).to eq(root['swaggerVersion']) + end + end + + describe "#api_version" do + it "returns root['apiVersion']" do + declaration = described_class.new(path, apis, models, controller_base_path, root) + expect(declaration.swagger_version).to eq(root['apiVersion']) + end + end end From b58a49a82668d84a5a7b1cac194bd3fd16c6702a Mon Sep 17 00:00:00 2001 From: Aaron Renner Date: Wed, 28 May 2014 01:41:33 -0600 Subject: [PATCH 3/5] Switched to a non-modifying version of #camelize_keys_deep --- lib/swagger/docs/api_declaration_file.rb | 30 +++-- .../swagger/docs/api_declaration_file_spec.rb | 127 +++++++++++++----- 2 files changed, 107 insertions(+), 50 deletions(-) diff --git a/lib/swagger/docs/api_declaration_file.rb b/lib/swagger/docs/api_declaration_file.rb index 6c271da..3bd03e2 100644 --- a/lib/swagger/docs/api_declaration_file.rb +++ b/lib/swagger/docs/api_declaration_file.rb @@ -5,16 +5,14 @@ class ApiDeclarationFile def initialize(path, apis, models, controller_base_path, root) @path = path - @apis = apis - @models = models + @apis = camelize_keys_deep apis + @models = camelize_keys_deep models @controller_base_path = controller_base_path @root = root end def generate_resource resource = build_resource_root_hash - camelize_keys_deep!(resource) - camelize_keys_deep!(models) # Add the already-normalized models to the resource. resource = resource.merge({:models => models}) if models.present? resource @@ -49,7 +47,7 @@ def build_resource_root_hash "basePath" => base_path, "resourcePath" => resource_path, "apis" => apis, - "resource_file_path" => resource_file_path + "resourceFilePath" => resource_file_path } end @@ -67,17 +65,21 @@ def trim_leading_slash(str) str.gsub(/\A\/+/, '') end - def camelize_keys_deep!(h) - h.keys.each do |k| - ks = k.to_s.camelize(:lower) - h[ks] = h.delete k - camelize_keys_deep! h[ks] if h[ks].kind_of? Hash - if h[ks].kind_of? Array - h[ks].each do |a| - next unless a.kind_of? Hash - camelize_keys_deep! a + def camelize_keys_deep(obj) + if obj.is_a? Hash + Hash[ + obj.map do |k, v| + new_key = k.to_s.camelize(:lower) + new_value = camelize_keys_deep v + [new_key, new_value] end + ] + elsif obj.is_a? Array + new_value = obj.collect do |a| + camelize_keys_deep a end + else + obj end end end diff --git a/spec/lib/swagger/docs/api_declaration_file_spec.rb b/spec/lib/swagger/docs/api_declaration_file_spec.rb index 85f6c2b..1967e28 100644 --- a/spec/lib/swagger/docs/api_declaration_file_spec.rb +++ b/spec/lib/swagger/docs/api_declaration_file_spec.rb @@ -2,47 +2,49 @@ describe Swagger::Docs::ApiDeclarationFile do let(:path) { "api/v1/sample" } - let(:apis) do [ - { - :path=>"sample/{id}", - :operations=>[ - { - :summary=>"Updates an existing User", - :parameters=>[ - { :param_type=>:path, :name=>:id, :type=>:integer, :description=>"User Id", :required=>true}, - {:param_type=>:form, :name=>:first_name, :type=>:string, :description=>"First name", :required=>false}, - {:param_type=>:form, :name=>:last_name, :type=>:string, :description=>"Last name", :required=>false}, - {:param_type=>:form, :name=>:email, :type=>:string, :description=>"Email address", :required=>false}, - {:param_type=>:form, :name=>:tag, :type=>:Tag, :description=>"Tag object", :required=>true} - ], - :response_messages=>[ - {:code=>401, :message=>"Unauthorized"}, - {:code=>404, :message=>"Not Found"}, - {:code=>406, :message=>"Not Acceptable"} - ], - :notes=>"Only the given fields are updated.", - :method=>:put, - :nickname=>"Api::V1::Sample#update" - } - ] - } - ] + let(:apis) do + [ + { + :path=>"sample/{id}", + :operations=>[ + { + :summary=>"Updates an existing User", + :parameters=>[ + {:param_type=>:path, :name=>:id, :type=>:integer, :description=>"User Id", :required=>true}, + {:param_type=>:form, :name=>:first_name, :type=>:string, :description=>"First name", :required=>false}, + {:param_type=>:form, :name=>:last_name, :type=>:string, :description=>"Last name", :required=>false}, + {:param_type=>:form, :name=>:email, :type=>:string, :description=>"Email address", :required=>false}, + {:param_type=>:form, :name=>:tag, :type=>:Tag, :description=>"Tag object", :required=>true} + ], + :response_messages=>[ + {:code=>401, :message=>"Unauthorized"}, + {:code=>404, :message=>"Not Found"}, + {:code=>406, :message=>"Not Acceptable"} + ], + :notes=>"Only the given fields are updated.", + :method=>:put, + :nickname=>"Api::V1::Sample#update" + } + ] + } + ] end let(:models) do { - :Tag=> - { - :id=>:Tag, - :required=>[:id], - :properties=> + :Tag=> { - :id=>{:type=>:integer, :description=>"User Id"}, - :name=>{:type=>:string, :description=>"Name", :foo=>"test"} - }, - :description=>"A Tag object." + :id=>:Tag, + :required=>[:id], + :properties=> + { + :id=>{:type=>:integer, :description=>"User Id"}, + :first_name=>{:type=>:string, :description=>"First Name"}, + :last_name=>{:type=>:string, :description=>"Last Name"} + }, + :description=>"A Tag object." + } } - } end let(:controller_base_path) { "" } @@ -61,7 +63,6 @@ it "generates the appropriate response" do declaration = described_class.new(path, apis, models, controller_base_path, root) - expected_response = { "apiVersion"=> declaration.api_version, "swaggerVersion"=> declaration.swagger_version, @@ -95,4 +96,58 @@ expect(declaration.swagger_version).to eq(root['apiVersion']) end end + + describe "#models" do + it "returns a models hash that's ready for output" do + declaration = described_class.new(path, apis, models, controller_base_path, root) + expected_models_hash = { + "tag" => + { + "id" => :Tag, + "required" =>[:id], + "properties" => + { + "id" =>{"type"=>:integer, "description"=>"User Id"}, + "firstName"=>{"type"=>:string, "description"=>"First Name"}, + "lastName"=>{"type"=>:string, "description"=>"Last Name"}, + }, + "description"=>"A Tag object." + } + } + + expect(declaration.models).to eq(expected_models_hash) + end + end + + describe "#apis" do + it "returns a api hash that's ready for output" do + declaration = described_class.new(path, apis, models, controller_base_path, root) + expected_apis_array = [ + { + "path"=>"sample/{id}", + "operations"=>[ + { + "summary"=>"Updates an existing User", + "parameters"=>[ + {"paramType"=>:path, "name"=>:id, "type"=>:integer, "description"=>"User Id", "required"=>true}, + {"paramType"=>:form, "name"=>:first_name, "type"=>:string, "description"=>"First name", "required"=>false}, + {"paramType"=>:form, "name"=>:last_name, "type"=>:string, "description"=>"Last name", "required"=>false}, + {"paramType"=>:form, "name"=>:email, "type"=>:string, "description"=>"Email address", "required"=>false}, + {"paramType"=>:form, "name"=>:tag, "type"=>:Tag, "description"=>"Tag object", "required"=>true} + ], + "responseMessages"=>[ + {"code"=>401, "message"=>"Unauthorized"}, + {"code"=>404, "message"=>"Not Found"}, + {"code"=>406, "message"=>"Not Acceptable"} + ], + "notes"=>"Only the given fields are updated.", + "method"=>:put, + "nickname"=>"Api::V1::Sample#update" + } + ] + } + ] + expect(declaration.apis).to eq(expected_apis_array) + end + end end From 8bbef3a21cef378ed96e625f7e12cab894768717 Mon Sep 17 00:00:00 2001 From: Aaron Renner Date: Wed, 28 May 2014 10:53:48 -0600 Subject: [PATCH 4/5] Make camelizing of model properties configurable. * Fix model tag name so it is the same case as the symbol passed in. A swagger_model :Tag was being put into the json models hash as 'tag' instead of 'Tag' * Created ApiDeclarationFileMetadata class to make it clear the fields that are needed on the ApiDeclarationFile metadata object * Updated readme --- README.md | 14 ++- lib/swagger/docs.rb | 1 + lib/swagger/docs/api_declaration_file.rb | 57 +++++++-- .../docs/api_declaration_file_metadata.rb | 18 +++ lib/swagger/docs/generator.rb | 10 +- .../api_declaration_file_metadata_spec.rb | 55 ++++++++ .../swagger/docs/api_declaration_file_spec.rb | 117 ++++++++++++------ spec/lib/swagger/docs/generator_spec.rb | 2 +- 8 files changed, 218 insertions(+), 56 deletions(-) create mode 100644 lib/swagger/docs/api_declaration_file_metadata.rb create mode 100644 spec/lib/swagger/docs/api_declaration_file_metadata_spec.rb diff --git a/README.md b/README.md index ec65a17..84f1818 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,12 @@ The following table shows all the current configuration options and their defaul :pretty + +camelize_model_properties +Camelizes property names of models. For example, a property name called first_name would be converted to firstName. +true + + @@ -174,19 +180,19 @@ end ### DRYing up common documentation Suppose you have a header or a parameter that must be present on several controllers and methods. Instead of duplicating it on all the controllers you can do this on your API base controller: - + ```ruby class Api::BaseController < ActionController::Base class << self Swagger::Docs::Generator::set_real_methods - + def inherited(subclass) super subclass.class_eval do setup_basic_api_documentation end end - + private def setup_basic_api_documentation [:index, :show, :create, :update, :delete].each do |api_action| @@ -198,7 +204,7 @@ class Api::BaseController < ActionController::Base end end ``` - + And then use it as a superclass to all you API controllers. All the subclassed controllers will have the same documentation applied to them. ### DSL Methods diff --git a/lib/swagger/docs.rb b/lib/swagger/docs.rb index 15a3b3d..82dca82 100644 --- a/lib/swagger/docs.rb +++ b/lib/swagger/docs.rb @@ -1,5 +1,6 @@ require "swagger/docs/config" require "swagger/docs/dsl" +require "swagger/docs/api_declaration_file_metadata" require "swagger/docs/api_declaration_file" require "swagger/docs/generator" require "swagger/docs/impotent_methods" diff --git a/lib/swagger/docs/api_declaration_file.rb b/lib/swagger/docs/api_declaration_file.rb index 3bd03e2..e161a70 100644 --- a/lib/swagger/docs/api_declaration_file.rb +++ b/lib/swagger/docs/api_declaration_file.rb @@ -1,14 +1,12 @@ module Swagger module Docs class ApiDeclarationFile - attr_reader :path, :apis, :models, :controller_base_path, :root + attr_reader :metadata, :apis - def initialize(path, apis, models, controller_base_path, root) - @path = path + def initialize(metadata, apis, models) + @metadata = metadata @apis = camelize_keys_deep apis - @models = camelize_keys_deep models - @controller_base_path = controller_base_path - @root = root + @models = models end def generate_resource @@ -19,15 +17,27 @@ def generate_resource end def base_path - root["basePath"] + metadata.base_path + end + + def path + metadata.path end def swagger_version - root["swaggerVersion"] + metadata.swagger_version end def api_version - root["apiVersion"] + metadata.api_version + end + + def controller_base_path + metadata.controller_base_path + end + + def camelize_model_properties + metadata.camelize_model_properties end def resource_path @@ -38,6 +48,10 @@ def resource_file_path trim_leading_slash(debased_path.to_s.underscore) end + def models + normalize_model_properties @models + end + private def build_resource_root_hash @@ -51,6 +65,16 @@ def build_resource_root_hash } end + def normalize_model_properties(models) + Hash[ + models.map do |k, v| + if camelize_model_properties + [k.to_s, camelize_keys_deep(v)] + else + [k.to_s, stringify_keys_deep(v)] + end + end] + end def demod "#{debased_path.to_s.camelize}".demodulize.camelize.underscore @@ -66,22 +90,31 @@ def trim_leading_slash(str) end def camelize_keys_deep(obj) + process_keys_deep(obj){|key| key.to_s.camelize(:lower)} + end + + def stringify_keys_deep(obj) + process_keys_deep(obj){|key| key.to_s} + end + + def process_keys_deep(obj, &block) if obj.is_a? Hash Hash[ obj.map do |k, v| - new_key = k.to_s.camelize(:lower) - new_value = camelize_keys_deep v + new_key = block.call(k) + new_value = process_keys_deep v, &block [new_key, new_value] end ] elsif obj.is_a? Array new_value = obj.collect do |a| - camelize_keys_deep a + process_keys_deep a, &block end else obj end end + end end end diff --git a/lib/swagger/docs/api_declaration_file_metadata.rb b/lib/swagger/docs/api_declaration_file_metadata.rb new file mode 100644 index 0000000..ecf6730 --- /dev/null +++ b/lib/swagger/docs/api_declaration_file_metadata.rb @@ -0,0 +1,18 @@ +module Swagger + module Docs + class ApiDeclarationFileMetadata + DEFAULT_SWAGGER_VERSION = "1.2" + + attr_reader :api_version, :path, :base_path, :controller_base_path, :swagger_version, :camelize_model_properties + + def initialize(api_version, path, base_path, controller_base_path, options={}) + @api_version = api_version + @path = path + @base_path = base_path + @controller_base_path = controller_base_path + @swagger_version = options.fetch(:swagger_version, DEFAULT_SWAGGER_VERSION) + @camelize_model_properties = options.fetch(:camelize_model_properties, true) + end + end + end +end diff --git a/lib/swagger/docs/generator.rb b/lib/swagger/docs/generator.rb index e4302d8..844f467 100644 --- a/lib/swagger/docs/generator.rb +++ b/lib/swagger/docs/generator.rb @@ -62,7 +62,7 @@ def generate_doc(api_version, settings, config) ret = process_path(path, root, config, settings) results[ret[:action]] << ret if ret[:action] == :processed - resources << generate_resource(ret[:path], ret[:apis], ret[:models], settings, root) + resources << generate_resource(ret[:path], ret[:apis], ret[:models], settings, root, config) debased_path = get_debased_path(ret[:path], settings[:controller_base_path]) resource_api = { path: "#{Config.transform_path(trim_leading_slash(debased_path))}.{format}", @@ -132,8 +132,12 @@ def process_path(path, root, config, settings) {action: :processed, path: path, apis: apis, models: models, klass: klass} end - def generate_resource(path, apis, models, settings, root) - declaration = ApiDeclarationFile.new(path, apis, models, settings[:controller_base_path], root) + def generate_resource(path, apis, models, settings, root, config) + metadata = ApiDeclarationFileMetadata.new(root["apiVersion"], path, root["basePath"], + settings[:controller_base_path], + camelize_model_properties: config.fetch(:camelize_model_properties, true), + swagger_version: root["swaggerVersion"]) + declaration = ApiDeclarationFile.new(metadata, apis, models) declaration.generate_resource end diff --git a/spec/lib/swagger/docs/api_declaration_file_metadata_spec.rb b/spec/lib/swagger/docs/api_declaration_file_metadata_spec.rb new file mode 100644 index 0000000..74f2807 --- /dev/null +++ b/spec/lib/swagger/docs/api_declaration_file_metadata_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe Swagger::Docs::ApiDeclarationFileMetadata do + + describe "#initialize" do + it "sets the api_version property" do + metadata = described_class.new("1.0", "path", "basePath", "controllerBasePath") + + expect(metadata.api_version).to eq("1.0") + end + + it "sets the path property" do + metadata = described_class.new("1.0", "path", "basePath", "controllerBasePath") + + expect(metadata.path).to eq("path") + end + + it "sets the base_path property" do + metadata = described_class.new("1.0", "path", "basePath", "controllerBasePath") + + expect(metadata.base_path).to eq("basePath") + end + + it "sets the controller_base_path property" do + metadata = described_class.new("1.0", "path", "basePath", "controllerBasePath") + + expect(metadata.controller_base_path).to eq("controllerBasePath") + end + + it "defaults the swagger_version property to DEFAULT_SWAGGER_VERSION" do + metadata = described_class.new("1.0", "path", "basePath", "controllerBasePath") + + expect(metadata.swagger_version).to eq(described_class::DEFAULT_SWAGGER_VERSION) + end + + it "allows the swagger_version property to be_overriden" do + metadata = described_class.new("1.0", "path", "basePath", "controllerBasePath", swagger_version: "2.0") + + expect(metadata.swagger_version).to eq("2.0") + end + + + it "defaults the camelize_model_properties property to true" do + metadata = described_class.new("1.0", "path", "basePath", "controllerBasePath") + + expect(metadata.camelize_model_properties).to eq(true) + end + + it "allows the camelize_model_properties property to be overidden" do + metadata = described_class.new("1.0", "path", "basePath", "controllerBasePath", camelize_model_properties: false) + + expect(metadata.camelize_model_properties).to eq(false) + end + end +end diff --git a/spec/lib/swagger/docs/api_declaration_file_spec.rb b/spec/lib/swagger/docs/api_declaration_file_spec.rb index 1967e28..b2b2574 100644 --- a/spec/lib/swagger/docs/api_declaration_file_spec.rb +++ b/spec/lib/swagger/docs/api_declaration_file_spec.rb @@ -1,7 +1,6 @@ require "spec_helper" describe Swagger::Docs::ApiDeclarationFile do - let(:path) { "api/v1/sample" } let(:apis) do [ { @@ -47,21 +46,14 @@ } end - let(:controller_base_path) { "" } - - let(:root) do - { - :api_version=>"1.0", - :swagger_version=>"1.2", - :base_path=>"http://api.no.where/", - :apis=>[] - } + let(:metadata) do + Swagger::Docs::ApiDeclarationFileMetadata.new("1.0", "api/v1/sample", "http://api.no.where/", "") end describe "#generate_resource" do it "generates the appropriate response" do - declaration = described_class.new(path, apis, models, controller_base_path, root) + declaration = described_class.new(metadata, apis, models) expected_response = { "apiVersion"=> declaration.api_version, @@ -77,51 +69,104 @@ end describe "#base_path" do - it "returns root['basePath']" do - declaration = described_class.new(path, apis, models, controller_base_path, root) - expect(declaration.base_path).to eq(root['basePath']) + it "returns metadata.base_path" do + metadata = double("metadata", base_path: "/hello") + declaration = described_class.new(metadata, apis, models) + expect(declaration.base_path).to eq(metadata.base_path) + end + end + + describe "#path" do + it "returns metadata.path" do + metadata = double("metadata", path: "/hello") + declaration = described_class.new(metadata, apis, models) + expect(declaration.path).to eq(metadata.path) + end + end + + describe "#controller_base_path" do + it "returns metadata.controller_base_path" do + metadata = double("metadata", controller_base_path: "/hello") + declaration = described_class.new(metadata, apis, models) + expect(declaration.controller_base_path).to eq(metadata.controller_base_path) end end describe "#swagger_version" do - it "returns root['swaggerVersion']" do - declaration = described_class.new(path, apis, models, controller_base_path, root) - expect(declaration.swagger_version).to eq(root['swaggerVersion']) + it "returns metadata.swagger_version" do + metadata = double("metadata", swagger_version: "1.2") + declaration = described_class.new(metadata, apis, models) + expect(declaration.swagger_version).to eq(metadata.swagger_version) end end describe "#api_version" do - it "returns root['apiVersion']" do - declaration = described_class.new(path, apis, models, controller_base_path, root) - expect(declaration.swagger_version).to eq(root['apiVersion']) + it "returns metadata.api_version" do + metadata = double("metadata", api_version: "1.0") + declaration = described_class.new(metadata, apis, models) + expect(declaration.api_version).to eq(metadata.api_version) + end + end + + describe "#camelize_model_properties" do + it "returns metadata.camelize_model_properties" do + metadata = double("metadata", camelize_model_properties: false) + declaration = described_class.new(metadata, apis, models) + expect(declaration.camelize_model_properties).to eq(metadata.camelize_model_properties) end end describe "#models" do - it "returns a models hash that's ready for output" do - declaration = described_class.new(path, apis, models, controller_base_path, root) - expected_models_hash = { - "tag" => - { - "id" => :Tag, - "required" =>[:id], - "properties" => + context "with camelize_model_properties set to true" do + it "returns a models hash that's ready for output" do + declaration = described_class.new(metadata, apis, models) + allow(declaration).to receive(:camelize_model_properties).and_return(true) + expected_models_hash = { + "Tag" => { - "id" =>{"type"=>:integer, "description"=>"User Id"}, - "firstName"=>{"type"=>:string, "description"=>"First Name"}, - "lastName"=>{"type"=>:string, "description"=>"Last Name"}, - }, - "description"=>"A Tag object." + "id" => :Tag, + "required" =>[:id], + "properties" => + { + "id" =>{"type"=>:integer, "description"=>"User Id"}, + "firstName"=>{"type"=>:string, "description"=>"First Name"}, + "lastName"=>{"type"=>:string, "description"=>"Last Name"}, + }, + "description"=>"A Tag object." + } + } + + expect(declaration.models).to eq(expected_models_hash) + end + end + + context "with camelize_model_properties set to false" do + it "returns a models hash that's ready for output" do + declaration = described_class.new(metadata, apis, models) + allow(declaration).to receive(:camelize_model_properties).and_return(false) + expected_models_hash = { + "Tag" => + { + "id" => :Tag, + "required" =>[:id], + "properties" => + { + "id" =>{"type"=>:integer, "description"=>"User Id"}, + "first_name"=>{"type"=>:string, "description"=>"First Name"}, + "last_name"=>{"type"=>:string, "description"=>"Last Name"}, + }, + "description"=>"A Tag object." + } } - } - expect(declaration.models).to eq(expected_models_hash) + expect(declaration.models).to eq(expected_models_hash) + end end end describe "#apis" do it "returns a api hash that's ready for output" do - declaration = described_class.new(path, apis, models, controller_base_path, root) + declaration = described_class.new(metadata, apis, models) expected_apis_array = [ { "path"=>"sample/{id}", diff --git a/spec/lib/swagger/docs/generator_spec.rb b/spec/lib/swagger/docs/generator_spec.rb index c660cfd..16d7eb3 100644 --- a/spec/lib/swagger/docs/generator_spec.rb +++ b/spec/lib/swagger/docs/generator_spec.rb @@ -321,7 +321,7 @@ } } } - expect(models['tag']).to eq expected_model + expect(models['Tag']).to eq expected_model end end end From 4bd7992bb17728e9a0cddd282780f77fb4c62e69 Mon Sep 17 00:00:00 2001 From: Rich Hollis Date: Fri, 30 May 2014 14:08:53 +0100 Subject: [PATCH 5/5] v0.1.7 --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/swagger/docs/version.rb | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9498cad..cd97a37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.7 + +- Make camelizing of model properties configurable. #55 + ## 0.1.6 - Document notes DSL diff --git a/README.md b/README.md index 84f1818..a218035 100644 --- a/README.md +++ b/README.md @@ -614,7 +614,7 @@ users.json output: ## Thanks to our contributors -Thanks to jdar, fotinakis, stevschmid, ldnunes and all of our contributors for making swagger-docs even better. +Thanks to jdar, fotinakis, stevschmid, ldnunes, aaronrenner and all of our contributors for making swagger-docs even better. ## Contributing diff --git a/lib/swagger/docs/version.rb b/lib/swagger/docs/version.rb index 13d9e88..a64e304 100644 --- a/lib/swagger/docs/version.rb +++ b/lib/swagger/docs/version.rb @@ -1,5 +1,5 @@ module Swagger module Docs - VERSION = "0.1.6" + VERSION = "0.1.7" end end