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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bf18bc08944dd205e9633c7e85a69ac1e0fe9908
4
- data.tar.gz: 10a0e3fb8a88842000cd28b29838609106b7c7a4
3
+ metadata.gz: 808c9b125e0ebb98bf9307f3922375344f073e2d
4
+ data.tar.gz: e50cd856a8362d1f15e7e95cf70c5307b31c264b
5
5
  SHA512:
6
- metadata.gz: d5e0e8fdd32d892ffe06e0b5d801db1d7286a9e67dd64d3edbbc339636b8e3ab652d8cbf5b12ace2e9e9587feb288bbe93dee5089a4e7590939cb79514c68a03
7
- data.tar.gz: 42f21abe51eb383ff37bbdc8c018d19f2246a2f78d59a9b66fb045d9a48c3d6f93283a1e5799c2c0d55aae4fd413179c41e47de9083ed201a2b8c49a60c3ef56
6
+ metadata.gz: eeb0d8a2698ff01a16bd477f7a43d7ad6d52dabb191cd8f366e341bd7ba46cd444fa58bdcaf221962f3fd95174d6d44cff5eff7d67222b0795371f748f9b21d3
7
+ data.tar.gz: 8f5d5d91cb25766ebe780ee784b9a49599439b5eddda1343fe507d22f42972715bd6c91c1c892ea3e3663230335fe99100480f468a1734b0cc374259e72edb79
@@ -1,20 +1,45 @@
1
1
  require 'swaggable/version'
2
2
 
3
3
  module Swaggable
4
+ # Rack
5
+ autoload :RackApp, 'swaggable/rack_app'
6
+ autoload :RackRedirect, 'swaggable/rack_redirect'
7
+ autoload :ValidatingRackApp, 'swaggable/validating_rack_app'
8
+ autoload :RackRequestAdapter, 'swaggable/rack_request_adapter'
9
+ autoload :RackResponseAdapter, 'swaggable/rack_response_adapter'
10
+
11
+ # Definitions
12
+ autoload :DefinitionBase, 'swaggable/definition_base'
4
13
  autoload :ApiDefinition, 'swaggable/api_definition'
5
14
  autoload :EndpointDefinition, 'swaggable/endpoint_definition'
6
- autoload :ParameterDefinition, 'swaggable/parameter_definition'
7
15
  autoload :TagDefinition, 'swaggable/tag_definition'
16
+ autoload :ParameterDefinition, 'swaggable/parameter_definition'
8
17
  autoload :ResponseDefinition, 'swaggable/response_definition'
9
- autoload :RackApp, 'swaggable/rack_app'
18
+ autoload :MimeTypeDefinition, 'swaggable/mime_type_definition'
19
+ autoload :MimeTypesCollection, 'swaggable/mime_types_collection'
20
+ autoload :SchemaDefinition, 'swaggable/schema_definition'
21
+ autoload :AttributeDefinition, 'swaggable/attribute_definition'
22
+
23
+ # Grape
10
24
  autoload :GrapeAdapter, 'swaggable/grape_adapter'
11
25
  autoload :GrapeEntityTranslator, 'swaggable/grape_entity_translator'
26
+
27
+ # Swagger
12
28
  autoload :Swagger2Serializer, 'swaggable/swagger_2_serializer'
13
29
  autoload :Swagger2Validator, 'swaggable/swagger_2_validator'
14
- autoload :EndpointIndex, 'swaggable/endpoint_index'
15
- autoload :DefinitionBase, 'swaggable/definition_base'
30
+
31
+ # Validators
32
+ autoload :ApiValidator, 'swaggable/api_validator'
33
+ autoload :EndpointValidator, 'swaggable/endpoint_validator'
34
+ autoload :CheckRequestContentType, 'swaggable/check_request_content_type'
35
+ autoload :CheckMandatoryParameters, 'swaggable/check_mandatory_parameters'
36
+ autoload :CheckExpectedParameters, 'swaggable/check_expected_parameters'
37
+ autoload :CheckBodySchema, 'swaggable/check_body_schema'
38
+ autoload :CheckResponseContentType, 'swaggable/check_response_content_type'
39
+ autoload :CheckResponseCode, 'swaggable/check_response_code'
40
+
41
+ # Others
42
+ autoload :Errors, 'swaggable/errors'
16
43
  autoload :EnumerableAttributes, 'swaggable/enumerable_attributes'
17
- autoload :SchemaDefinition, 'swaggable/schema_definition'
18
- autoload :AttributeDefinition, 'swaggable/attribute_definition'
19
- autoload :RackRedirect, 'swaggable/rack_redirect'
44
+ autoload :QueryParams, 'swaggable/query_params'
20
45
  end
