grape-swagger 0.33.0 → 0.34.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -7
  3. data/.rubocop_todo.yml +0 -6
  4. data/.travis.yml +10 -11
  5. data/CHANGELOG.md +72 -6
  6. data/Gemfile +4 -5
  7. data/README.md +68 -4
  8. data/grape-swagger.gemspec +2 -1
  9. data/lib/grape-swagger/doc_methods/build_model_definition.rb +0 -17
  10. data/lib/grape-swagger/doc_methods/extensions.rb +6 -1
  11. data/lib/grape-swagger/doc_methods/format_data.rb +51 -0
  12. data/lib/grape-swagger/doc_methods/move_params.rb +22 -49
  13. data/lib/grape-swagger/doc_methods/parse_params.rb +6 -0
  14. data/lib/grape-swagger/doc_methods.rb +2 -0
  15. data/lib/grape-swagger/endpoint/params_parser.rb +10 -17
  16. data/lib/grape-swagger/endpoint.rb +32 -13
  17. data/lib/grape-swagger/version.rb +1 -1
  18. data/lib/grape-swagger.rb +1 -1
  19. data/spec/issues/751_deeply_nested_objects_spec.rb +190 -0
  20. data/spec/lib/endpoint/params_parser_spec.rb +44 -20
  21. data/spec/lib/endpoint_spec.rb +3 -3
  22. data/spec/lib/extensions_spec.rb +10 -0
  23. data/spec/lib/format_data_spec.rb +91 -0
  24. data/spec/lib/move_params_spec.rb +4 -266
  25. data/spec/lib/optional_object_spec.rb +0 -1
  26. data/spec/spec_helper.rb +1 -1
  27. data/spec/swagger_v2/api_swagger_v2_hash_and_array_spec.rb +3 -1
  28. data/spec/swagger_v2/api_swagger_v2_response_with_root_spec.rb +153 -0
  29. data/spec/swagger_v2/description_not_initialized_spec.rb +39 -0
  30. data/spec/swagger_v2/endpoint_versioned_path_spec.rb +33 -0
  31. data/spec/swagger_v2/mounted_target_class_spec.rb +1 -1
  32. data/spec/swagger_v2/namespace_tags_prefix_spec.rb +15 -1
  33. data/spec/swagger_v2/params_array_spec.rb +2 -2
  34. data/spec/swagger_v2/parent_less_namespace_spec.rb +32 -0
  35. data/spec/swagger_v2/{reference_entity.rb → reference_entity_spec.rb} +17 -10
  36. metadata +36 -9
  37. data/spec/swagger_v2/description_not_initialized.rb +0 -39
  38. data/spec/swagger_v2/parent_less_namespace.rb +0 -49
@@ -3,6 +3,16 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe GrapeSwagger::DocMethods::Extensions do
6
+ context 'it should not break method introspection' do
7
+ describe '.method' do
8
+ describe 'method introspection' do
9
+ specify do
10
+ expect(described_class.method(described_class.methods.first)).to be_a(Method)
11
+ end
12
+ end
13
+ end
14
+ end
15
+
6
16
  describe '#find_definition' do
7
17
  subject { described_class }
