From 752c12997e661cab035c49e976951bf54518c115 Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 3 Nov 2021 05:09:07 +0300 Subject: [PATCH 1/3] Allow to set @authorize_with for an explicit action in a controller --- lib/swagger_yard/operation.rb | 4 ++++ lib/swagger_yard/swagger.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/swagger_yard/operation.rb b/lib/swagger_yard/operation.rb index e825dec..a2f48f0 100644 --- a/lib/swagger_yard/operation.rb +++ b/lib/swagger_yard/operation.rb @@ -6,6 +6,7 @@ class Response class Operation attr_accessor :description, :ruby_method + attr_accessor :authorizations attr_writer :summary attr_reader :path, :http_method attr_reader :parameters @@ -36,6 +37,8 @@ def self.from_yard_object(yard_object, path_item) else operation.default_response.example = tag.text end + when "authorize_with" + operation.authorizations[tag.text] ||= [] end end @@ -50,6 +53,7 @@ def initialize(path_item) @parameters = [] @default_response = nil @responses = [] + @authorizations = {}.merge(api_group.authorizations.to_h) end def summary diff --git a/lib/swagger_yard/swagger.rb b/lib/swagger_yard/swagger.rb index 286cf4d..7d49d90 100644 --- a/lib/swagger_yard/swagger.rb +++ b/lib/swagger_yard/swagger.rb @@ -84,7 +84,7 @@ def operation(op) op_hash["description"] = op.description unless op.description.empty? op_hash["summary"] = op.summary unless op.summary.empty? - authorizations = op.api_group.authorizations + authorizations = op.authorizations unless authorizations.empty? op_hash["security"] = authorizations.map {|k,v| { k => v} } end From acc8a5de8958db766a658fbbd2e421d5b0a03384 Mon Sep 17 00:00:00 2001 From: Anton Date: Mon, 8 Nov 2021 16:52:56 +0300 Subject: [PATCH 2/3] Fix OpenAPI nullable properties --- lib/swagger_yard/openapi.rb | 10 ++++++++++ spec/lib/swagger_yard/openapi_spec.rb | 25 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/swagger_yard/openapi.rb b/lib/swagger_yard/openapi.rb index 923330c..0872893 100644 --- a/lib/swagger_yard/openapi.rb +++ b/lib/swagger_yard/openapi.rb @@ -70,6 +70,16 @@ def response(resp, op) end end + def property(prop) + prop.type.schema_with(model_path: model_path).tap do |h| + unless h['$ref'] + h["description"] = prop.description if prop.description && !prop.description.strip.empty? + h["nullable"] = true if prop.nullable + h["example"] = prop.example if prop.example + end + end + end + def security_defs(security_objects) defs = super Hash[defs.map do |name, d| diff --git a/spec/lib/swagger_yard/openapi_spec.rb b/spec/lib/swagger_yard/openapi_spec.rb index 6bd572b..5a3b79f 100644 --- a/spec/lib/swagger_yard/openapi_spec.rb +++ b/spec/lib/swagger_yard/openapi_spec.rb @@ -225,6 +225,31 @@ end + context "models" do + let(:model) { SwaggerYard::Model.from_yard_object(yard_class('MyModel', content)) } + let(:spec) { stub(path_objects: SwaggerYard::Paths.new([]), tag_objects: [], + security_objects: [], model_objects: { model.id => model }) } + + subject { described_class.new(spec).to_h["components"]["schemas"] } + + context 'nullables' do + subject { super()['MyModel']['properties'] } + + context "with a nullable flag" do + let(:content) { ['@model MyModel', '@property name(nullable) [string] Name'] } + + its(['name', 'type']) { is_expected.to eq('string') } + its(['name', 'nullable']) { is_expected.to eq(true) } + end + + context "with a nullable model" do + let(:content) { ['@model MyModel', '@property name(nullable) [Name] Name'] } + + its(['name']) { is_expected.to eq('$ref' => '#/components/schemas/Name') } + end + end + end + context 'with config.openapi_version set and Swagger.new' do subject { SwaggerYard::Swagger.new.to_h } before { SwaggerYard.config.openapi_version = '3.0.0' } From 66c6517b113330d671a968e7c1bc0fbb139a919a Mon Sep 17 00:00:00 2001 From: Anton Date: Thu, 11 Nov 2021 14:46:28 +0300 Subject: [PATCH 3/3] Allow to set an example for an operation's parameter This commit allows to specify an example to an operation's parameter via in the following form: ``` @parameter name [Type] Description -- example_name ``` --- lib/swagger_yard/openapi.rb | 1 + lib/swagger_yard/parameter.rb | 13 ++++++------ .../dummy/app/controllers/pets_controller.rb | 2 +- spec/lib/swagger_yard/openapi_spec.rb | 2 +- spec/lib/swagger_yard/operation_spec.rb | 20 +++++++++++++++++++ 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/lib/swagger_yard/openapi.rb b/lib/swagger_yard/openapi.rb index 923330c..836159f 100644 --- a/lib/swagger_yard/openapi.rb +++ b/lib/swagger_yard/openapi.rb @@ -39,6 +39,7 @@ def parameters(params) "in" => param.param_type }.tap do |h| schema = param.type.schema_with(model_path: model_path) + schema["example"] = param.example unless param.example.nil? h["schema"] = schema h["explode"] = true if !Array(param.allow_multiple).empty? && schema["items"] end diff --git a/lib/swagger_yard/parameter.rb b/lib/swagger_yard/parameter.rb index ff12012..8d62a60 100644 --- a/lib/swagger_yard/parameter.rb +++ b/lib/swagger_yard/parameter.rb @@ -1,14 +1,15 @@ module SwaggerYard class Parameter - attr_accessor :name, :type, :description, :param_type, :required, :allow_multiple + attr_accessor :name, :type, :description, :param_type, :required, :allow_multiple, :example def self.from_yard_tag(tag) tag = SwaggerYard.requires_name_and_type(tag) return nil unless tag name, options_string = tag.name.split(/[\(\)]/) - description = tag.text + description, example = tag.text.to_s.split(/\s*--\s*/) description = name if description.nil? || description.strip.empty? + example = nil if !example.nil? && example.strip.empty? type = Type.from_type_list(tag.types) options = {} @@ -21,12 +22,12 @@ def self.from_yard_tag(tag) end end - new(name, type, description, options) + new(name, type, description, example, options) end # TODO: support more variation in scope types def self.from_path_param(name) - new(name, Type.new("string"), "Scope response to #{name}", { + new(name, Type.new("string"), "Scope response to #{name}", nil, { required: true, allow_multiple: false, param_type: "path", @@ -34,8 +35,8 @@ def self.from_path_param(name) }) end - def initialize(name, type, description, options={}) - @name, @type, @description = name, type, description + def initialize(name, type, description, example, options={}) + @name, @type, @description, @example = name, type, description, example @required = options[:required] || false @param_type = options[:param_type] || 'query' diff --git a/spec/fixtures/dummy/app/controllers/pets_controller.rb b/spec/fixtures/dummy/app/controllers/pets_controller.rb index 935acb7..5aecc1e 100644 --- a/spec/fixtures/dummy/app/controllers/pets_controller.rb +++ b/spec/fixtures/dummy/app/controllers/pets_controller.rb @@ -17,7 +17,7 @@ def index # return a Pet # @path [GET] /pets/{id} - # @parameter id [integer] The ID for the Pet + # @parameter id [integer] The ID for the Pet -- 1 # @response_type [Pet] # @error_message [EmptyPet] 404 Pet not found # @error_message 400 Invalid ID supplied diff --git a/spec/lib/swagger_yard/openapi_spec.rb b/spec/lib/swagger_yard/openapi_spec.rb index 6bd572b..076bbfe 100644 --- a/spec/lib/swagger_yard/openapi_spec.rb +++ b/spec/lib/swagger_yard/openapi_spec.rb @@ -33,7 +33,7 @@ its(["get", "responses"]) { are_expected.to include("default", 404, 400) } - its(["get", "parameters"]) { are_expected.to include(a_parameter_named("id")) } + its(["get", "parameters"]) { are_expected.to include(include("name" => "id", "schema" => include("example" => "1"))) } its(["get", "security"]) { is_expected.to eq([{'header_x_application_api_key' => []}])} end diff --git a/spec/lib/swagger_yard/operation_spec.rb b/spec/lib/swagger_yard/operation_spec.rb index 96f21e6..c207ccc 100644 --- a/spec/lib/swagger_yard/operation_spec.rb +++ b/spec/lib/swagger_yard/operation_spec.rb @@ -69,6 +69,26 @@ its("parameters.last.description") { is_expected.to eq("name") } end + context "with a declared parameter that has a description and an example" do + let(:tags) { [yard_tag("@path [GET] /hello"), + yard_tag("@parameter name [string] Description -- Example Name")] } + + its("parameters.count") { is_expected.to eq(1) } + its("parameters.last.name") { is_expected.to eq("name") } + its("parameters.last.description") { is_expected.to eq("Description") } + its("parameters.last.example") { is_expected.to eq("Example Name") } + end + + context "with a declared parameter that has no description but has an example" do + let(:tags) { [yard_tag("@path [GET] /hello"), + yard_tag("@parameter name [string] -- Example Name")] } + + its("parameters.count") { is_expected.to eq(1) } + its("parameters.last.name") { is_expected.to eq("name") } + its("parameters.last.description") { is_expected.to eq("name") } + its("parameters.last.example") { is_expected.to eq("Example Name") } + end + context "with a declared parameter that has no description (reversed name/type)" do let(:tags) { [yard_tag("@path [GET] /hello"), yard_tag("@parameter [string] name")] }