@@ -0,0 +1,42 @@
1
+ module Swaggable
2
+ class ApiValidator
3
+ attr_accessor(
4
+ :definition,
5
+ :request,
6
+ )
7
+
8
+ def initialize opts = {}
9
+ @definition, @request = opts.values_at(:definition, :request)
10
+ end
11
+
12
+ def errors_for_request
13
+ endpoint_validator.errors_for_request request
14
+ end
15
+
16
+ def errors_for_response response
17
+ endpoint_validator.errors_for_response response
18
+ end
19
+
20
+ def endpoint
21
+ definition.endpoints.detect do |e|
22
+ e.match? *request_signature
23
+ end || raise_endpoint_not_found
24
+ end
25
+
26
+ def endpoint_validator
27
+ @endpoint_validator ||= EndpointValidator.new(endpoint: endpoint)
28
+ end
29
+
30
+ private
31
+
32
+ def raise_endpoint_not_found
33
+ raise Errors::EndpointNotFound.new(
34
+ "No endpoint matched #{request_signature.join(" ")}"
35
+ )
36
+ end
37
+
38
+ def request_signature
39
+ [request['REQUEST_METHOD'], request['PATH_INFO']]
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,72 @@
1
+ module Swaggable
2
+ class CheckBodySchema
3
+ attr_reader :endpoint, :request
4
+
5
+ def initialize args
6
+ @endpoint = args.fetch(:endpoint)
7
+ @request = args.fetch(:request)
8
+ end
9
+
10
+ def self.call(*args)
11
+ new(*args).send :errors
12
+ end
13
+
14
+ private
15
+
16
+ def errors
17
+ Errors::ValidationsCollection.new.tap do |errors|
18
+ errors_for_required_parameters_in_body.each {|e| errors << e }
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def errors_for_required_parameters_in_body
25
+ if body_definition == nil
26
+ []
27
+ elsif !body_definition.required?
28
+ []
29
+ elsif !request.body
30
+ [Errors::Validation.new("Missing body")]
31
+ elsif body_definition.schema.empty?
32
+ []
33
+ else
34
+ missing_attribute_errors + unexpected_attribute_errors
35
+ end
36
+ end
37
+
38
+ def missing_attribute_errors
39
+ [].tap do |errors|
40
+ schema.attributes.select(&:required?).each do |attr|
41
+ unless body.has_key? attr.name
42
+ errors << Errors::Validation.new("Missing body parameter #{attr.inspect}")
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def unexpected_attribute_errors
49
+ [].tap do |errors|
50
+ body.each do |key, value|
51
+ unless schema.attributes[key]
52
+ errors << Errors::Validation.new("Unexpected body parameter #{key.inspect}")
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def body_definition
61
+ endpoint.body
62
+ end
63
+
64
+ def schema
65
+ body_definition.schema
66
+ end
67
+
68
+ def body
69
+ request.parsed_body
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,26 @@
1
+ module Swaggable
2
+ class CheckExpectedParameters
3
+ attr_reader :endpoint, :request
4
+
5
+ def initialize args
6
+ @endpoint = args.fetch(:endpoint)
7
+ @request = args.fetch(:request)
8
+ end
9
+
10
+ def self.call(*args)
11
+ new(*args).send :errors
12
+ end
13
+
14
+ private
15
+
16
+ def errors
17
+ Errors::ValidationsCollection.new.tap do |errors|
18
+ request.parameters(endpoint).each do |parameter|
19
+ unless endpoint.parameters.include? parameter
20
+ errors << Errors::Validation.new("Unexpected parameter #{parameter.inspect}")
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Swaggable
2
+ class CheckMandatoryParameters
3
+ attr_reader :endpoint, :request
4
+
5
+ def initialize args
6
+ @endpoint = args.fetch(:endpoint)
7
+ @request = args.fetch(:request)
8
+ end
9
+
10
+ def self.call(*args)
11
+ new(*args).send :errors
12
+ end
13
+
14
+ private
15
+
16
+ def errors
17
+ Errors::ValidationsCollection.new.tap do |errors|
18
+ endpoint.parameters.select(&:required).each do |parameter|
19
+ unless request.parameters(endpoint).include? parameter
20
+ errors << Errors::Validation.new("Missing parameter #{parameter.inspect}")
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Swaggable
2
+ class CheckRequestContentType
3
+ attr_reader :endpoint, :request
4
+
5
+ def initialize args
6
+ @endpoint = args.fetch(:endpoint)
7
+ @request = args.fetch(:request)
8
+ end
9
+
10
+ def self.call(*args)
11
+ new(*args).send :errors
12
+ end
13
+
14
+ private
15
+
16
+ def errors
17
+ Errors::ValidationsCollection.new.tap do |errors|
18
+ type = request.content_type
19
+
20
+ unless type.nil? || endpoint.consumes.include?(type)
21
+ errors << Errors::UnsupportedContentType.new("Content-Type #{type} not supported")
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ module Swaggable
2
+ class CheckResponseCode
3
+ attr_reader :endpoint, :response
4
+
5
+ def initialize args
6
+ @endpoint = args.fetch(:endpoint)
7
+ @response = args.fetch(:response)
8
+ end
9
+
10
+ def self.call(*args)
11
+ new(*args).send :errors
12
+ end
13
+
14
+ private
15
+
16
+ def errors
17
+ Errors::ValidationsCollection.new.tap do |errors|
18
+ unless endpoint.responses[response.code]
19
+ errors << Errors::Validation.new("Status code #{response.code.inspect} not supported")
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ module Swaggable
2
+ class CheckResponseContentType
3
+ attr_reader :endpoint, :response
4
+
5
+ def initialize args
6
+ @endpoint = args.fetch(:endpoint)
7
+ @response = args.fetch(:response)
8
+ end
9
+
10
+ def self.call(*args)
11
+ new(*args).send :errors
12
+ end
13
+
14
+ private
15
+
16
+ def errors
17
+ Errors::ValidationsCollection.new.tap do |errors|
18
+ type = response.content_type
19
+
20
+ unless type.nil? || endpoint.produces.include?(type)
21
+ errors << Errors::UnsupportedContentType.new("Content-Type #{type} not supported")
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,6 @@
1
1
  require 'forwarding_dsl'
