swagger-diff 1.0.1
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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +20 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +27 -0
- data/README.md +126 -0
- data/Rakefile +1 -0
- data/bin/console +7 -0
- data/bin/setup +7 -0
- data/exe/swagger-diff +17 -0
- data/lib/swagger/diff.rb +8 -0
- data/lib/swagger/diff/diff.rb +138 -0
- data/lib/swagger/diff/patch.rb +15 -0
- data/lib/swagger/diff/rspec_matchers.rb +24 -0
- data/lib/swagger/diff/specification.rb +194 -0
- data/lib/swagger/diff/version.rb +5 -0
- data/swagger-diff.gemspec +34 -0
- data/swagger-diff.gif +0 -0
- metadata +195 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 48e4764f21be525db2bdb268245bd3bb4be88e33
|
4
|
+
data.tar.gz: a756a2111d804ce0612185f1778cec7c559eec9f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9739b31a7a4036a78972a4c9a8fd41dff57b0dfd02174655fe87cbe848cf45e3acfbc73cc9522a5192130b88aa644293c79b29e08de149de81829cc9386ff5f1
|
7
|
+
data.tar.gz: fc583c80d81fc78c737e7713f865abd2b682a8064455179f7c0a8d236dd3a3006fd8d32dd708e28a55063d0086a4dbf9dfa2fd6a549cb1a303f4ff8f11ab58e9
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Metrics/AbcSize:
|
2
|
+
Max: 30
|
3
|
+
|
4
|
+
Metrics/ClassLength:
|
5
|
+
Max: 200
|
6
|
+
|
7
|
+
Metrics/LineLength:
|
8
|
+
Max: 120
|
9
|
+
|
10
|
+
Metrics/MethodLength:
|
11
|
+
Max: 25
|
12
|
+
|
13
|
+
Metrics/PerceivedComplexity:
|
14
|
+
Max: 10
|
15
|
+
|
16
|
+
Style/Documentation:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Style/FileName:
|
20
|
+
Enabled: false
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2.2
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2015, Civis Analytics
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of Civis Analytics nor the names of its
|
15
|
+
contributors may be used to endorse or promote products derived from
|
16
|
+
this software without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# Swagger::Diff
|
2
|
+
|
3
|
+
[](https://travis-ci.org/civisanalytics/swagger-diff)
|
4
|
+
[](http://badge.fury.io/rb/swagger-diff)
|
5
|
+
|
6
|
+

|
7
|
+
|
8
|
+
> You can tell me by the way I walk - Genesis
|
9
|
+
|
10
|
+
Swagger::Diff is a utility for comparing two different
|
11
|
+
[Swagger](http://swagger.io/) specifications.
|
12
|
+
Its intended use is to determine whether a newer API specification is
|
13
|
+
backwards-compatible with an older API specification.
|
14
|
+
It provides both an [RSpec](http://rspec.info/) matcher and helper functions
|
15
|
+
that can be used directly.
|
16
|
+
Specifications are considered backwards compatible if:
|
17
|
+
|
18
|
+
- all path and verb combinations in the old specification are present in the
|
19
|
+
new one
|
20
|
+
- no request parameters are required in the new specification that were not
|
21
|
+
required in the old one
|
22
|
+
- all request parameters in the old specification are present in the new one
|
23
|
+
- all request parameters in the old specification have the same type in the
|
24
|
+
new one
|
25
|
+
- all response attributes in the old specification are present in the new one
|
26
|
+
- all response attributes in the old specification have the same type in the new
|
27
|
+
one
|
28
|
+
|
29
|
+
## Installation
|
30
|
+
|
31
|
+
Add this line to your application's Gemfile:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
gem 'swagger-diff'
|
35
|
+
```
|
36
|
+
|
37
|
+
And then execute:
|
38
|
+
|
39
|
+
$ bundle
|
40
|
+
|
41
|
+
Or install it yourself as:
|
42
|
+
|
43
|
+
$ gem install swagger-diff
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
Swagger::Diff uses the [Swagger](https://github.com/swagger-rb/swagger-rb) gem
|
48
|
+
to parse Swagger specifications.
|
49
|
+
Specifications can be any
|
50
|
+
[supported format](https://github.com/swagger-rb/swagger-rb/tree/v0.2.3#parsing):
|
51
|
+
|
52
|
+
- the path to a file containing a Swagger specification.
|
53
|
+
This may be local (*e.g.*, `/path/to/swagger.json`) or remote (*e.g.*,
|
54
|
+
`http://host.domain/swagger.yml`)
|
55
|
+
- a Hash containing a parsed Swagger specification (*e.g.*, the output of
|
56
|
+
`JSON.parse`)
|
57
|
+
- a string of JSON containing Swagger specification
|
58
|
+
- a string of YAML containing Swagger specification
|
59
|
+
|
60
|
+
### RSpec
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
expect(<new>).to be_compatible_with(<old>)
|
64
|
+
```
|
65
|
+
|
66
|
+
If `new` is incompatible with `old`, the spec will fail and print a list of
|
67
|
+
backwards-incompatibilities.
|
68
|
+
|
69
|
+
### Direct Invocation
|
70
|
+
|
71
|
+
If you are not using RSpec, you can directly invoke the comparison function:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
diff = Swagger::Diff::Diff.new(<old>, <new>)
|
75
|
+
diff.compatible?
|
76
|
+
```
|
77
|
+
|
78
|
+
It will return `true` if `new` is compatible with `old`, `false` otherwise.
|
79
|
+
`#incompatibilities` will return a hash containing the incompatible endpoints,
|
80
|
+
request parameters, and response attributes; *e.g.*,
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
{ endpoints: ['put /a/{}'],
|
84
|
+
request_params: {
|
85
|
+
'get /a/' => ['missing request param: limit (type: integer)'],
|
86
|
+
'post /a/' => ['new required request param: extra'],
|
87
|
+
'put /b/{}' => ['new required request param: extra']
|
88
|
+
},
|
89
|
+
response_attributes: {
|
90
|
+
'post /a/' => ['missing attribute from 200 response: description (type: string)'],
|
91
|
+
'get /a/{}' => ['missing attribute from 200 response: description (type: string)'],
|
92
|
+
'put /b/{}' => ['missing attribute from 200 response: description (type: string)']
|
93
|
+
}
|
94
|
+
}
|
95
|
+
```
|
96
|
+
|
97
|
+
### Command-Line
|
98
|
+
|
99
|
+
It also includes a command-line version:
|
100
|
+
|
101
|
+
```bash
|
102
|
+
$ swagger-diff <old> <new>
|
103
|
+
```
|
104
|
+
|
105
|
+
`swagger-diff` will print a list of any backwards-incompatibilities `new` has
|
106
|
+
when compared to `old`.
|
107
|
+
|
108
|
+
## Gem Development
|
109
|
+
|
110
|
+
After checking out the repo, run `bin/setup` to install dependencies.
|
111
|
+
Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
112
|
+
|
113
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
114
|
+
To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
115
|
+
|
116
|
+
## Contributing
|
117
|
+
|
118
|
+
1. Fork it ( https://github.com/civisanalytics/swagger-diff/fork )
|
119
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
120
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
121
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
122
|
+
5. Create a new Pull Request
|
123
|
+
|
124
|
+
## License
|
125
|
+
|
126
|
+
Swagger::Diff is released under the [BSD 3-Clause License](LICENSE.txt).
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/bin/console
ADDED
data/bin/setup
ADDED
data/exe/swagger-diff
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'swagger/diff'
|
4
|
+
|
5
|
+
if ARGV.length == 2
|
6
|
+
diff = Swagger::Diff::Diff.new(ARGV[0], ARGV[1])
|
7
|
+
|
8
|
+
unless diff.compatible?
|
9
|
+
puts diff.incompatibilities_message
|
10
|
+
exit 1
|
11
|
+
end
|
12
|
+
else
|
13
|
+
puts 'Usage: swagger-diff <old> <new>
|
14
|
+
|
15
|
+
Checks <new> for backwards-compatibility with <old>. If <new> is incompatible,
|
16
|
+
a list of incompatibilities will be printed.'
|
17
|
+
end
|
data/lib/swagger/diff.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'forwardable' # Needed to require swagger-core.
|
2
|
+
require 'swagger'
|
3
|
+
require 'rspec/expectations'
|
4
|
+
require 'swagger/diff/diff'
|
5
|
+
require 'swagger/diff/patch'
|
6
|
+
require 'swagger/diff/rspec_matchers'
|
7
|
+
require 'swagger/diff/specification'
|
8
|
+
require 'swagger/diff/version'
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Swagger
|
2
|
+
module Diff
|
3
|
+
class Diff
|
4
|
+
def initialize(old, new)
|
5
|
+
@new_specification = Swagger::Diff::Specification.new(new)
|
6
|
+
@old_specification = Swagger::Diff::Specification.new(old)
|
7
|
+
end
|
8
|
+
|
9
|
+
def compatible?
|
10
|
+
endpoints_compatible? && requests_compatible? && responses_compatible?
|
11
|
+
end
|
12
|
+
|
13
|
+
def incompatibilities
|
14
|
+
{ endpoints: missing_endpoints.to_a.sort,
|
15
|
+
request_params: incompatible_request_params,
|
16
|
+
response_attributes: incompatible_response_attributes }
|
17
|
+
end
|
18
|
+
|
19
|
+
def incompatibilities_message
|
20
|
+
msg = ''
|
21
|
+
if incompatibilities[:endpoints]
|
22
|
+
msg += incompatibilities_message_endpoints(incompatibilities[:endpoints])
|
23
|
+
end
|
24
|
+
if incompatibilities[:request_params]
|
25
|
+
msg += incompatibilities_message_params(incompatibilities[:request_params])
|
26
|
+
end
|
27
|
+
if incompatibilities[:response_attributes]
|
28
|
+
msg += incompatibilities_message_attributes(incompatibilities[:response_attributes])
|
29
|
+
end
|
30
|
+
msg
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def incompatibilities_message_endpoints(endpoints)
|
36
|
+
if endpoints.empty?
|
37
|
+
''
|
38
|
+
else
|
39
|
+
msg = "- missing endpoints\n"
|
40
|
+
endpoints.each do |endpoint|
|
41
|
+
msg += " - #{endpoint}\n"
|
42
|
+
end
|
43
|
+
msg
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def incompatibilities_message_inner(typestr, collection)
|
48
|
+
if collection.nil? || collection.empty?
|
49
|
+
''
|
50
|
+
else
|
51
|
+
msg = "- incompatible #{typestr}\n"
|
52
|
+
collection.sort.each do |endpoint, attributes|
|
53
|
+
msg += " - #{endpoint}\n"
|
54
|
+
attributes.each do |attribute|
|
55
|
+
msg += " - #{attribute}\n"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
msg
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def incompatibilities_message_params(params)
|
63
|
+
incompatibilities_message_inner('request params', params)
|
64
|
+
end
|
65
|
+
|
66
|
+
def incompatibilities_message_attributes(attributes)
|
67
|
+
incompatibilities_message_inner('response attributes', attributes)
|
68
|
+
end
|
69
|
+
|
70
|
+
def missing_endpoints
|
71
|
+
@old_specification.endpoints - @new_specification.endpoints
|
72
|
+
end
|
73
|
+
|
74
|
+
def incompatible_request_params
|
75
|
+
ret = {}
|
76
|
+
incompatible_request_params_enumerator.each do |key, val|
|
77
|
+
ret[key] ||= []
|
78
|
+
ret[key] << val
|
79
|
+
end
|
80
|
+
ret
|
81
|
+
end
|
82
|
+
|
83
|
+
def incompatible_request_params_enumerator
|
84
|
+
Enumerator.new do |yielder|
|
85
|
+
@old_specification.request_params.each do |key, old_params|
|
86
|
+
new_params = @new_specification.request_params[key]
|
87
|
+
next if new_params.nil?
|
88
|
+
(new_params[:required] - old_params[:required]).each do |req|
|
89
|
+
yielder << [key, "new required request param: #{req}"]
|
90
|
+
end
|
91
|
+
(old_params[:all] - new_params[:all]).each do |req|
|
92
|
+
yielder << [key, "missing request param: #{req}"]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end.lazy
|
96
|
+
end
|
97
|
+
|
98
|
+
def incompatible_response_attributes
|
99
|
+
ret = {}
|
100
|
+
incompatible_response_attributes_enumerator.each do |key, val|
|
101
|
+
ret[key] ||= []
|
102
|
+
ret[key] << val
|
103
|
+
end
|
104
|
+
ret
|
105
|
+
end
|
106
|
+
|
107
|
+
def incompatible_response_attributes_enumerator
|
108
|
+
Enumerator.new do |yielder|
|
109
|
+
@old_specification.response_attributes.each do |key, old_attributes|
|
110
|
+
new_attributes = @new_specification.response_attributes[key]
|
111
|
+
next if new_attributes.nil?
|
112
|
+
old_attributes.keys.each do |code|
|
113
|
+
if new_attributes.key?(code)
|
114
|
+
(old_attributes[code] - new_attributes[code]).each do |resp|
|
115
|
+
yielder << [key, "missing attribute from #{code} response: #{resp}"]
|
116
|
+
end
|
117
|
+
else
|
118
|
+
yielder << [key, "missing #{code} response"]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end.lazy
|
123
|
+
end
|
124
|
+
|
125
|
+
def endpoints_compatible?
|
126
|
+
missing_endpoints.empty?
|
127
|
+
end
|
128
|
+
|
129
|
+
def requests_compatible?
|
130
|
+
incompatible_request_params_enumerator.none?
|
131
|
+
end
|
132
|
+
|
133
|
+
def responses_compatible?
|
134
|
+
incompatible_response_attributes_enumerator.none?
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Workaround for Swagger::V2::SecurityScheme[1] incorrectly specifying
|
2
|
+
# required fields. maxlinc is right: the Swagger specification[2] is
|
3
|
+
# misleading. Fixed Fields suggests many parameters are required that are not
|
4
|
+
# required by Swagger's official validator[3]. Working around this by removing
|
5
|
+
# them from the array of required properties.
|
6
|
+
#
|
7
|
+
# [1] https://github.com/swagger-rb/swagger-rb/blob/v0.2.3/lib/swagger/v2/security_scheme.rb#L9-L11
|
8
|
+
# [2] http://swagger.io/specification/#securityDefinitionsObject
|
9
|
+
# [3] https://github.com/swagger-api/validator-badge
|
10
|
+
|
11
|
+
OAUTH2_PARAMS = [:flow, :authorizationUrl, :scopes]
|
12
|
+
|
13
|
+
Swagger::V2::SecurityScheme.required_properties.reject! do |k, _|
|
14
|
+
OAUTH2_PARAMS.include?(k)
|
15
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
RSpec::Matchers.define :be_compatible_with do |expected|
|
2
|
+
match do |actual|
|
3
|
+
diff = Swagger::Diff::Diff.new(expected, actual)
|
4
|
+
diff.compatible?
|
5
|
+
end
|
6
|
+
|
7
|
+
failure_message do |actual|
|
8
|
+
diff = Swagger::Diff::Diff.new(expected, actual)
|
9
|
+
with = if File.exist?(expected)
|
10
|
+
" with '#{expected}'"
|
11
|
+
else
|
12
|
+
''
|
13
|
+
end
|
14
|
+
"expected Swagger to be compatible#{with}, found:\n#{diff.incompatibilities_message}"
|
15
|
+
end
|
16
|
+
|
17
|
+
failure_message_when_negated do
|
18
|
+
"expected Swagger to be incompatible with '#{expected}'"
|
19
|
+
end
|
20
|
+
|
21
|
+
description do
|
22
|
+
'be backwards-compatible with another specification'
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
module Swagger
|
2
|
+
module Diff
|
3
|
+
class Specification
|
4
|
+
def initialize(spec)
|
5
|
+
@spec = spec
|
6
|
+
@parsed = parse_swagger(spec)
|
7
|
+
@endpoint_hash = parsed_to_hash(@parsed)
|
8
|
+
end
|
9
|
+
|
10
|
+
def endpoints
|
11
|
+
@endpoint_hash.keys.to_set
|
12
|
+
end
|
13
|
+
|
14
|
+
def request_params
|
15
|
+
@request_params ||= begin
|
16
|
+
ret = {}
|
17
|
+
@endpoint_hash.each do |key, endpoint|
|
18
|
+
ret[key] = request_params_inner(params_or_nil(endpoint))
|
19
|
+
end
|
20
|
+
ret
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def response_attributes
|
25
|
+
@response_attributes ||= begin
|
26
|
+
ret = {}
|
27
|
+
@endpoint_hash.each do |key, endpoint|
|
28
|
+
ret[key] = response_attributes_inner(endpoint)
|
29
|
+
end
|
30
|
+
ret
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def merge_refs!(h1, h2)
|
37
|
+
h2.each do |k, v|
|
38
|
+
if h1.include?(k)
|
39
|
+
h1[k] += h1[k].merge(v)
|
40
|
+
else
|
41
|
+
h1[k] = v
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def params_or_nil(endpoint)
|
47
|
+
endpoint && endpoint.parameters || nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_swagger(swagger)
|
51
|
+
if swagger.is_a? Hash
|
52
|
+
Swagger.build(swagger)
|
53
|
+
elsif File.exist?(swagger)
|
54
|
+
Swagger.load(swagger)
|
55
|
+
else
|
56
|
+
swagger = open(swagger).read if swagger[0..7] =~ %r{^https?://}
|
57
|
+
begin
|
58
|
+
JSON.parse(swagger)
|
59
|
+
rescue JSON::ParserError
|
60
|
+
begin
|
61
|
+
YAML.load(swagger)
|
62
|
+
rescue Psych::SyntaxError
|
63
|
+
raise 'Only filenames or raw or parsed strings of JSON or YAML are supported.'
|
64
|
+
else
|
65
|
+
Swagger.build(swagger, format: :yaml)
|
66
|
+
end
|
67
|
+
else
|
68
|
+
Swagger.build(swagger, format: :json)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def parsed_to_hash(parsed)
|
74
|
+
ret = {}
|
75
|
+
parsed.operations.each do |endpoint|
|
76
|
+
ret["#{endpoint.verb} #{endpoint.path.gsub(/{.*?}/, '{}')}"] = endpoint
|
77
|
+
end
|
78
|
+
ret
|
79
|
+
end
|
80
|
+
|
81
|
+
# Parses a $ref into a flat list of parameters, recursively if necessary.
|
82
|
+
#
|
83
|
+
# Returns a hash with 2 keys where the value is a set of flattened
|
84
|
+
# parameter definitions (i.e., all parameters, including nested
|
85
|
+
# parameters, are included in a single set).
|
86
|
+
def refs(ref, prefix = '')
|
87
|
+
definitions = @parsed.definitions
|
88
|
+
idx = ref.rindex('/')
|
89
|
+
key = ref[idx + 1..-1]
|
90
|
+
ret = {}
|
91
|
+
if definitions[key].allOf
|
92
|
+
definitions[key].allOf.each do |schema|
|
93
|
+
if schema['$ref']
|
94
|
+
merge_refs!(ret, refs(schema['$ref'], prefix))
|
95
|
+
else
|
96
|
+
merge_refs!(ret, properties(schema.properties, schema.required, prefix))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
else
|
100
|
+
merge_refs!(ret,
|
101
|
+
properties(definitions[key].properties,
|
102
|
+
definitions[key].required, prefix))
|
103
|
+
end
|
104
|
+
ret
|
105
|
+
end
|
106
|
+
|
107
|
+
def nested(ref, prefix, name, list = false)
|
108
|
+
# Check for cycles by testing whether name was already added to
|
109
|
+
# prefix.
|
110
|
+
key = "#{prefix}#{name}#{'[]' if list}"
|
111
|
+
if prefix == "#{name}/" || prefix =~ %r{/#{name}(\[\])?/}
|
112
|
+
# Anonymizing the reference in case the name changed (e.g.,
|
113
|
+
# generated Swagger).
|
114
|
+
{ all: ["#{key} (type: reference)"] }
|
115
|
+
else
|
116
|
+
refs(ref, "#{key}/")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def properties_for_ref(prefix, name, schema, required, list = false)
|
121
|
+
key = "#{prefix}#{name}"
|
122
|
+
ret = { required: Set.new, all: Set.new }
|
123
|
+
if schema.key?('$ref')
|
124
|
+
merge_refs!(ret, nested(schema['$ref'], prefix, name, list))
|
125
|
+
else
|
126
|
+
ret[:required].add(key) if required && required.include?(name)
|
127
|
+
ret[:all].add("#{key} (type: #{schema.type}#{'[]' if list})")
|
128
|
+
end
|
129
|
+
ret
|
130
|
+
end
|
131
|
+
|
132
|
+
def properties(properties, required, prefix = '')
|
133
|
+
ret = { required: Set.new, all: Set.new }
|
134
|
+
properties.each do |name, schema|
|
135
|
+
if schema.type == 'array'
|
136
|
+
merge_refs!(ret, properties_for_ref(prefix, name, schema.items, required, true))
|
137
|
+
elsif schema.type == 'object'
|
138
|
+
if schema.allOf
|
139
|
+
# TODO: handle nested allOfs.
|
140
|
+
else
|
141
|
+
# Swagger 2.0 doesn't appear to support non-string keys[1]. If
|
142
|
+
# this changes, this will need to be updated.
|
143
|
+
# [1] https://github.com/swagger-api/swagger-spec/issues/299
|
144
|
+
# TODO: this doesn't handle hashes of objects.
|
145
|
+
type = if schema.additionalProperties &&
|
146
|
+
schema.additionalProperties.type
|
147
|
+
schema.additionalProperties.type
|
148
|
+
else
|
149
|
+
'*'
|
150
|
+
end
|
151
|
+
ret[:all].add("#{prefix}#{name} (type: Hash[string, #{type}])")
|
152
|
+
end
|
153
|
+
else
|
154
|
+
merge_refs!(ret, properties_for_ref(prefix, name, schema, required))
|
155
|
+
end
|
156
|
+
end
|
157
|
+
ret
|
158
|
+
end
|
159
|
+
|
160
|
+
def request_params_inner(params)
|
161
|
+
ret = { required: Set.new, all: Set.new }
|
162
|
+
return ret if params.nil?
|
163
|
+
params.each do |param|
|
164
|
+
if param.in == 'path' || param.in == 'query'
|
165
|
+
ret[:required].add(param.name) if param.required
|
166
|
+
ret[:all].add("#{param.name} (type: #{param.type})")
|
167
|
+
else
|
168
|
+
merge_refs!(ret, refs(param.schema['$ref']))
|
169
|
+
end
|
170
|
+
end
|
171
|
+
ret
|
172
|
+
end
|
173
|
+
|
174
|
+
def response_attributes_inner(endpoint)
|
175
|
+
ret = {}
|
176
|
+
endpoint.responses.each do |code, response|
|
177
|
+
if response.schema
|
178
|
+
if response.schema.include?('type') && response.schema.type == 'array'
|
179
|
+
ref = response.schema.items['$ref']
|
180
|
+
prefix = '[]/'
|
181
|
+
else
|
182
|
+
ref = response.schema['$ref']
|
183
|
+
prefix = ''
|
184
|
+
end
|
185
|
+
ret[code] = refs(ref, prefix)[:all]
|
186
|
+
else
|
187
|
+
ret[code] = Set.new
|
188
|
+
end
|
189
|
+
end
|
190
|
+
ret
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'swagger/diff/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'swagger-diff'
|
8
|
+
spec.version = Swagger::Diff::VERSION
|
9
|
+
spec.authors = ['Jeff Cousens']
|
10
|
+
spec.email = ['opensource@civisanalytics.com']
|
11
|
+
|
12
|
+
spec.summary = 'Utility for comparing two Swagger specifications.'
|
13
|
+
spec.description = 'Swagger::Diff is a utility for comparing two different Swagger specifications.
|
14
|
+
It is intended to determine whether a newer API specification is backwards-
|
15
|
+
compatible with an older API specification. It provides both an RSpec matcher
|
16
|
+
and helper functions that can be used directly.'
|
17
|
+
spec.homepage = 'https://github.com/civisanalytics/swagger-diff'
|
18
|
+
spec.license = 'BSD 3-Clause'
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
+
spec.bindir = 'exe'
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.add_dependency 'swagger-core', '~> 0.2.3'
|
26
|
+
spec.add_dependency 'rspec-expectations', '~> 3.3'
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.9'
|
28
|
+
spec.add_development_dependency 'rake', '~> 10.4'
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.3'
|
30
|
+
spec.add_development_dependency 'pry', '~> 0.10.1'
|
31
|
+
spec.add_development_dependency 'rubocop', '~> 0.33.0'
|
32
|
+
spec.add_development_dependency 'vcr', '~> 2.9'
|
33
|
+
spec.add_development_dependency 'webmock', '~> 1.21'
|
34
|
+
end
|
data/swagger-diff.gif
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: swagger-diff
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeff Cousens
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: swagger-core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.2.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.2.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec-expectations
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.9'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.9'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.4'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.4'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.3'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.3'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.10.1
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.10.1
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.33.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.33.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: vcr
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '2.9'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '2.9'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: webmock
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.21'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1.21'
|
139
|
+
description: |-
|
140
|
+
Swagger::Diff is a utility for comparing two different Swagger specifications.
|
141
|
+
It is intended to determine whether a newer API specification is backwards-
|
142
|
+
compatible with an older API specification. It provides both an RSpec matcher
|
143
|
+
and helper functions that can be used directly.
|
144
|
+
email:
|
145
|
+
- opensource@civisanalytics.com
|
146
|
+
executables:
|
147
|
+
- swagger-diff
|
148
|
+
extensions: []
|
149
|
+
extra_rdoc_files: []
|
150
|
+
files:
|
151
|
+
- ".gitignore"
|
152
|
+
- ".rspec"
|
153
|
+
- ".rubocop.yml"
|
154
|
+
- ".ruby-version"
|
155
|
+
- ".travis.yml"
|
156
|
+
- Gemfile
|
157
|
+
- LICENSE.txt
|
158
|
+
- README.md
|
159
|
+
- Rakefile
|
160
|
+
- bin/console
|
161
|
+
- bin/setup
|
162
|
+
- exe/swagger-diff
|
163
|
+
- lib/swagger/diff.rb
|
164
|
+
- lib/swagger/diff/diff.rb
|
165
|
+
- lib/swagger/diff/patch.rb
|
166
|
+
- lib/swagger/diff/rspec_matchers.rb
|
167
|
+
- lib/swagger/diff/specification.rb
|
168
|
+
- lib/swagger/diff/version.rb
|
169
|
+
- swagger-diff.gemspec
|
170
|
+
- swagger-diff.gif
|
171
|
+
homepage: https://github.com/civisanalytics/swagger-diff
|
172
|
+
licenses:
|
173
|
+
- BSD 3-Clause
|
174
|
+
metadata: {}
|
175
|
+
post_install_message:
|
176
|
+
rdoc_options: []
|
177
|
+
require_paths:
|
178
|
+
- lib
|
179
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
180
|
+
requirements:
|
181
|
+
- - ">="
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: '0'
|
184
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - ">="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: '0'
|
189
|
+
requirements: []
|
190
|
+
rubyforge_project:
|
191
|
+
rubygems_version: 2.4.5
|
192
|
+
signing_key:
|
193
|
+
specification_version: 4
|
194
|
+
summary: Utility for comparing two Swagger specifications.
|
195
|
+
test_files: []
|