8
18
 
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe GrapeSwagger::DocMethods::FormatData do
6
+ let(:subject) { GrapeSwagger::DocMethods::FormatData }
7
+
8
+ [true, false].each do |array_use_braces|
9
+ context 'when param is nested in a param of array type' do
10
+ let(:braces) { array_use_braces ? '[]' : '' }
11
+ let(:params) do
12
+ [
13
+ { in: 'formData', name: "param1#{braces}", type: 'array', items: { type: 'string' } },
14
+ { in: 'formData', name: 'param1[param2]', type: 'string' }
15
+ ]
16
+ end
17
+
18
+ it 'skips root parameter' do
19
+ expect(subject.to_format(params).first).not_to have_key "param1#{braces}"
20
+ end
21
+
22
+ it 'Move array type to param2' do
23
+ expect(subject.to_format(params).first).to include(name: "param1#{braces}[param2]", type: 'array')
24
+ end
25
+ end
26
+ end
27
+
28
+ context 'when param is nested in a param of hash type' do
29
+ let(:params) { [{ in: 'formData', type: 'object', name: 'param1' }, { in: 'formData', name: 'param1[param2]', type: 'string' }] }
30
+
31
+ it 'skips root parameter' do
32
+ expect(subject.to_format(params).first).not_to have_key 'param1'
33
+ end
34
+
35
+ it 'Move array type to param2' do
36
+ expect(subject.to_format(params).first).to include(name: 'param1[param2]', type: 'string')
37
+ end
38
+ end
39
+
40
+ context 'when params contain a complex array' do
41
+ let(:params) do
42
+ [
43
+ { name: 'id', required: true, type: 'string' },
44
+ { name: 'description', required: false, type: 'string' },
45
+ { name: 'stuffs', required: true, type: 'array' },
46
+ { name: 'stuffs[id]', required: true, type: 'string' }
47
+ ]
48
+ end
49
+
50
+ let(:expected_params) do
51
+ [
52
+ { name: 'id', required: true, type: 'string' },
53
+ { name: 'description', required: false, type: 'string' },
54
+ { name: 'stuffs[id]', required: true, type: 'array', items: { type: 'string' } }
55
+ ]
56
+ end
57
+
58
+ it 'parses params correctly and adds array type all concerned elements' do
59
+ expect(subject.to_format(params)).to eq expected_params
60
+ end
61
+
62
+ context 'when array params are not contiguous with parent array' do
63
+ let(:params) do
64
+ [
65
+ { name: 'id', required: true, type: 'string' },
66
+ { name: 'description', required: false, type: 'string' },
67
+ { name: 'stuffs', required: true, type: 'array' },
68
+ { name: 'stuffs[owners]', required: true, type: 'array' },
69
+ { name: 'stuffs[creators]', required: true, type: 'array' },
70
+ { name: 'stuffs[owners][id]', required: true, type: 'string' },
71
+ { name: 'stuffs[creators][id]', required: true, type: 'string' },
72
+ { name: 'stuffs_and_things', required: true, type: 'string' }
73
+ ]
74
+ end
75
+
76
+ let(:expected_params) do
77
+ [
78
+ { name: 'id', required: true, type: 'string' },
79
+ { name: 'description', required: false, type: 'string' },
80
+ { name: 'stuffs[owners][id]', required: true, type: 'array', items: { type: 'string' } },
81
+ { name: 'stuffs[creators][id]', required: true, type: 'array', items: { type: 'string' } },
82
+ { name: 'stuffs_and_things', required: true, type: 'string' }
83
+ ]
84
+ end
85
+
86
+ it 'parses params correctly and adds array type all concerned elements' do
87
+ expect(subject.to_format(params)).to eq expected_params
88
+ end
89
+ end
90
+ end
91
+ end
@@ -40,13 +40,13 @@ describe GrapeSwagger::DocMethods::MoveParams do
40
40
  describe 'movable params' do
41
41
  specify 'allowed verbs' do
42
42
  allowed_verbs.each do |verb|
43
- expect(subject.can_be_moved?(movable_params, verb)).to be true
43
+ expect(subject.can_be_moved?(verb, movable_params)).to be true
44
44
  end
45
45
  end
46
46
 
47
47
  specify 'not allowed verbs' do
48
48
  not_allowed_verbs.each do |verb|
49
- expect(subject.can_be_moved?(movable_params, verb)).to be false
49
+ expect(subject.can_be_moved?(verb, movable_params)).to be false
50
50
  end
51
51
  end
52
52
  end
@@ -54,13 +54,13 @@ describe GrapeSwagger::DocMethods::MoveParams do
54
54
  describe 'not movable params' do
55
55
  specify 'allowed verbs' do
56
56
  allowed_verbs.each do |verb|
57
- expect(subject.can_be_moved?(not_movable_params, verb)).to be false
57
+ expect(subject.can_be_moved?(verb, not_movable_params)).to be false
58
58
  end
59
59
  end
60
60
 
