openapi-sourcetools 0.4.3 → 0.6.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/LICENSE.txt +1 -1
- data/bin/openapi-addheaders +73 -0
- data/bin/openapi-addparameters +72 -0
- data/bin/openapi-addresponses +72 -0
- data/bin/openapi-addschemas +104 -0
- data/bin/openapi-checkschemas +217 -0
- data/bin/openapi-frequencies +57 -84
- data/bin/openapi-generate +59 -0
- data/bin/openapi-merge +58 -84
- data/bin/openapi-processpaths +61 -88
- data/lib/apiobjects.rb +333 -0
- data/lib/common.rb +53 -27
- data/lib/gen.rb +87 -0
- data/lib/generate.rb +89 -0
- data/lib/helper.rb +96 -0
- data/lib/loaders.rb +51 -0
- data/lib/task.rb +101 -0
- metadata +27 -8
- data/bin/openapi-generatecode +0 -128
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9320a1d6d4202c453fb7065f00bc534206b1c780560ae443cd30293f27827595
|
4
|
+
data.tar.gz: af45eabe9a8f392c705398957cf276acb39923655207ffda12ee8d93e1c072a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f81cae3adb06d42d3bf5361615a0a21ca42346d08b1f8b83462116668d2ad61ec05ac707e723d910bbe55eb29699129ba13570b44504581e92597242ab3d810
|
7
|
+
data.tar.gz: 656959f866321cd68814fed2ef1c9121fe1db36674cb7fbd49f9f2a652e5f43a29d6b1d491a2197278577084d1328e01f99fd1eff8fca1c7ef7d6299ed512b17
|
data/LICENSE.txt
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Copyright © 2024 Ismo Kärkkäinen
|
5
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
6
|
+
|
7
|
+
require_relative '../lib/apiobjects'
|
8
|
+
require_relative '../lib/common'
|
9
|
+
require 'optparse'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
|
13
|
+
def replace_headers(obj, components)
|
14
|
+
return unless obj.is_a?(Hash)
|
15
|
+
obj.each do |k, v|
|
16
|
+
if k == 'headers'
|
17
|
+
v.keys.sort!.each do |name|
|
18
|
+
r = v[name]
|
19
|
+
next unless r.is_a?(Hash) # Could complain.
|
20
|
+
next if r.key?('$ref')
|
21
|
+
v[name] = { '$ref' => components.reference(r) }
|
22
|
+
end
|
23
|
+
else
|
24
|
+
replace_headers(v, components)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def main
|
30
|
+
input_name = nil
|
31
|
+
output_name = nil
|
32
|
+
path = %w[components headers]
|
33
|
+
components = Components.new(path, 'Header')
|
34
|
+
|
35
|
+
ENV['POSIXLY_CORRECT'] = '1'
|
36
|
+
parser = OptionParser.new do |opts|
|
37
|
+
opts.summary_indent = ' '
|
38
|
+
opts.summary_width = 26
|
39
|
+
opts.banner = 'Usage: openapi-addheaders [options]'
|
40
|
+
opts.separator ''
|
41
|
+
opts.separator 'Options:'
|
42
|
+
opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
|
43
|
+
input_name = f
|
44
|
+
end
|
45
|
+
opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
|
46
|
+
output_name = f
|
47
|
+
end
|
48
|
+
components.add_options(opts)
|
49
|
+
opts.on('-h', '--help', 'Print this help and exit.') do
|
50
|
+
$stdout.puts %(#{opts}
|
51
|
+
|
52
|
+
Loads API document in OpenAPI format and moves headers under components and
|
53
|
+
replaces the original with reference.
|
54
|
+
|
55
|
+
#{components.help}
|
56
|
+
)
|
57
|
+
exit 0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
parser.parse!
|
61
|
+
|
62
|
+
doc = load_source(input_name)
|
63
|
+
return 2 if doc.nil?
|
64
|
+
|
65
|
+
components.items = doc.dig(*path) || {}
|
66
|
+
replace_headers(doc.dig('components', 'responses') || {}, components)
|
67
|
+
replace_headers(doc.fetch('paths', {}), components)
|
68
|
+
bury(doc, path, components.items) unless components.items.empty?
|
69
|
+
|
70
|
+
dump_result(output_name, YAML.dump(doc, line_width: 1_000_000), 3)
|
71
|
+
end
|
72
|
+
|
73
|
+
exit(main) if (defined? $unit_test).nil?
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Copyright © 2024 Ismo Kärkkäinen
|
5
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
6
|
+
|
7
|
+
require_relative '../lib/apiobjects'
|
8
|
+
require_relative '../lib/common'
|
9
|
+
require 'optparse'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
def replace_parameter(p, components)
|
13
|
+
return p unless p.is_a?(Hash) # Could complain.
|
14
|
+
return p if p.key?('$ref')
|
15
|
+
{ '$ref' => components.reference(p) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def replace_parameters(obj, components)
|
19
|
+
return unless obj.is_a?(Hash)
|
20
|
+
obj.each do |k, v|
|
21
|
+
if k == 'parameters'
|
22
|
+
obj[k] = v.map { |p| replace_parameter(p, components) }
|
23
|
+
else
|
24
|
+
replace_parameters(v, components)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def main
|
30
|
+
input_name = nil
|
31
|
+
output_name = nil
|
32
|
+
path = %w[components parameters]
|
33
|
+
components = Components.new(path, 'Parameter')
|
34
|
+
|
35
|
+
ENV['POSIXLY_CORRECT'] = '1'
|
36
|
+
parser = OptionParser.new do |opts|
|
37
|
+
opts.summary_indent = ' '
|
38
|
+
opts.summary_width = 26
|
39
|
+
opts.banner = 'Usage: openapi-addparameters [options]'
|
40
|
+
opts.separator ''
|
41
|
+
opts.separator 'Options:'
|
42
|
+
opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
|
43
|
+
input_name = f
|
44
|
+
end
|
45
|
+
opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
|
46
|
+
output_name = f
|
47
|
+
end
|
48
|
+
components.add_options(opts)
|
49
|
+
opts.on('-h', '--help', 'Print this help and exit.') do
|
50
|
+
$stdout.puts %(#{opts}
|
51
|
+
|
52
|
+
Loads API document in OpenAPI format and moves parameters under components and
|
53
|
+
replaces the original with reference.
|
54
|
+
|
55
|
+
#{components.help}
|
56
|
+
)
|
57
|
+
exit 0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
parser.parse!
|
61
|
+
|
62
|
+
doc = load_source(input_name)
|
63
|
+
return 2 if doc.nil?
|
64
|
+
|
65
|
+
components.items = doc.dig(*path) || {}
|
66
|
+
replace_parameters(doc.fetch('paths', {}), components)
|
67
|
+
bury(doc, path, components.items) unless components.items.empty?
|
68
|
+
|
69
|
+
dump_result(output_name, YAML.dump(doc, line_width: 1_000_000), 3)
|
70
|
+
end
|
71
|
+
|
72
|
+
exit(main) if defined?($unit_test).nil?
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Copyright © 2024 Ismo Kärkkäinen
|
5
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
6
|
+
|
7
|
+
require_relative '../lib/apiobjects'
|
8
|
+
require_relative '../lib/common'
|
9
|
+
require 'optparse'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
|
13
|
+
def replace_responses(obj, components)
|
14
|
+
return unless obj.is_a?(Hash)
|
15
|
+
obj.each do |k, v|
|
16
|
+
if k == 'responses'
|
17
|
+
v.keys.sort!.each do |code|
|
18
|
+
r = v[code]
|
19
|
+
next unless r.is_a?(Hash) # Could complain.
|
20
|
+
next if r.key?('$ref')
|
21
|
+
v[code] = { '$ref' => components.reference(r) }
|
22
|
+
end
|
23
|
+
else
|
24
|
+
replace_responses(v, components)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def main
|
30
|
+
input_name = nil
|
31
|
+
output_name = nil
|
32
|
+
path = %w[components responses]
|
33
|
+
components = Components.new(path, 'Response')
|
34
|
+
|
35
|
+
ENV['POSIXLY_CORRECT'] = '1'
|
36
|
+
parser = OptionParser.new do |opts|
|
37
|
+
opts.summary_indent = ' '
|
38
|
+
opts.summary_width = 26
|
39
|
+
opts.banner = 'Usage: openapi-addresponses [options]'
|
40
|
+
opts.separator ''
|
41
|
+
opts.separator 'Options:'
|
42
|
+
opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
|
43
|
+
input_name = f
|
44
|
+
end
|
45
|
+
opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
|
46
|
+
output_name = f
|
47
|
+
end
|
48
|
+
components.add_options(opts)
|
49
|
+
opts.on('-h', '--help', 'Print this help and exit.') do
|
50
|
+
$stdout.puts %(#{opts}
|
51
|
+
|
52
|
+
Loads API document in OpenAPI format and moves responses under components and
|
53
|
+
replaces the original with reference.
|
54
|
+
|
55
|
+
#{components.help}
|
56
|
+
)
|
57
|
+
exit 0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
parser.parse!
|
61
|
+
|
62
|
+
doc = load_source(input_name)
|
63
|
+
return 2 if doc.nil?
|
64
|
+
|
65
|
+
components.items = doc.dig(*path) || {}
|
66
|
+
replace_responses(doc.fetch('paths', {}), components)
|
67
|
+
bury(doc, path, components.items) unless components.items.empty?
|
68
|
+
|
69
|
+
dump_result(output_name, YAML.dump(doc, line_width: 1_000_000), 3)
|
70
|
+
end
|
71
|
+
|
72
|
+
exit(main) if (defined? $unit_test).nil?
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Copyright © 2024 Ismo Kärkkäinen
|
5
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
6
|
+
|
7
|
+
require_relative '../lib/apiobjects'
|
8
|
+
require_relative '../lib/common'
|
9
|
+
require 'optparse'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
|
13
|
+
def remove_subitem(obj, path)
|
14
|
+
return obj.delete(path.first) || {} if path.size == 1
|
15
|
+
parent = obj.dig(*path.take(path.size - 1))
|
16
|
+
return {} if parent.nil?
|
17
|
+
parent.delete(path.last) || {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def replace_inlines(obj, components, top_level = false)
|
21
|
+
if obj.is_a?(Array)
|
22
|
+
obj.each do |o|
|
23
|
+
return false unless replace_inlines(o, components)
|
24
|
+
end
|
25
|
+
return true
|
26
|
+
end
|
27
|
+
return true unless obj.is_a?(Hash)
|
28
|
+
return true if obj.key?('$ref')
|
29
|
+
# Prior return would be the place to get rid of other keys if so desired.
|
30
|
+
# Is inlined, process parts recursively.
|
31
|
+
items = obj['allOf']
|
32
|
+
items = obj['anyOf'] if items.nil?
|
33
|
+
items = obj['oneOf'] if items.nil?
|
34
|
+
# All above need special treatment in code generation templates.
|
35
|
+
return replace_inlines(items, components) unless items.nil?
|
36
|
+
t = obj['type']
|
37
|
+
if t.nil? # Some kind of intermediate-level object.
|
38
|
+
(obj.keys.sort! { |a, b| a.to_s <=> b.to_s }).each do |key|
|
39
|
+
return false unless replace_inlines(obj[key], components)
|
40
|
+
end
|
41
|
+
return true
|
42
|
+
end
|
43
|
+
case t
|
44
|
+
when 'array'
|
45
|
+
return false unless replace_inlines(obj['items'], components)
|
46
|
+
when 'object'
|
47
|
+
props = obj.fetch('properties', {})
|
48
|
+
props.keys.sort!.each do |name|
|
49
|
+
return false unless replace_inlines(props[name], components)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
obj.replace({ '$ref' => components.reference(obj) }) unless top_level
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def main
|
57
|
+
input_name = nil
|
58
|
+
output_name = nil
|
59
|
+
path = %w[components schemas]
|
60
|
+
components = Components.new(path, 'Schema')
|
61
|
+
|
62
|
+
ENV['POSIXLY_CORRECT'] = '1'
|
63
|
+
parser = OptionParser.new do |opts|
|
64
|
+
opts.summary_indent = ' '
|
65
|
+
opts.summary_width = 26
|
66
|
+
opts.banner = 'Usage: openapi-addschemas [options]'
|
67
|
+
opts.separator ''
|
68
|
+
opts.separator 'Options:'
|
69
|
+
opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
|
70
|
+
input_name = f
|
71
|
+
end
|
72
|
+
opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
|
73
|
+
output_name = f
|
74
|
+
end
|
75
|
+
components.add_options(opts)
|
76
|
+
opts.on('-h', '--help', 'Print this help and exit.') do
|
77
|
+
$stdout.puts %(#{opts}
|
78
|
+
|
79
|
+
Loads API document in OpenAPI format and adds a schema for each inline type.
|
80
|
+
|
81
|
+
#{components.help}
|
82
|
+
)
|
83
|
+
exit 0
|
84
|
+
end
|
85
|
+
end
|
86
|
+
parser.parse!
|
87
|
+
|
88
|
+
doc = load_source(input_name)
|
89
|
+
return 2 if doc.nil?
|
90
|
+
|
91
|
+
# Find schema object and remove it temporarily to prevent being
|
92
|
+
# processed again.
|
93
|
+
components.items = remove_subitem(doc, path)
|
94
|
+
# Get rid of inlined types within existing schemas first.
|
95
|
+
components.items.keys.sort!.each do |k|
|
96
|
+
return 4 unless replace_inlines(components.items[k], components, true)
|
97
|
+
end
|
98
|
+
return 4 unless replace_inlines(doc, components)
|
99
|
+
bury(doc, path, components.items) unless components.items.empty?
|
100
|
+
|
101
|
+
dump_result(output_name, YAML.dump(doc, line_width: 1_000_000), 3)
|
102
|
+
end
|
103
|
+
|
104
|
+
exit(main) if (defined? $unit_test).nil?
|
@@ -0,0 +1,217 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Copyright © 2024 Ismo Kärkkäinen
|
5
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
6
|
+
|
7
|
+
require_relative '../lib/apiobjects'
|
8
|
+
require_relative '../lib/common'
|
9
|
+
require 'optparse'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
|
13
|
+
def typesame(a, b, ignored_keys)
|
14
|
+
return same(a, b, ignored_keys) unless a['type'] == 'object'
|
15
|
+
return same(a, b, ignored_keys) unless b['type'] == 'object'
|
16
|
+
# For each property in a, find out unused mathing property in b that is
|
17
|
+
# also either required or not.
|
18
|
+
# Requiring property affects e.g. C++ by allowing direct membership or
|
19
|
+
# when optional, a pointer that can have nullptr value. Or class instance
|
20
|
+
# or null in TypeScript so I consider required to affect type.
|
21
|
+
pa = a.fetch('properties', {})
|
22
|
+
ra = a.fetch('required', [])
|
23
|
+
pb = b.fetch('properties', {}).merge
|
24
|
+
rb = b.fetch('required', [])
|
25
|
+
pa.each do |aname, aspec|
|
26
|
+
bnames = pb.keys.sort!
|
27
|
+
if ra.include?(aname)
|
28
|
+
bnames.select! { |n| rb.include?(n) }
|
29
|
+
else
|
30
|
+
bnames.reject! { |n| rb.include?(n) }
|
31
|
+
end
|
32
|
+
matched = false
|
33
|
+
bnames.each do |bname|
|
34
|
+
bspec = pb[bname]
|
35
|
+
# Intended use is after openapi-addschemas so same should work as well.
|
36
|
+
if typesame(aspec, bspec, ignored_keys)
|
37
|
+
pb.delete(bname)
|
38
|
+
matched = true
|
39
|
+
break
|
40
|
+
end
|
41
|
+
end
|
42
|
+
return false unless matched
|
43
|
+
end
|
44
|
+
pb.empty? # All matched and nothing left in pb.
|
45
|
+
end
|
46
|
+
|
47
|
+
def keys2array(keys)
|
48
|
+
keys.to_h { |k| [ k, [] ] }
|
49
|
+
end
|
50
|
+
|
51
|
+
def gather_equivalencies(components)
|
52
|
+
order = components.items.keys.sort!
|
53
|
+
result = keys2array(order)
|
54
|
+
(order.size - 1).times do |k|
|
55
|
+
item = components.items[order[k]]
|
56
|
+
((k + 1)...order.size).each do |n|
|
57
|
+
if same(item, components.items[order[n]], components.ignored_keys)
|
58
|
+
result[order[k]].push(order[n])
|
59
|
+
result[order[n]].push(order[k])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
result.delete_if { |_k, v| v.empty? }
|
64
|
+
end
|
65
|
+
|
66
|
+
def gather_typematches(components)
|
67
|
+
order = components.items.keys.sort!
|
68
|
+
result = keys2array(order)
|
69
|
+
(order.size - 1).times do |k|
|
70
|
+
item = components.items[order[k]]
|
71
|
+
((k + 1)...order.size).each do |n|
|
72
|
+
if typesame(item, components.items[order[n]], components.ignored_keys)
|
73
|
+
result[order[k]].push(order[n])
|
74
|
+
result[order[n]].push(order[k])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
result.delete_if { |_k, v| v.empty? }
|
79
|
+
end
|
80
|
+
|
81
|
+
def gather_refs(obj, prefix, past, refs)
|
82
|
+
if obj.is_a?(Hash)
|
83
|
+
obj.each do |k, v|
|
84
|
+
if v.is_a?(String) && v.start_with?(prefix)
|
85
|
+
p = past.join('/')
|
86
|
+
if refs.key?(v)
|
87
|
+
refs[v].push(p)
|
88
|
+
else
|
89
|
+
refs[v] = [ p ]
|
90
|
+
end
|
91
|
+
else
|
92
|
+
past.push(k)
|
93
|
+
gather_refs(v, prefix, past, refs)
|
94
|
+
past.pop
|
95
|
+
end
|
96
|
+
end
|
97
|
+
elsif obj.is_a?(Array)
|
98
|
+
obj.size.times do |n|
|
99
|
+
past.push(n)
|
100
|
+
gather_refs(obj[n], prefix, past, refs)
|
101
|
+
past.pop
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def gather_references(doc, prefix)
|
107
|
+
refs = {}
|
108
|
+
gather_refs(doc, prefix, [], refs)
|
109
|
+
out = {}
|
110
|
+
refs.keys.sort!.each do |k|
|
111
|
+
r = refs[k]
|
112
|
+
next if r.empty?
|
113
|
+
out[k] = r.sort!
|
114
|
+
end
|
115
|
+
out
|
116
|
+
end
|
117
|
+
|
118
|
+
def drop_untouched(refs, report, prefix)
|
119
|
+
touched = Set.new
|
120
|
+
%w[equivalent typematch].each do |s|
|
121
|
+
r = report[s]
|
122
|
+
next if r.nil?
|
123
|
+
r.keys.each { |k| touched.add("#{prefix}#{k}") }
|
124
|
+
end
|
125
|
+
refs.delete_if { |k, _v| !touched.member?(k) }
|
126
|
+
end
|
127
|
+
|
128
|
+
def count_refs(refs)
|
129
|
+
counts = {}
|
130
|
+
refs.each { |k, v| counts[k] = v.size }
|
131
|
+
counts
|
132
|
+
end
|
133
|
+
|
134
|
+
def main
|
135
|
+
input_name = nil
|
136
|
+
output_name = nil
|
137
|
+
path = %w[components schemas]
|
138
|
+
components = Components.new(path, '')
|
139
|
+
equivalent = true
|
140
|
+
typematch = false
|
141
|
+
references = false
|
142
|
+
count = true
|
143
|
+
keep = false
|
144
|
+
|
145
|
+
ENV['POSIXLY_CORRECT'] = '1'
|
146
|
+
parser = OptionParser.new do |opts|
|
147
|
+
opts.summary_indent = ' '
|
148
|
+
opts.summary_width = 26
|
149
|
+
opts.banner = 'Usage: openapi-checkschemas [options]'
|
150
|
+
opts.separator ''
|
151
|
+
opts.separator 'Options:'
|
152
|
+
opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
|
153
|
+
input_name = f
|
154
|
+
end
|
155
|
+
opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
|
156
|
+
output_name = f
|
157
|
+
end
|
158
|
+
opts.on('-e', '--[no-]equivalent', "Report equivalent schemas, default #{equivalent}") do |b|
|
159
|
+
equivalent = b
|
160
|
+
end
|
161
|
+
opts.on('-t', '--[no-]typematch', "Report typematch schemas, default #{typematch}") do |b|
|
162
|
+
typematch = b
|
163
|
+
end
|
164
|
+
opts.on('-r', '--[no-]reference', "Report schema references, default #{references}") do |b|
|
165
|
+
references = b
|
166
|
+
end
|
167
|
+
opts.on('-c', '--[no-]count', "Report schema reference counts, default #{count}") do |b|
|
168
|
+
count = b
|
169
|
+
end
|
170
|
+
opts.on('-k', '--[no-]keep', "Keep all schema references/counts, default #{keep}") do |b|
|
171
|
+
keep = b
|
172
|
+
end
|
173
|
+
components.add_options(opts)
|
174
|
+
opts.on('-h', '--help', 'Print this help and exit.') do
|
175
|
+
$stdout.puts %(#{opts}
|
176
|
+
|
177
|
+
Loads API document in OpenAPI format and checks if any schemas are duplicates
|
178
|
+
of another and to what degree. Equivalency may be an exact match, or property
|
179
|
+
names differ but types match.
|
180
|
+
|
181
|
+
#{components.help}
|
182
|
+
|
183
|
+
Search is performed only at top schema level. References to equivalent types
|
184
|
+
are not considered equivalent when references themselves are not equivalent.
|
185
|
+
Any allOf, anyOf, oneOf checks merely check the refernces. Hence two different
|
186
|
+
allOf schemas may in practice result in equivalent types and that is not
|
187
|
+
detected.
|
188
|
+
|
189
|
+
Implicit expectation is the openapi-addschemas has been used to process the
|
190
|
+
input, as inlined types in requests for example are ignored.
|
191
|
+
)
|
192
|
+
exit 0
|
193
|
+
end
|
194
|
+
end
|
195
|
+
parser.parse!
|
196
|
+
|
197
|
+
doc = load_source(input_name)
|
198
|
+
return 2 if doc.nil?
|
199
|
+
|
200
|
+
components.items = doc.dig(*path) || {}
|
201
|
+
report = {}
|
202
|
+
report['equivalent'] = gather_equivalencies(components) if equivalent
|
203
|
+
report['typematch'] = gather_typematches(components) if typematch
|
204
|
+
if references || count
|
205
|
+
refs = gather_references(doc, components.path)
|
206
|
+
if equivalent || typematch # There is something to limit references to.
|
207
|
+
refs = drop_untouched(refs, report, components.path) unless keep
|
208
|
+
end
|
209
|
+
report['reference'] = refs if references
|
210
|
+
report['count'] = count_refs(refs) if count
|
211
|
+
end
|
212
|
+
# Filter reference counts that are not for type in equivalent or typematch.
|
213
|
+
# Could check for pretty-printed output format here.
|
214
|
+
dump_result(output_name, YAML.dump(report, line_width: 80), 3)
|
215
|
+
end
|
216
|
+
|
217
|
+
exit(main) if (defined? $unit_test).nil?
|