swaggable 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swaggable.rb +32 -7
  3. data/lib/swaggable/api_validator.rb +42 -0
  4. data/lib/swaggable/check_body_schema.rb +72 -0
  5. data/lib/swaggable/check_expected_parameters.rb +26 -0
  6. data/lib/swaggable/check_mandatory_parameters.rb +26 -0
  7. data/lib/swaggable/check_request_content_type.rb +26 -0
  8. data/lib/swaggable/check_response_code.rb +24 -0
  9. data/lib/swaggable/check_response_content_type.rb +26 -0
  10. data/lib/swaggable/endpoint_definition.rb +30 -2
  11. data/lib/swaggable/endpoint_validator.rb +27 -0
  12. data/lib/swaggable/errors.rb +9 -0
  13. data/lib/swaggable/errors/validations_collection.rb +44 -0
  14. data/lib/swaggable/grape_adapter.rb +2 -2
  15. data/lib/swaggable/mime_type_definition.rb +73 -0
  16. data/lib/swaggable/mime_types_collection.rb +60 -0
  17. data/lib/swaggable/parameter_definition.rb +19 -0
  18. data/lib/swaggable/query_params.rb +50 -0
  19. data/lib/swaggable/rack_request_adapter.rb +88 -0
  20. data/lib/swaggable/rack_response_adapter.rb +49 -0
  21. data/lib/swaggable/swagger_2_serializer.rb +3 -3
  22. data/lib/swaggable/validating_rack_app.rb +30 -0
  23. data/lib/swaggable/version.rb +1 -1
  24. data/spec/{swaggable/integration_spec.rb → integration/dsl_spec.rb} +0 -40
  25. data/spec/integration/rack_app_spec.rb +44 -0
  26. data/spec/integration/validating_rack_app_spec.rb +50 -0
  27. data/spec/spec_helper.rb +9 -5
  28. data/spec/swaggable/api_validator_spec.rb +61 -0
  29. data/spec/swaggable/check_body_schema_spec.rb +94 -0
  30. data/spec/swaggable/check_expected_parameters_spec.rb +110 -0
  31. data/spec/swaggable/check_mandatory_parameters_spec.rb +110 -0
  32. data/spec/swaggable/check_request_content_type_spec.rb +51 -0
  33. data/spec/swaggable/check_response_code_spec.rb +37 -0
  34. data/spec/swaggable/check_response_content_type_spec.rb +51 -0
  35. data/spec/swaggable/endpoint_definition_spec.rb +47 -0
  36. data/spec/swaggable/endpoint_validator_spec.rb +102 -0
  37. data/spec/swaggable/errors/validations_collection_spec.rb +78 -0
  38. data/spec/swaggable/grape_adapter_spec.rb +2 -2
  39. data/spec/swaggable/mime_type_definition_spec.rb +96 -0
  40. data/spec/swaggable/mime_types_collection_spec.rb +83 -0
  41. data/spec/swaggable/parameter_definition_spec.rb +23 -0
  42. data/spec/swaggable/query_params_spec.rb +37 -0
  43. data/spec/swaggable/rack_request_adapter_spec.rb +89 -0
  44. data/spec/swaggable/rack_response_adapter_spec.rb +46 -0
  45. data/spec/swaggable/swagger_2_serializer_spec.rb +4 -2
  46. data/spec/swaggable/swagger_2_validator_spec.rb +2 -2
  47. data/spec/swaggable/validating_rack_app_spec.rb +91 -0
  48. data/swaggable.gemspec +1 -0
  49. metadata +68 -4