61
61
  specify 'not allowed verbs' do
62
62
  not_allowed_verbs.each do |verb|
63
- expect(subject.can_be_moved?(not_movable_params, verb)).to be false
63
+ expect(subject.can_be_moved?(verb, not_movable_params)).to be false
64
64
  end
65
65
  end
66
66
  end
@@ -326,268 +326,6 @@ describe GrapeSwagger::DocMethods::MoveParams do
326
326
  end
327
327
  end
328
328
 
329
- describe 'prepare_nested_types' do
330
- before :each do
331
- subject.send(:prepare_nested_types, params)
332
- end
333
-
334
- let(:params) do
335
- [
336
- {
337
- in: 'body',
338
- name: 'address[street_lines]',
339
- description: 'street lines',
340
- type: 'array',
341
- items: {
342
- type: 'string'
343
- },
344
- required: true
345
- }
346
- ]
347
- end
348
-
349
- context 'when params contains nothing with :items key' do
350
- let(:params) do
351
- [
352
- {
353
- in: 'body',
354
- name: 'phone_number',
355
- description: 'phone number',
356
- type: 'string',
357
- required: true
358
- }
359
- ]
360
- end
361
-
362
- let(:expected_params) do
363
- [
364
- {
365
- in: 'body',
366
- name: 'phone_number',
367
- description: 'phone number',
368
- type: 'string',
369
- required: true
370
- }
371
- ]
372
- end
373
-
374
- it 'does nothing' do
375
- expect(params).to eq expected_params
376
- end
377
- end
378
-
379
- context 'when params contains :items key with array type' do
380
- let(:params) do
381
- [
382
- {
383
- in: 'body',
384
- name: 'address_street_lines',
385
- description: 'street lines',
386
- type: 'array',
387
- items: {
388
- type: 'array'
389
- },
390
- required: true
391
- }
392
- ]
393
- end
394
-
395
- let(:expected_params) do
396
- [
397
- {
398
- in: 'body',
399
- name: 'address_street_lines',
400
- description: 'street lines',
401
- type: 'string',
402
- required: true
403
- }
404
- ]
405
- end
406
-
407
- it 'sets type to string and removes :items' do
408
- expect(params).to eq expected_params
409
- end
410
- end
411
-
412
- context 'when params contains :items key with $ref' do
413
- let(:params) do
414
- [
415
- {
416
- in: 'body',
417
- name: 'address_street_lines',
418
- description: 'street lines',
419
- type: 'array',
420
- items: {
421
- '$ref' => '#/definitions/StreetLine'
422
- },
423
- required: true
424
- }
425
- ]
426
- end
427
-
428
- let(:expected_params) do
429
- [
430
- {
431
- in: 'body',
432
- name: 'address_street_lines',
433
- description: 'street lines',
434
- type: 'object',
435
- items: {
436
- '$ref' => '#/definitions/StreetLine'
437
- },
438
- required: true
439
- }
440
- ]
441
- end
442
-
443
- it 'sets type to object and does not remove :items' do
444
- expect(params).to eq expected_params
445
- end
446
- end
447
-
448
- context 'when params contains :items without $ref or array type' do
449
- let(:params) do
450
- [
451
- {
452
- in: 'body',
453
- name: 'address_street_lines',
454
- description: 'street lines',
455
- type: 'array',
456
- items: {
457
- type: 'string'
458
- },
459
- required: true
460
- }
461
- ]
462
- end
463
-
464
- let(:expected_params) do
465
- [
466
- {
467
- in: 'body',
468
- name: 'address_street_lines',
469
- description: 'street lines',
470
- type: 'string',
471
- required: true
472
- }
473
- ]
474
- end
475
-
476
- it 'sets type to :items :type and removes :items' do
477
- expect(params).to eq expected_params
478
- end
479
- end
480
-
481
- context 'when params contains :items key with :format' do
482
- let(:params) do
483
- [
484
- {
485
- in: 'body',
486
- name: 'street_number',
487
- description: 'street number',
488
- type: 'array',
489
- items: {
490
- type: 'integer',
491
- format: 'int32'
492
- },
493
- required: true
494
- }
495
- ]
496
- end
497
-
498
- let(:expected_params) do
499
- [
500
- {
501
- in: 'body',
502
- name: 'street_number',
503
- description: 'street number',
504
- type: 'integer',
505
- format: 'int32',
506
- required: true
507
- }
508
- ]
509
- end
510
-
511
- it 'sets format and removes :items' do
512
- expect(params).to eq expected_params
513
- end
514
- end
515
- end
516
-
517
- describe 'recursive_call' do
518
- before :each do
519
- subject.send(:recursive_call, properties, 'test', nested_params)
520
- end
521
-
522
- let(:properties) { {} }
523
-
524
- context 'when nested params is an array' do
525
- let(:nested_params) do
526
- [
527
- {
528
- in: 'body',
529
- name: 'aliases',
530
- description: 'The aliases of test.',
531
- type: 'array',
532
- items: { type: 'string' },
533
- required: true
534
- }
535
- ]
536
- end
537
-
538
- let(:expected_properties) do
539
- {
540
- type: 'array',
541
- items: {
542
- type: 'object',
543
- properties: {
544
- aliases: {
545
- type: 'string',
546
- description: 'The aliases of test.'
547
- }
548
- },
549
- required: [:aliases]
550
- }
551
- }
552
- end
553
-
554
- it 'adds property as symbol with array type and items' do
555
- expect(properties[:test]).to eq expected_properties
556
- end
557
- end
558
-
559
- context 'when nested params is not an array' do
560
- let(:nested_params) do
561
- [
562
- {
563
- in: 'body',
564
- name: 'id',
565
- description: 'The unique ID of test.',
566
- type: 'string',
567
- required: true
568
- }
569
- ]
570
- end
571
-
572
- let(:expected_properties) do
573
- {
574
- type: 'object',
575
- required: [:id],
576
- properties: {
577
- id: {
578
- type: 'string',
579
- description: 'The unique ID of test.'
580
- }
581
- }
582
- }
583
- end
584
-
585
- it 'adds property as symbol with object type' do
586
- expect(properties[:test]).to eq expected_properties
587
- end
588
- end
589
- end
590
-
591
329
  describe 'add_properties_to_definition' do
