swaggable 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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