@@ -0,0 +1,44 @@
1
+ require_relative '../spec_helper'
2
+ require 'rack/test'
3
+ require 'grape'
4
+
5
+ RSpec.describe 'Integration' do
6
+ context 'RackApp' do
7
+ include Rack::Test::Methods
8
+
9
+ def app
10
+ subject
11
+ end
12
+
13
+ subject { rack_app }
14
+ let(:rack_app) { Swaggable::RackApp.new(api_definition: api_def) }
15
+ let(:api_def) { Swaggable::ApiDefinition.from_grape_api(grape_api) }
16
+
17
+ let(:grape_api) do
18
+ Class.new(Grape::API).tap do |g|
19
+ g.params do
20
+ requires :user_uuid
21
+ end
22
+
23
+ g.get '/' do
24
+ status 200
25
+ body({some: 'body'})
26
+ end
27
+
28
+ end.tap do |grape|
29
+ stub_const('MyGrapeApi', grape)
30
+ end
31
+ end
32
+
33
+ it 'has status 200 OK' do
34
+ get '/'
35
+ expect(last_response.status).to eq 200
36
+ expect(last_response.headers['Content-Type']).to eq 'application/json'
37
+ end
38
+
39
+ it 'validates' do
40
+ expect(subject.validate!).to be true
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,50 @@
1
+ require_relative '../spec_helper'
2
+ require 'rack/test'
3
+ require 'grape'
4
+
5
+ RSpec.describe 'Integration' do
6
+ describe 'ValidatorRackApp' do
7
+ include Rack::Test::Methods
8
+
9
+ def app_to_validate
10
+ @app_to_validate ||= -> env { [200, {}, []] }
11
+ end
12
+
13
+ def app
14
+ Swaggable::ValidatingRackApp.new app: app_to_validate, definition: definition
15
+ end
16
+
17
+ let :definition do
18
+ Swaggable::ApiDefinition.new do
19
+ endpoints.add_new do
20
+ path '/existing_endpoint'
21
+ verb :get
22
+
23
+ responses.add_new do
24
+ status 200
25
+ description 'Success'
26
+ end
27
+
28
+ consumes << :json
29
+ produces << :json
30
+ end
31
+ end
32
+ end
33
+
34
+ it 'does nothing if the validations pass' do
35
+ expect{ get '/existing_endpoint', {}, 'CONTENT_TYPE' => 'application/json' }.
36
+ not_to raise_error
37
+ end
38
+
39
+ it 'raises an exception if request validation doesn\'t pass' do
40
+ expect{ get '/existing_endpoint', {}, 'CONTENT_TYPE' => 'application/xml' }.
41
+ to raise_error Swaggable::Errors::Validation
42
+ end
43
+
44
+ it 'raises an exception if response validation doesn\'t pass' do
45
+ @app_to_validate = -> env { [418, {'Content-Type' => 'application/xml'}, ['']] }
46
+ expect{ get '/existing_endpoint' }.to raise_error Swaggable::Errors::Validation
47
+ end
48
+ end
49
+ end
50
+
@@ -4,11 +4,15 @@ require 'pry'
4
4
  require 'webmock/rspec'
5
5
 
6
6
  RSpec.configure do |config|
7
- config.color = true
8
- config.tty = true
9
- config.disable_monkey_patching!
10
- # config.full_backtrace = true
11
- # config.formatter = :documentation # :documentation, :progress, :html, :textmate
7
+ config.color = true
8
+ config.tty = true
9
+ config.disable_monkey_patching!
10
+ # config.full_backtrace = true
11
+ # config.formatter = :documentation # :documentation, :progress, :html, :textmate
12
+
13
+ config.mock_with :rspec do |mocks|
14
+ mocks.verify_partial_doubles = true
15
+ end
12
16
  end
13
17
 
14
18
  WebMock.disable_net_connect!(:allow => "codeclimate.com")