592
330
  before :each do
593
331
  subject.send(:add_properties_to_definition, definition, properties, [])
@@ -39,7 +39,6 @@ describe GrapeSwagger::DocMethods::OptionalObject do
39
39
  let(:options) do
40
40
  { host: proc { |request| request.host =~ /^example/ ? '/api-example' : '/api' } }
41
41
  end
42
- # rubocop:enable RegexpMatch
43
42
  specify do
44
43
  expect(subject.build(key, options, request)).to eql '/api-example'
45
44
  end
data/spec/spec_helper.rb CHANGED
@@ -19,7 +19,7 @@ MODEL_PARSER = ENV.key?('MODEL_PARSER') ? ENV['MODEL_PARSER'].to_s.downcase.sub(
19
19
  require 'grape'
20
20
  require 'grape-swagger'
21
21
 
22
- Dir[File.join(Dir.getwd, 'spec/support/*.rb')].each { |f| require f }
22
+ Dir[File.join(Dir.getwd, 'spec/support/*.rb')].sort.each { |f| require f }
23
23
  require "grape-swagger/#{MODEL_PARSER}" if MODEL_PARSER != 'mock'
24
24
  require File.join(Dir.getwd, "spec/support/model_parsers/#{MODEL_PARSER}_parser.rb")
25
25
 
@@ -10,7 +10,9 @@ describe 'document hash and array' do
10
10
  class TestApi < Grape::API
11
11
  format :json
12
12
 
13
- documentation = ::Entities::DocumentedHashAndArrayModel.documentation if ::Entities::DocumentedHashAndArrayModel.respond_to?(:documentation)
13
+ if ::Entities::DocumentedHashAndArrayModel.respond_to?(:documentation)
14
+ documentation = ::Entities::DocumentedHashAndArrayModel.documentation
15
+ end
14
16
 
15
17
  desc 'This returns something'
16
18
  namespace :arbitrary do
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'response with root' do
6
+ include_context "#{MODEL_PARSER} swagger example"
7
+
8
+ before :all do
9
+ module TheApi
10
+ class ResponseApiWithRoot < Grape::API
11
+ format :json
12
+
13
+ desc 'This returns something',
14
+ http_codes: [{ code: 200, model: Entities::Something }]
15
+ get '/ordinary_response' do
16
+ { 'declared_params' => declared(params) }
17
+ end
18
+
19
+ desc 'This returns something',
20
+ is_array: true,
21
+ http_codes: [{ code: 200, model: Entities::Something }]
22
+ get '/response_with_array' do
23
+ { 'declared_params' => declared(params) }
24
+ end
25
+
26
+ route_setting :swagger, root: true
27
+ desc 'This returns something',
28
+ http_codes: [{ code: 200, model: Entities::Something }]
29
+ get '/response_with_root' do
30
+ { 'declared_params' => declared(params) }
31
+ end
32
+
33
+ route_setting :swagger, root: true
34
+ desc 'This returns underscored root',
35
+ http_codes: [{ code: 200, model: Entities::ApiError }]
36
+ get '/response_with_root_underscore' do
37
+ { 'declared_params' => declared(params) }
38
+ end
39
+
40
+ route_setting :swagger, root: true
41
+ desc 'This returns something',
42
+ is_array: true,
43
+ http_codes: [{ code: 200, model: Entities::Something }]
44
+ get '/response_with_array_and_root' do
45
+ { 'declared_params' => declared(params) }
46
+ end
47
+
48
+ route_setting :swagger, root: 'custom_root'
49
+ desc 'This returns something',
50
+ http_codes: [{ code: 200, model: Entities::Something }]
51
+ get '/response_with_custom_root' do
52
+ { 'declared_params' => declared(params) }
53
+ end
54
+
55
+ add_swagger_documentation
56
+ end
57
+ end
58
+ end
59
+
60
+ def app
61
+ TheApi::ResponseApiWithRoot
62
+ end
63
+
64
+ describe 'GET /ordinary_response' do
65
+ subject do
66
+ get '/swagger_doc/ordinary_response'
67
+ JSON.parse(last_response.body)
68
+ end
69
+
70
+ it 'does not add root or array' do
71
+ schema = subject.dig('paths', '/ordinary_response', 'get', 'responses', '200', 'schema')
72
+ expect(schema).to eq(
73
+ '$ref' => '#/definitions/Something'
74
+ )
75
+ end
76
+ end
77
+
78
+ describe 'GET /response_with_array' do
79
+ subject do
80
+ get '/swagger_doc/response_with_array'
81
+ JSON.parse(last_response.body)
82
+ end
83
+
84
+ it 'adds array to the response' do
85
+ schema = subject.dig('paths', '/response_with_array', 'get', 'responses', '200', 'schema')
86
+ expect(schema).to eq(
87
+ 'type' => 'array', 'items' => { '$ref' => '#/definitions/Something' }
88
+ )
89
+ end
90
+ end
91
+
92
+ describe 'GET /response_with_root' do
93
+ subject do
94
+ get '/swagger_doc/response_with_root'
95
+ JSON.parse(last_response.body)
96
+ end
97
+
98
+ it 'adds root to the response' do
99
+ schema = subject.dig('paths', '/response_with_root', 'get', 'responses', '200', 'schema')
100
+ expect(schema).to eq(
101
+ 'type' => 'object',
102
+ 'properties' => { 'something' => { '$ref' => '#/definitions/Something' } }
103
+ )
104
+ end
105
+ end
106
+
107
+ describe 'GET /response_with_root_underscore' do
108
+ subject do
109
+ get '/swagger_doc/response_with_root_underscore'
110
+ JSON.parse(last_response.body)
111
+ end
112
+
113
+ it 'adds root to the response' do
114
+ schema = subject.dig('paths', '/response_with_root_underscore', 'get', 'responses', '200', 'schema')
115
+ expect(schema).to eq(
116
+ 'type' => 'object',
117
+ 'properties' => { 'api_error' => { '$ref' => '#/definitions/ApiError' } }
118
+ )
119
+ end
120
+ end
121
+
122
+ describe 'GET /response_with_array_and_root' do
123
+ subject do
124
+ get '/swagger_doc/response_with_array_and_root'
125
+ JSON.parse(last_response.body)
126
+ end
127
+
128
+ it 'adds root and array to the response' do
129
+ schema = subject.dig('paths', '/response_with_array_and_root', 'get', 'responses', '200', 'schema')
130
+ expect(schema).to eq(
131
+ 'type' => 'object',
132
+ 'properties' => {
133
+ 'somethings' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/Something' } }
134
+ }
135
+ )
136
+ end
137
+ end
138
+
139
+ describe 'GET /response_with_custom_root' do
140
+ subject do
141
+ get '/swagger_doc/response_with_custom_root'
142
+ JSON.parse(last_response.body)
143
+ end
144
+
145
+ it 'adds root to the response' do
146
+ schema = subject.dig('paths', '/response_with_custom_root', 'get', 'responses', '200', 'schema')
147
+ expect(schema).to eq(
148
+ 'type' => 'object',
149
+ 'properties' => { 'custom_root' => { '$ref' => '#/definitions/Something' } }
150
+ )
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'has no description, if details or description are nil' do
6
+ include_context "#{MODEL_PARSER} swagger example"
7
+
8
+ before :all do
9
+ module TheApi
10
+ class GfmRcDetailApi < Grape::API
11
+ format :json
12
+
13
+ desc nil,
14
+ detail: nil,
15
+ entity: Entities::UseResponse,
16
+ failure: [{ code: 400, model: Entities::ApiError }]
17
+ get '/use_gfm_rc_detail' do
18
+ { 'declared_params' => declared(params) }
19
+ end
20
+
21
+ add_swagger_documentation
22
+ end
23
+ end
24
+ end
25
+
26
+ def app
27
+ TheApi::GfmRcDetailApi
28
+ end
29
+
30
+ subject do
31
+ get '/swagger_doc'
32
+ JSON.parse(last_response.body)
33
+ end
34
+
35
+ specify do
36
+ expect(subject['paths']['/use_gfm_rc_detail']['get']).not_to include('description')
37
+ expect(subject['paths']['/use_gfm_rc_detail']['get']['description']).to eql(nil)
38
+ end
39
+ end
@@ -52,6 +52,39 @@ describe 'Grape::Endpoint#path_and_definitions' do
52
52
  expect(subject.first['/v1/item'][:get][:tags]).to eq ['special-item']
53
53
  end
54
54
  end
55
+
56
+ context 'when parameter with a custom type is specified' do
57
+ let(:item) do
58
+ Class.new(Grape::API) do
59
+ Color = Struct.new(:value) do
60
+ def self.parse(value)
61
+ new(value: value)
62
+ end
63
+ end
64
+
65
+ class ColorEntity < Grape::Entity
66
+ expose :value
67
+ end
68
+
69
+ version 'v1', using: :path
70
+
71
+ resource :item do
72
+ params do
73
+ requires :root, type: Hash do
74
+ optional :color, type: Color, documentation: { type: ColorEntity }
75
+ end
76
+ end
77
+ post '/'
78
+ end
79
+ end
80
+ end
81
+
82
+ it 'creates a reference to the model instead of using the non-existent type' do
83
+ color = subject.dig(1, 'postV1Item', :properties, :root, :properties, :color)
84
+ expect(color).not_to eq(type: 'ColorEntity')
85
+ expect(color).to eq('$ref' => '#/definitions/ColorEntity')
86
+ end
87
+ end
55
88
  end
56
89
 
57
90
  context 'when mounting an API more than once', if: GrapeVersion.satisfy?('>= 1.2.0') do
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe 'docs mounted separately from api' do
5
+ xdescribe 'docs mounted separately from api' do
6
6
  before :all do
7
7
  class ActualApi < Grape::API
8
8
  desc 'Document root'