grape-swagger 0.26.0 → 0.26.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +1 -0
  4. data/.rspec +0 -1
  5. data/.rubocop.yml +4 -0
  6. data/.travis.yml +26 -24
  7. data/CHANGELOG.md +14 -0
  8. data/Gemfile +3 -5
  9. data/README.md +9 -85
  10. data/UPGRADING.md +9 -0
  11. data/lib/grape-swagger.rb +20 -71
  12. data/lib/grape-swagger/doc_methods.rb +4 -6
  13. data/lib/grape-swagger/doc_methods/parse_params.rb +5 -9
  14. data/lib/grape-swagger/doc_methods/tag_name_description.rb +18 -5
  15. data/lib/grape-swagger/endpoint.rb +8 -10
  16. data/lib/grape-swagger/errors.rb +7 -6
  17. data/lib/grape-swagger/rake/oapi_tasks.rb +5 -1
  18. data/lib/grape-swagger/version.rb +1 -1
  19. data/spec/issues/532_allow_custom_format_spec.rb +36 -0
  20. data/spec/issues/537_enum_values_spec.rb +47 -0
  21. data/spec/issues/572_array_post_body_spec.rb +49 -0
  22. data/spec/lib/endpoint_spec.rb +42 -1
  23. data/spec/lib/extensions_spec.rb +42 -0
  24. data/spec/lib/oapi_tasks_spec.rb +13 -2
  25. data/spec/lib/operation_id_spec.rb +1 -5
  26. data/spec/lib/parse_params_spec.rb +58 -0
  27. data/spec/lib/tag_name_description_spec.rb +77 -0
  28. data/spec/lib/version_spec.rb +26 -0
  29. data/spec/spec_helper.rb +11 -0
  30. data/spec/swagger_v2/api_swagger_v2_detail_spec.rb +1 -77
  31. data/spec/swagger_v2/description_not_initialized.rb +1 -1
  32. data/spec/swagger_v2/guarded_endpoint_spec.rb +2 -2
  33. metadata +16 -9
  34. data/lib/grape-swagger/markdown/kramdown_adapter.rb +0 -37
  35. data/lib/grape-swagger/markdown/redcarpet_adapter.rb +0 -92
  36. data/spec/markdown/kramdown_adapter_spec.rb +0 -31
  37. data/spec/markdown/redcarpet_adapter_spec.rb +0 -66
@@ -20,7 +20,7 @@ module GrapeSwagger
20
20
 
21
21
  # optional properties
22
22
  document_description(settings)
23
- document_type_and_format(data_type)
23
+ document_type_and_format(settings, data_type)
24
24
  document_array_param(value_type, definitions) if value_type[:is_array]
25
25
  document_default_value(settings)
26
26
  document_range_values(settings)
@@ -51,13 +51,14 @@ module GrapeSwagger
51
51
  @parsed_param[:default] = settings[:default] if settings[:default].present?
52
52
  end
53
53
 
54
- def document_type_and_format(data_type)
54
+ def document_type_and_format(settings, data_type)
55
55
  if DataType.primitive?(data_type)
56
56
  data = DataType.mapping(data_type)
57
57
  @parsed_param[:type], @parsed_param[:format] = data
58
58
  else
59
59
  @parsed_param[:type] = data_type
60
60
  end
61
+ @parsed_param[:format] = settings[:format] if settings[:format].present?
61
62
  end
62
63
 
63
64
  def document_array_param(value_type, definitions)
@@ -99,15 +100,10 @@ module GrapeSwagger
99
100
 
100
101
  def parse_enum_or_range_values(values)
101
102
  case values
103
+ when Proc
104
+ parse_enum_or_range_values(values.call)
102
105
  when Range
103
106
  parse_range_values(values) if values.first.is_a?(Integer)
104
- when Proc
105
- values_result = values.call
106
- if values_result.is_a?(Range) && values_result.first.is_a?(Integer)
107
- parse_range_values(values_result)
108
- else
109
- { enum: values_result }
110
- end
111
107
  else
112
108
  { enum: values } if values
113
109
  end
@@ -4,14 +4,27 @@ module GrapeSwagger
4
4
  class << self
5
5
  def build(paths)
6
6
  paths.values.each_with_object([]) do |path, memo|
7
- path.values.first[:tags].each do |tag|
8
- memo << {
9
- name: tag,
10
- description: "Operations about #{tag.pluralize}"
11
- }
7
+ tags = path.values.first[:tags]
8
+
9
+ case tags
10
+ when String
11
+ memo << build_memo(tags)
12
+ when Array
13
+ path.values.first[:tags].each do |tag|
14
+ memo << build_memo(tag)
15
+ end
12
16
  end
13
17
  end.uniq
14
18
  end