@@ -0,0 +1,61 @@
1
+ require_relative '../spec_helper'
2
+ require 'rack'
3
+
4
+ RSpec.describe 'Swaggable::ApiValidator' do
5
+ subject { subject_instance }
6
+ let(:subject_instance) { subject_class.new definition: api_definition, request: api_request }
7
+ let(:subject_class) { Swaggable::ApiValidator }
8
+ let(:api_request) { request :get, '/existing_endpoint' }
9
+ let(:api_response) { [200, {}, []] }
10
+
11
+ let :api_definition do
12
+ Swaggable::ApiDefinition.new do
13
+ endpoints.add_new do
14
+ path '/existing_endpoint'
15
+ verb :get
16
+ end
17
+ end
18
+ end
19
+
20
+ def request verb, path
21
+ Rack::MockRequest.env_for path, 'REQUEST_METHOD' => verb
22
+ end
23
+
24
+ describe '#endpoint_validator' do
25
+ it 'has the right endpoint' do
26
+ expect(subject.endpoint_validator.endpoint).to be subject.endpoint
27
+ end
28
+
29
+ it 'raises an exception if no endpoint was found' do
30
+ api_definition.endpoints.first.path '/another_path'
31
+ expect{ subject.endpoint_validator }.
32
+ to raise_error(Swaggable::Errors::EndpointNotFound)
33
+ end
34
+ end
35
+
36
+ describe '#errors_for_request()' do
37
+ it 'delegates to the endpoint_validator' do
38
+ errors = double('errors')
39
+
40
+ allow(subject.endpoint_validator).
41
+ to receive(:errors_for_request).
42
+ with(api_request).
43
+ and_return(errors)
44
+
45
+ expect(subject.errors_for_request).to be errors
46
+ end
47
+ end
48
+
49
+ describe '#errors_for_response(response)' do
50
+ it 'delegates to the endpoint_validator' do
51
+ errors = double('errors')
52
+
53
+ allow(subject.endpoint_validator).
54
+ to receive(:errors_for_response).
55
+ with(api_response).
56
+ and_return(errors)
57
+
58
+ expect(subject.errors_for_response api_response).to be errors
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,94 @@
1
+ require_relative '../spec_helper'
2
+ require 'rack'
3
+
4
+ RSpec.describe 'Swaggable::CheckBodySchema' do
5
+ subject { subject_instance }
6
+ let(:subject_instance) { subject_class.new }
7
+ let(:subject_class) { Swaggable::CheckBodySchema }
8
+
9
+
10
+ let(:request) do
11
+ Swaggable::RackRequestAdapter.new Rack::MockRequest.env_for('/', {
12
+ 'REQUEST_METHOD' => 'POST',
13
+ 'CONTENT_TYPE' => content_type,
14
+ :input => body,
15
+ })
16
+ end
17
+
18
+ let(:verb) { 'POST' }
19
+ let(:content_type) { nil }
20
+ let(:body) { nil }
21
+
22
+ let(:endpoint) { Swaggable::EndpointDefinition.new }
23
+ let(:body_definition) { endpoint.parameters.add_new { location :body } }
24
+
25
+ def do_run
26
+ subject_class.call endpoint: endpoint, request: request
27
+ end
28
+
29
+ describe '.call' do
30
+ context 'present and there is no schema' do
31
+ let(:body) { '{}' }
32
+ before { body_definition.required true }
33
+
34
+ it 'returns no error' do
35
+ expect(do_run).to be_empty
36
+ end
37
+ end
38
+
39
+ context 'present and matches schema' do
40
+ let(:content_type) { 'application/json' }
41
+ let(:body) { '{"name":"John"}' }
42
+
43
+ before do
44
+ body_definition.schema.attributes.add_new { name 'name'; type :string }
45
+ end
46
+
47
+ it 'returns no error' do
48
+ expect(do_run).to be_empty
49
+ end
50
+ end
51
+
52
+ context 'when required but not present' do
53
+ before { body_definition.required true }
54
+
55
+ it 'returns error' do
56
+ errors = do_run
57
+ expect(errors.count).to eq 1
58
+ expect(errors.first.message).to match /body/
59
+ end
60
+ end
61
+
62
+ context 'when present but missing required attribute' do
63
+ let(:content_type) { 'application/json' }
64
+ let(:body) { '{}' }
65
+
66
+ before do
67
+ body_definition.required true
68
+ body_definition.schema.attributes.add_new { name 'name'; type :string; required true }
69
+ end
70
+
71
+ it 'returns error' do
72
+ errors = do_run
73
+ expect(errors.count).to eq 1
74
+ expect(errors.first.message).to match /body/
75
+ end
76
+ end
77
+
78
+ context 'when present but missing unexpected attribute' do
79
+ let(:content_type) { 'application/json' }
80
+ let(:body) { '{"made_up_attribute":42}' }
81
+
82
+ before do
83
+ body_definition.required true
84
+ body_definition.schema.attributes.add_new { name 'name'; type :string }
85
+ end
86
+
87
+ it 'returns error' do
88
+ errors = do_run
89
+ expect(errors.count).to eq 1
90
+ expect(errors.first.message).to match /body/
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,110 @@
1
+ require_relative '../spec_helper'
2
+ require 'rack'
3
+
4
+ RSpec.describe 'Swaggable::CheckExpectedParameters' do
5
+ subject { subject_instance }
6
+ let(:subject_instance) { subject_class.new }
7
+ let(:subject_class) { Swaggable::CheckExpectedParameters }
8
+
9
+ let(:parameters) { endpoint.parameters }
10
+ let(:endpoint) { Swaggable::EndpointDefinition.new { path '/' } }
11
+ let(:request) { Swaggable::RackRequestAdapter.new Rack::MockRequest.env_for("#{path}?#{query_string}") }
12
+ let(:path) { '/' }
13
+ let(:query_string) { '' }
14
+
15
+ def do_run
16
+ subject_class.call endpoint: endpoint, request: request
17
+ end
18
+
19
+ describe '.call' do
20
+ context 'present parameter is expected' do
21
+ it 'returns an empty errors list' do
22
+ expect(do_run).to be_empty
23
+ end
24
+ end
25
+
26
+ context 'present parameter is unexpected' do
27
+ let(:query_string) { 'name=John' }
28
+
29
+ it 'returns an empty errors list' do
30
+ errors = do_run
31
+ expect(errors.count).to eq 1
32
+ expect(errors.first.message).to match /name/
33
+ end
34
+ end
35
+
36
+ # context 'all mandatory parameters are present' do
37
+ # before do
38
+ # parameters.add_new { name :email; required true }
39
+ # request.query_parameters[:email] = 'user@example.com'
40
+ # end
41
+
42
+ # it 'returns no errors' do
43
+ # expect(do_run).to be_empty
44
+ # end
45
+ # end
46
+
47
+ # context 'missing mandatory parameters undefined location' do
48
+ # before do
49
+ # parameters.add_new { name :email; required true }
50
+ # end
51
+
52
+ # it 'returns an error per missing parameter' do
53
+ # errors = do_run
54
+ # expect(errors.count).to eq 1
55
+ # expect(errors.first.message).to match /email/
56
+ # end
57
+ # end
58
+
59
+ # context 'missing mandatory query param' do
60
+ # before do
61
+ # parameters.add_new { name :email; required true; location :query }
62
+ # end
63
+
64
+ # it 'returns an error per missing parameter' do
65
+ # errors = do_run
66
+ # expect(errors.count).to eq 1
67
+ # expect(errors.first.message).to match /email/
68
+ # end
69
+ # end
70
+
71
+ # context 'missing mandatory path param' do
72
+ # before do
73
+ # parameters.add_new { name 'user_id'; required true; location :path }
74
+ # end
75
+
76
+ # it 'returns an error per missing parameter' do
77
+ # errors = do_run
78
+ # expect(errors.count).to eq 1
79
+ # expect(errors.first.message).to match /user_id/
80
+ # end
81
+ # end
82
+
83
+ # context 'present mandatory path param' do
84
+ # let(:path) { '/users/37.json' }
85
+
86
+ # before do
87
+ # endpoint.path '/users/{user_id}.json'
88
+ # parameters.add_new { name 'user_id'; required true; location :path }
89
+ # end
90
+
91
+ # it 'no errors' do
92
+ # expect(do_run).to be_empty
93
+ # end
94
+ # end
95
+
96
+ # context 'missing non-mandatory parameters' do
97
+ # before do
98
+ # parameters.add_new { name :email; required false }
99
+ # end
100
+
101
+ # it 'returns no errors' do
102
+ # expect(do_run).to be_empty
103
+ # end
104
+ # end
105
+
106
+ # # TODO
107
+ # describe 'header parameter'
108
+ # describe 'form parameter'
109
+ end
110
+ end
@@ -0,0 +1,110 @@
1
+ require_relative '../spec_helper'
2
+ require 'rack'
3
+
4
+ RSpec.describe 'Swaggable::CheckMandatoryParameters' do
5
+ subject { subject_instance }
6
+ let(:subject_instance) { subject_class.new }
7
+ let(:subject_class) { Swaggable::CheckMandatoryParameters }
8
+
9
+ let(:parameters) { endpoint.parameters }
10
+ let(:endpoint) { Swaggable::EndpointDefinition.new { path '/' } }
11
+
12
+ let(:request) do
13
+ Swaggable::RackRequestAdapter.new Rack::MockRequest.env_for(path, {
14
+ 'REQUEST_METHOD' => verb,
15
+ 'CONTENT_TYPE' => content_type,
16
+ :input => body,
17
+ })
18
+ end
19
+
20
+ let(:path) { '/' }
21
+ let(:verb) { 'GET' }
22
+ let(:content_type) { nil }
23
+ let(:body) { nil }
24
+
25
+ def do_run
26
+ subject_class.call endpoint: endpoint, request: request
27
+ end
28
+
29
+ describe '.call' do
30
+ context 'no mandatory parameters' do
31
+ it 'returns an empty errors list' do
32
+ expect(do_run).to be_empty
33
+ end
34
+ end
35
+
36
+ context 'all mandatory parameters are present' do
37
+ before do
38
+ parameters.add_new { name :email; required true; location :query }
39
+ request.query_parameters[:email] = 'user@example.com'
40
+ end
41
+
42
+ it 'returns no errors' do
43
+ expect(do_run).to be_empty
44
+ end
45
+ end
46
+
47
+ context 'missing mandatory parameters undefined location' do
48
+ before do
49
+ parameters.add_new { name :email; required true }
50
+ end
51
+
52
+ it 'returns an error per missing parameter' do
53
+ errors = do_run
54
+ expect(errors.count).to eq 1
55
+ expect(errors.first.message).to match /email/
56
+ end
57
+ end
58
+
59
+ context 'missing mandatory query param' do
60
+ before do
61
+ parameters.add_new { name :email; required true; location :query }
62
+ end
63
+
64
+ it 'returns an error per missing parameter' do
65
+ errors = do_run
66
+ expect(errors.count).to eq 1
67
+ expect(errors.first.message).to match /email/
68
+ end
69
+ end
70
+
71
+ context 'missing mandatory path param' do
72
+ before do
73
+ parameters.add_new { name 'user_id'; required true; location :path }
74
+ end
75
+
76
+ it 'returns an error per missing parameter' do
77
+ errors = do_run
78
+ expect(errors.count).to eq 1
79
+ expect(errors.first.message).to match /user_id/
80
+ end
81
+ end
82
+
83
+ context 'present mandatory path param' do
84
+ let(:path) { '/users/37.json' }
85
+
86
+ before do
87
+ endpoint.path '/users/{user_id}.json'
88
+ parameters.add_new { name 'user_id'; required true; location :path }
89
+ end
90
+
91
+ it 'no errors' do
92
+ expect(do_run).to be_empty
93
+ end
94
+ end
95
+
96
+ context 'missing non-mandatory parameters' do
97
+ before do
98
+ parameters.add_new { name :email; required false }
99
+ end
100
+
101
+ it 'returns no errors' do
102
+ expect(do_run).to be_empty
103
+ end
104
+ end
105
+
106
+ # TODO
107
+ describe 'header parameter'
108
+ describe 'form parameter'
109
+ end
110
+ end