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.
- checksums.yaml +4 -4
- data/lib/swaggable.rb +32 -7
- data/lib/swaggable/api_validator.rb +42 -0
- data/lib/swaggable/check_body_schema.rb +72 -0
- data/lib/swaggable/check_expected_parameters.rb +26 -0
- data/lib/swaggable/check_mandatory_parameters.rb +26 -0
- data/lib/swaggable/check_request_content_type.rb +26 -0
- data/lib/swaggable/check_response_code.rb +24 -0
- data/lib/swaggable/check_response_content_type.rb +26 -0
- data/lib/swaggable/endpoint_definition.rb +30 -2
- data/lib/swaggable/endpoint_validator.rb +27 -0
- data/lib/swaggable/errors.rb +9 -0
- data/lib/swaggable/errors/validations_collection.rb +44 -0
- data/lib/swaggable/grape_adapter.rb +2 -2
- data/lib/swaggable/mime_type_definition.rb +73 -0
- data/lib/swaggable/mime_types_collection.rb +60 -0
- data/lib/swaggable/parameter_definition.rb +19 -0
- data/lib/swaggable/query_params.rb +50 -0
- data/lib/swaggable/rack_request_adapter.rb +88 -0
- data/lib/swaggable/rack_response_adapter.rb +49 -0
- data/lib/swaggable/swagger_2_serializer.rb +3 -3
- data/lib/swaggable/validating_rack_app.rb +30 -0
- data/lib/swaggable/version.rb +1 -1
- data/spec/{swaggable/integration_spec.rb → integration/dsl_spec.rb} +0 -40
- data/spec/integration/rack_app_spec.rb +44 -0
- data/spec/integration/validating_rack_app_spec.rb +50 -0
- data/spec/spec_helper.rb +9 -5
- data/spec/swaggable/api_validator_spec.rb +61 -0
- data/spec/swaggable/check_body_schema_spec.rb +94 -0
- data/spec/swaggable/check_expected_parameters_spec.rb +110 -0
- data/spec/swaggable/check_mandatory_parameters_spec.rb +110 -0
- data/spec/swaggable/check_request_content_type_spec.rb +51 -0
- data/spec/swaggable/check_response_code_spec.rb +37 -0
- data/spec/swaggable/check_response_content_type_spec.rb +51 -0
- data/spec/swaggable/endpoint_definition_spec.rb +47 -0
- data/spec/swaggable/endpoint_validator_spec.rb +102 -0
- data/spec/swaggable/errors/validations_collection_spec.rb +78 -0
- data/spec/swaggable/grape_adapter_spec.rb +2 -2
- data/spec/swaggable/mime_type_definition_spec.rb +96 -0
- data/spec/swaggable/mime_types_collection_spec.rb +83 -0
- data/spec/swaggable/parameter_definition_spec.rb +23 -0
- data/spec/swaggable/query_params_spec.rb +37 -0
- data/spec/swaggable/rack_request_adapter_spec.rb +89 -0
- data/spec/swaggable/rack_response_adapter_spec.rb +46 -0
- data/spec/swaggable/swagger_2_serializer_spec.rb +4 -2
- data/spec/swaggable/swagger_2_validator_spec.rb +2 -2
- data/spec/swaggable/validating_rack_app_spec.rb +91 -0
- data/swaggable.gemspec +1 -0
- metadata +68 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 808c9b125e0ebb98bf9307f3922375344f073e2d
|
4
|
+
data.tar.gz: e50cd856a8362d1f15e7e95cf70c5307b31c264b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eeb0d8a2698ff01a16bd477f7a43d7ad6d52dabb191cd8f366e341bd7ba46cd444fa58bdcaf221962f3fd95174d6d44cff5eff7d67222b0795371f748f9b21d3
|
7
|
+
data.tar.gz: 8f5d5d91cb25766ebe780ee784b9a49599439b5eddda1343fe507d22f42972715bd6c91c1c892ea3e3663230335fe99100480f468a1734b0cc374259e72edb79
|
data/lib/swaggable.rb
CHANGED
@@ -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 :
|
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
|
-
|
15
|
-
|
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 :
|
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 ||=
|
43
|
+
@consumes ||= MimeTypesCollection.new
|
38
44
|
end
|
39
45
|
|
40
46
|
def produces
|
41
|
-
@produces ||=
|
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,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
|