19
+
20
+ private
21
+
22
+ def build_memo(tag)
23
+ {
24
+ name: tag,
25
+ description: "Operations about #{tag.pluralize}"
26
+ }
27
+ end
15
28
  end
16
29
  end
17
30
  end
@@ -8,7 +8,7 @@ module Grape
8
8
 
9
9
  if content_types.empty?
10
10
  formats = [target_class.format, target_class.default_format].compact.uniq
11
- formats = Grape::Formatter::Base.formatters({}).keys if formats.empty?
11
+ formats = Grape::Formatter.formatters({}).keys if formats.empty?
12
12
  content_types = Grape::ContentTypes::CONTENT_TYPES.select do |content_type, _mime_type|
13
13
  formats.include? content_type
14
14
  end.values
@@ -105,12 +105,12 @@ module Grape
105
105
  def method_object(route, options, path)
106
106
  method = {}
107
107
  method[:summary] = summary_object(route)
108
- method[:description] = description_object(route, options[:markdown])
108
+ method[:description] = description_object(route)
109
109
  method[:produces] = produces_object(route, options[:produces] || options[:format])
110
110
  method[:consumes] = consumes_object(route, options[:format])
111
111
  method[:parameters] = params_object(route)
112
112
  method[:security] = security_object(route)
113
- method[:responses] = response_object(route, options[:markdown])
113
+ method[:responses] = response_object(route)
114
114
  method[:tags] = route.options.fetch(:tags, tag_object(route))
115
115
  method[:operationId] = GrapeSwagger::DocMethods::OperationId.build(route, path)
116
116
  method.delete_if { |_, value| value.blank? }
@@ -130,10 +130,9 @@ module Grape
130
130
  summary
131
131
  end
132
132
 
133
- def description_object(route, markdown)
133
+ def description_object(route)
134
134
  description = route.description if route.description.present?
135
135
  description = route.options[:detail] if route.options.key?(:detail)
136
- description = markdown.markdown(description.to_s).chomp if markdown
137
136
 
138
137
  description
139
138
  end
@@ -178,7 +177,7 @@ module Grape
178
177
  parameters
179
178
  end
180
179
 
181
- def response_object(route, markdown)
180
+ def response_object(route)
182
181
  codes = (route.http_codes || route.options[:failure] || [])
183
182
 
184
183
  codes = apply_success_codes(route) + codes
@@ -196,13 +195,12 @@ module Grape
196
195
  end
197
196
 
198
197
  next if memo.key?(204)
199
- next unless !response_model.start_with?('Swagger_doc') &&
200
- ((@definitions[response_model] && value[:code].to_s.start_with?('2')) || value[:model])
198
+ next unless !response_model.start_with?('Swagger_doc') && (@definitions[response_model] || value[:model])
201
199
 
202
- @definitions[response_model][:description] = description_object(route, markdown)
200
+ @definitions[response_model][:description] = description_object(route)
203
201
  # TODO: proof that the definition exist, if model isn't specified
204
202
  reference = { '$ref' => "#/definitions/#{response_model}" }
205
- memo[value[:code]][:schema] = if route.options[:is_array]
203
+ memo[value[:code]][:schema] = if route.options[:is_array] && value[:code] < 300
206
204
  { 'type' => 'array', 'items' => reference }
207
205
  else
208
206
  reference
@@ -1,12 +1,13 @@
1
1
  module GrapeSwagger
2
2
  module Errors
3
- class MarkdownDependencyMissingError < StandardError
4
- def initialize(missing_gem)
5
- super("Missing required dependency: #{missing_gem}")
6
- end
7
- end
8
-
9
3
  class UnregisteredParser < StandardError; end
10
4
  class SwaggerSpec < StandardError; end
5
+ class SwaggerSpecDeprecated < SwaggerSpec
6
+ class << self
7
+ def tell!(what)
8
+ warn "[GrapeSwagger] usage of #{what} is deprecated"
9
+ end
10
+ end
11
+ end
11
12
  end
12
13
  end
@@ -35,9 +35,11 @@ module GrapeSwagger
35
35
  store – save as JSON file, default: false (optional)
36
36
  resource - if given only for that it would be generated (optional)'
37
37
  task fetch: :environment do
38
+ # :nocov:
38
39
  make_request
39
40
 
40
41
  save_to_file? ? File.write(file, @oapi) : $stdout.print(@oapi)
42
+ # :nocov:
41
43
  end
42
44
  end
43
45
 
@@ -47,6 +49,7 @@ module GrapeSwagger
47
49
  params (usage: key=value):
48
50
  resource - if given only for that it would be generated (optional)'
49
51
  task validate: :environment do
52
+ # :nocov:
50
53
  ENV['store'] = 'true'