2
2
  require 'mini_object'
3
+ require 'addressable/template'
3
4
 
4
5
  module Swaggable
5
6
  class EndpointDefinition
@@ -12,6 +13,11 @@ module Swaggable
12
13
  :summary,
13
14
  )
14
15
 
16
+ def initialize *args, &block
17
+ self.verb = 'GET'
18
+ super *args, &block
19
+ end
20
+
15
21
  def tags
16
22
  @tags ||= MiniObject::IndexedList.new.tap do |l|
17
23
  l.build { TagDefinition.new }
@@ -34,16 +40,38 @@ module Swaggable
34
40
  end
35
41
 
36
42
  def consumes
37
- @consumes ||= Set.new
43
+ @consumes ||= MimeTypesCollection.new
38
44
  end
39
45
 
40
46
  def produces
41
- @produces ||= Set.new
47
+ @produces ||= MimeTypesCollection.new
42
48
  end
43
49
 
44
50
  def configure &block
45
51
  ForwardingDsl.run(self, &block)
46
52
  self
47
53
  end
54
+
55
+ def match? v, p
56
+ v.to_s.upcase == verb.to_s.upcase && !!(path_template.match p)
57
+ end
58
+
59
+ def verb= value
60
+ @verb = value.to_s.upcase
61
+ end
62
+
63
+ def path_parameters_for path
64
+ path_template.extract(path) || {}
65
+ end
66
+
67
+ def body
68
+ parameters.detect {|p| p.location == :body }
69
+ end
70
+
71
+ private
72
+
73
+ def path_template
74
+ Addressable::Template.new path
75
+ end
48
76
  end
49
77
  end
@@ -0,0 +1,27 @@
1
+ module Swaggable
2
+ class EndpointValidator
3
+ attr_accessor(
4
+ :endpoint,
5
+ )
6
+
7
+ def initialize(args)
8
+ @endpoint = args.fetch(:endpoint)
9
+ end
10
+
11
+ def errors_for_request request
12
+ Errors::ValidationsCollection.new.tap do |errors|
13
+ errors.merge! CheckRequestContentType.(endpoint: endpoint, request: request)
14
+ errors.merge! CheckMandatoryParameters.(endpoint: endpoint, request: request)
15
+ errors.merge! CheckExpectedParameters.(endpoint: endpoint, request: request)
16
+ errors.merge! CheckBodySchema.(endpoint: endpoint, request: request)
17
+ end
18
+ end
19
+
20
+ def errors_for_response response
21
+ Errors::ValidationsCollection.new.tap do |errors|
22
+ errors.merge! CheckResponseContentType.(endpoint: endpoint, response: response)
23
+ errors.merge! CheckResponseCode.(endpoint: endpoint, response: response)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ module Swaggable
2
+ module Errors
3
+ autoload :ValidationsCollection, 'swaggable/errors/validations_collection'
4
+
5
+ Validation = Class.new StandardError
6
+ EndpointNotFound = Class.new StandardError
7
+ UnsupportedContentType = Class.new Validation
8
+ end
9
+ end
@@ -0,0 +1,44 @@
1
+ module Swaggable
2
+ module Errors
3
+ class ValidationsCollection < Validation
4
+ include Enumerable
5
+ extend Forwardable
6
+
7
+ def_delegators(
8
+ :@list,
9
+ :last,
10
+ :first,
11
+ :count,
12
+ :empty?,
13
+ )
14
+
15
+ def initialize
16
+ @list = []
17
+ end
18
+
19
+ def << entry
20
+ list << entry
21
+ end
22
+
23
+ def each *args, &block
24
+ list.each(*args, &block)
25
+ end
26
+
27
+ def inspect
28
+ "#<#{self.class.name}: #{message}>"
29
+ end
30
+
31
+ def merge! other
32
+ other.each {|e| self << e }
33
+ end
34
+
35
+ def message
36
+ list.map(&:message).join('. ') + '.'
37
+ end
38
+
39
+ protected
40
+
41
+ attr_reader :list
42
+ end
43
+ end
44
+ end