51
54
  ::Rake::Task['oapi:fetch'].invoke
52
55
  exit if error?
@@ -55,6 +58,7 @@ module GrapeSwagger
55
58
 
56
59
  $stdout.puts 'install swagger-cli with `npm install swagger-cli -g`' if output.nil?
57
60
  FileUtils.rm(file)
61
+ # :nocov:
58
62
  end
59
63
  end
60
64
 
@@ -62,7 +66,7 @@ module GrapeSwagger
62
66
  #
63
67
  def make_request
64
68
  get url_for
65
- last_response
69
+
66
70
  @oapi = JSON.pretty_generate(
67
71
  JSON.parse(
68
72
  last_response.body, symolize_names: true
@@ -1,3 +1,3 @@
1
1
  module GrapeSwagger
2
- VERSION = '0.26.0'.freeze
2
+ VERSION = '0.26.1'.freeze
3
3
  end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe '#542 array of type in post params' do
4
+ let(:app) do
5
+ Class.new(Grape::API) do
6
+ namespace :issue_532 do
7
+ params do
8
+ requires :logs, type: String, documentation: { format: 'log' }
9
+ optional :phone_number, type: Integer, documentation: { format: 'phone_number' }
10
+ end
11
+
12
+ post do
13
+ present params
14
+ end
15
+ end
16
+
17
+ add_swagger_documentation format: :json
18
+ end
19
+ end
20
+
21
+ subject do
22
+ get '/swagger_doc'
23
+ JSON.parse(last_response.body)
24
+ end
25
+
26
+ let(:parameters) { subject['paths']['/issue_532']['post']['parameters'] }
27
+
28
+ specify do
29
+ expect(parameters).to eql(
30
+ [
31
+ { 'in' => 'formData', 'name' => 'logs', 'type' => 'string', 'format' => 'log', 'required' => true },
32
+ { 'in' => 'formData', 'name' => 'phone_number', 'type' => 'integer', 'format' => 'phone_number', 'required' => false }
33
+ ]
34
+ )
35
+ end
36
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe '#537 enum values spec' do
4
+ let(:app) do
5
+ Class.new(Grape::API) do
6
+ namespace :issue_539 do
7
+ class Spec < Grape::Entity
8
+ expose :enum_property, documentation: { values: [:foo, :bar] }
9
+ expose :enum_property_default, documentation: { values: %w(a b c), default: 'c' }
10
+ expose :own_format, documentation: { format: 'log' }
11
+ end
12
+
13
+ desc 'create account',
14
+ success: Spec
15
+ get do
16
+ end
17
+ end
18
+
19
+ add_swagger_documentation format: :json
20
+ end
21
+ end
22
+
23
+ subject do
24
+ get '/swagger_doc'
25
+ JSON.parse(last_response.body)
26
+ end
27
+
28
+ let(:property) { subject['definitions']['Spec']['properties']['enum_property'] }
29
+ specify do
30
+ expect(property).to include 'enum'
31
+ expect(property['enum']).to eql %w(foo bar)
32
+ end
33
+
34
+ let(:property_default) { subject['definitions']['Spec']['properties']['enum_property_default'] }
35
+ specify do
36
+ expect(property_default).to include 'enum'
37
+ expect(property_default['enum']).to eql %w(a b c)
38
+ expect(property_default).to include 'default'
39
+ expect(property_default['default']).to eql 'c'
40
+ end
41
+
42
+ let(:own_format) { subject['definitions']['Spec']['properties']['own_format'] }
43
+ specify do
44
+ expect(own_format).to include 'format'
45
+ expect(own_format['format']).to eql 'log'
46
+ end
47
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe '#572 is_array is applied to all possible responses' do
4
+ include_context "#{MODEL_PARSER} swagger example"
5
+
6
+ let(:app) do
7
+ Class.new(Grape::API) do
8
+ namespace :issue_572 do
9
+ desc 'get issue',
10
+ is_array: true,
11
+ success: Entities::UseResponse,
12
+ failure: [
13
+ [401, 'BadKittenError', Entities::ApiError],
14
+ [404, 'NoTreatsError', Entities::ApiError],
15
+ [429, 'TooManyScratchesError', Entities::ApiError]
16
+ ]
17
+
18
+ get
19
+ end
20
+
21
+ add_swagger_documentation format: :json
22
+ end
23
+ end
24
+
25
+ subject do
26
+ get '/swagger_doc'
27
+ JSON.parse(last_response.body)
28
+ end
29
+
30
+ let(:codes) { %w(200 401 404 429) }
31
+
32
+ let(:responses) { subject['paths']['/issue_572']['get']['responses'] }
33
+
34
+ specify { expect(responses.keys.sort).to eq codes }
35
+
36
+ specify do
37
+ expect(responses['200']['schema']).to include 'type'
38
+ expect(responses['200']['schema']['type']).to eql 'array'
39
+ end
40
+
41
+ describe 'no array types' do
42
+ specify do
43
+ codes[1..-1].each do |code|
44
+ expect(responses[code]['schema']).not_to include 'type'
45
+ expect(responses[code]['schema'].keys).to eql ['$ref']
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,7 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Grape::Endpoint do
4
- subject { described_class.new(Grape::Util::InheritableSetting.new, path: '/', method: :get) }
4
+ subject do
5
+ described_class.new(Grape::Util::InheritableSetting.new, path: '/', method: :get)
6
+ end
5
7
 
6
8
  describe '#param_type_is_array?' do
7
9
  it 'returns true if the value passed represents an array' do
@@ -15,4 +17,43 @@ describe Grape::Endpoint do
15
17
  expect(subject.send(:param_type_is_array?, '[String, Integer]')).to be_falsey
16
18
  end
17
19
  end
20
+
21
+ describe '.content_types_for' do
22
+ describe 'defined on target_class' do
23
+ let(:own_json) { 'text/own-json' }
24
+ let(:own_xml) { 'text/own-xml' }
25
+ let(:content_types) do
26
+ {
27
+ own_json: own_json,
28
+ own_xml: own_xml
29
+ }
30
+ end
31
+ let(:target_class) { OpenStruct.new(content_types: content_types) }
32
+
33
+ let(:object) { subject.content_types_for(target_class) }
34
+ specify do
35
+ expect(object).to eql [own_json, own_xml]
36
+ end
37
+ end
38
+
39
+ describe 'not defined' do
40
+ describe 'format given' do
41
+ let(:format) { :json }
42
+ let(:target_class) { OpenStruct.new(format: format) }
43
+ let(:object) { subject.content_types_for(target_class) }
44
+ specify do
45
+ expect(object).to eql ['application/json']
46
+ end
47
+
48
+ describe 'format not given' do
49
+ let(:target_class) { OpenStruct.new }
50
+ let(:object) { subject.content_types_for(target_class) }
51
+
52
+ specify do
53
+ expect(object).to eql %w(application/xml application/json text/plain)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
18
59
  end
@@ -1,6 +1,48 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe GrapeSwagger::DocMethods::Extensions do
4
+ describe '#find_definition' do
5
+ subject { described_class }
6
+
7
+ let(:method) { :get }
8
+ let(:status) { 200 }
9
+
10
+ before { allow(subject).to receive(:method).and_return(method) }
11
+
12
+ describe 'no response for status' do
13
+ let(:path) { { get: { responses: {} } } }
14
+
15
+ specify do
16
+ definition = subject.find_definition(status, path)
17
+ expect(definition).to be_nil
18
+ end
19
+ end
20
+
21
+ describe 'response found' do
22
+ let(:model) { 'Item' }
23
+
24
+ describe 'ref given' do
25
+ let(:path) do
26
+ { get: { responses: { 200 => { schema: { '$ref' => "#/definitions/#{model}" } } } } }
27
+ end
28
+ specify do
29
+ definition = subject.find_definition(status, path)
30
+ expect(definition).to eql model
31
+ end
32
+ end
33
+
34
+ describe 'items given' do
35
+ let(:path) do
36
+ { get: { responses: { 200 => { schema: { 'items' => { '$ref' => "#/definitions/#{model}" } } } } } }
37
+ end
38
+ specify do
39
+ definition = subject.find_definition(status, path)
40
+ expect(definition).to eql model
41
+ end
42
+ end
43
+ end
44
+ end
45
+
4
46
  describe '#extended? and extension' do
5
47
  subject { described_class }
6
48
  describe 'return false (default)' do
@@ -5,11 +5,11 @@ RSpec.describe GrapeSwagger::Rake::OapiTasks do
5
5
  item = Class.new(Grape::API) do
6
6
  version 'v1', using: :path
7
7
 
8
- resource :item do
8
+ namespace :item do
9
9
  get '/'
10
10
  end
11
11
 
12
- resource :otherItem do
12
+ namespace :otherItem do
13
13
  get '/'
14
14
  end
15
15
  end
@@ -100,6 +100,17 @@ RSpec.describe GrapeSwagger::Rake::OapiTasks do
100
100
  end
101
101
  end
102
102
  end
103
+
104
+ describe 'call it' do
105
+ before do
106
+ subject.send(:make_request)
107
+ end
108
+ specify do
109
+ expect(subject).to respond_to :oapi
110
+ expect(subject.oapi).to be_a String
111
+ expect(subject.oapi).not_to be_empty
112
+ end
113
+ end
103
114
  end
104
115
 
105
116
  describe '#file' do