openapi-sourcetools 0.7.0 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 211083083a2a3c53321234416ffff41bd1d00320a5ebbdab3ccdcd49576653b3
4
- data.tar.gz: 34535443138a6308869974d62e79321d9ec9dbb3f46434c19d571075044751e1
3
+ metadata.gz: 71267174b8f95f5a510b93590f8fcdb8daf2737df19db6d2d7ebf48410444cf8
4
+ data.tar.gz: cf682ab07dcc557c9f3a2b9785f46aded117c4b50d840a6e8dd0b31e80840a63
5
5
  SHA512:
6
- metadata.gz: 3c4a5361e25a1cf7903b6038a32f4a94cf42579c0c4fefb21f885fae996c219a02eef322fd91d694dfe0ff668314685b990d3128ca7e6a98c3f30660639494d0
7
- data.tar.gz: a563ba480140037e0ef91dbdc89fb62a2175954897726717976760879a74bf8284d673d3a259ce338b7b7493c84fd6c1f5cf4246b90ee42ae1959d3b31c7b21f
6
+ metadata.gz: efe4f67e9903c4b65e404fe349e668ff659e538ddff7bc7e28a59bd7f98167e16faabb4876d5b45e1142562ad6e6c4e22c7471e1f374c3debc4e204855a7e169
7
+ data.tar.gz: 3ab4a1cf50426d6405cb1281f4c159d43480ead4f11414f8642b6e0a5d7cb4eca4f3769fb43cff4c5f1884ef5a72d88fdcc9b9c09bbdeb80f4624b008cfba059
@@ -32,7 +32,6 @@ def main
32
32
  path = %w[components headers]
33
33
  components = Components.new(path, 'Header')
34
34
 
35
- ENV['POSIXLY_CORRECT'] = '1'
36
35
  parser = OptionParser.new do |opts|
37
36
  opts.summary_indent = ' '
38
37
  opts.summary_width = 26
@@ -42,7 +41,7 @@ def main
42
41
  opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
43
42
  input_name = f
44
43
  end
45
- opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
44
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
46
45
  output_name = f
47
46
  end
48
47
  components.add_options(opts)
@@ -57,7 +56,7 @@ replaces the original with reference.
57
56
  exit 0
58
57
  end
59
58
  end
60
- parser.parse!
59
+ parser.order!
61
60
 
62
61
  doc = load_source(input_name)
63
62
  return 2 if doc.nil?
@@ -70,4 +69,4 @@ replaces the original with reference.
70
69
  dump_result(output_name, doc, 3)
71
70
  end
72
71
 
73
- exit(main) if (defined? $unit_test).nil?
72
+ exit(main) if defined?($unit_test).nil?
@@ -32,7 +32,6 @@ def main
32
32
  path = %w[components parameters]
33
33
  components = Components.new(path, 'Parameter')
34
34
 
35
- ENV['POSIXLY_CORRECT'] = '1'
36
35
  parser = OptionParser.new do |opts|
37
36
  opts.summary_indent = ' '
38
37
  opts.summary_width = 26
@@ -42,7 +41,7 @@ def main
42
41
  opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
43
42
  input_name = f
44
43
  end
45
- opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
44
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
46
45
  output_name = f
47
46
  end
48
47
  components.add_options(opts)
@@ -57,7 +56,7 @@ replaces the original with reference.
57
56
  exit 0
58
57
  end
59
58
  end
60
- parser.parse!
59
+ parser.order!
61
60
 
62
61
  doc = load_source(input_name)
63
62
  return 2 if doc.nil?
@@ -32,7 +32,6 @@ def main
32
32
  path = %w[components responses]
33
33
  components = Components.new(path, 'Response')
34
34
 
35
- ENV['POSIXLY_CORRECT'] = '1'
36
35
  parser = OptionParser.new do |opts|
37
36
  opts.summary_indent = ' '
38
37
  opts.summary_width = 26
@@ -42,7 +41,7 @@ def main
42
41
  opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
43
42
  input_name = f
44
43
  end
45
- opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
44
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
46
45
  output_name = f
47
46
  end
48
47
  components.add_options(opts)
@@ -57,7 +56,7 @@ replaces the original with reference.
57
56
  exit 0
58
57
  end
59
58
  end
60
- parser.parse!
59
+ parser.order!
61
60
 
62
61
  doc = load_source(input_name)
63
62
  return 2 if doc.nil?
@@ -69,4 +68,4 @@ replaces the original with reference.
69
68
  dump_result(output_name, doc, 3)
70
69
  end
71
70
 
72
- exit(main) if (defined? $unit_test).nil?
71
+ exit(main) if defined?($unit_test).nil?
@@ -17,7 +17,7 @@ def remove_subitem(obj, path)
17
17
  parent.delete(path.last) || {}
18
18
  end
19
19
 
20
- def replace_inlines(obj, components, top_level = false)
20
+ def replace_inlines(obj, components, top_level_name = nil)
21
21
  if obj.is_a?(Array)
22
22
  obj.each do |o|
23
23
  return false unless replace_inlines(o, components)
@@ -25,8 +25,11 @@ def replace_inlines(obj, components, top_level = false)
25
25
  return true
26
26
  end
27
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.
28
+ if obj.key?('$ref')
29
+ components.store_anchor(obj)
30
+ # Here would be the place to get rid of other keys if so desired.
31
+ return true
32
+ end
30
33
  # Is inlined, process parts recursively.
31
34
  items = obj['allOf']
32
35
  items = obj['anyOf'] if items.nil?
@@ -49,17 +52,34 @@ def replace_inlines(obj, components, top_level = false)
49
52
  return false unless replace_inlines(props[name], components)
50
53
  end
51
54
  end
52
- obj.replace({ '$ref' => components.reference(obj) }) unless top_level
55
+ r = components.ref_string(top_level_name) || components.reference(obj)
56
+ components.store_anchor(obj, r)
57
+ obj.replace({ '$ref' => r }) if top_level_name.nil?
53
58
  true
54
59
  end
55
60
 
61
+ def replace_anchor_refs(obj, components)
62
+ if obj.is_a?(Array)
63
+ obj.each do |o|
64
+ replace_anchor_refs(o, components)
65
+ end
66
+ return
67
+ end
68
+ return unless obj.is_a?(Hash)
69
+ r = obj['$ref']
70
+ obj['$ref'] = components.anchor_ref_replacement(r) unless r.nil?
71
+ obj.values.each do |value|
72
+ # Loops over $ref value but that is a string so nothing done to it.
73
+ replace_anchor_refs(value, components)
74
+ end
75
+ end
76
+
56
77
  def main
57
78
  input_name = nil
58
79
  output_name = nil
59
80
  path = %w[components schemas]
60
81
  components = Components.new(path, 'Schema')
61
82
 
62
- ENV['POSIXLY_CORRECT'] = '1'
63
83
  parser = OptionParser.new do |opts|
64
84
  opts.summary_indent = ' '
65
85
  opts.summary_width = 26
@@ -69,7 +89,7 @@ def main
69
89
  opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
70
90
  input_name = f
71
91
  end
72
- opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
92
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
73
93
  output_name = f
74
94
  end
75
95
  components.add_options(opts)
@@ -83,7 +103,7 @@ Loads API document in OpenAPI format and adds a schema for each inline type.
83
103
  exit 0
84
104
  end
85
105
  end
86
- parser.parse!
106
+ parser.order!
87
107
 
88
108
  doc = load_source(input_name)
89
109
  return 2 if doc.nil?
@@ -93,12 +113,15 @@ Loads API document in OpenAPI format and adds a schema for each inline type.
93
113
  components.items = remove_subitem(doc, path)
94
114
  # Get rid of inlined types within existing schemas first.
95
115
  components.items.keys.sort!.each do |k|
96
- return 4 unless replace_inlines(components.items[k], components, true)
116
+ components.add_schema_name(k)
117
+ return 4 unless replace_inlines(components.items[k], components, k)
97
118
  end
98
119
  return 4 unless replace_inlines(doc, components)
120
+ components.alter_anchors
99
121
  bury(doc, path, components.items) unless components.items.empty?
122
+ replace_anchor_refs(doc, components)
100
123
 
101
124
  dump_result(output_name, doc, 3)
102
125
  end
103
126
 
104
- exit(main) if (defined? $unit_test).nil?
127
+ exit(main) if defined?($unit_test).nil?
@@ -142,7 +142,6 @@ def main
142
142
  count = true
143
143
  keep = false
144
144
 
145
- ENV['POSIXLY_CORRECT'] = '1'
146
145
  parser = OptionParser.new do |opts|
147
146
  opts.summary_indent = ' '
148
147
  opts.summary_width = 26
@@ -152,22 +151,22 @@ def main
152
151
  opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
153
152
  input_name = f
154
153
  end
155
- opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
154
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
156
155
  output_name = f
157
156
  end
158
- opts.on('-e', '--[no-]equivalent', "Report equivalent schemas, default #{equivalent}") do |b|
157
+ opts.on('-e', '--[no-]equivalent', "Report equivalent schemas, default #{yesno(equivalent)}") do |b|
159
158
  equivalent = b
160
159
  end
161
- opts.on('-t', '--[no-]typematch', "Report typematch schemas, default #{typematch}") do |b|
160
+ opts.on('-t', '--[no-]typematch', "Report typematch schemas, default #{yesno(typematch)}") do |b|
162
161
  typematch = b
163
162
  end
164
- opts.on('-r', '--[no-]reference', "Report schema references, default #{references}") do |b|
163
+ opts.on('-r', '--[no-]reference', "Report schema references, default #{yesno(references)}") do |b|
165
164
  references = b
166
165
  end
167
- opts.on('-c', '--[no-]count', "Report schema reference counts, default #{count}") do |b|
166
+ opts.on('-c', '--[no-]count', "Report schema reference counts, default #{yesno(count)}") do |b|
168
167
  count = b
169
168
  end
170
- opts.on('-k', '--[no-]keep', "Keep all schema references/counts, default #{keep}") do |b|
169
+ opts.on('-k', '--[no-]keep', "Keep all schema references/counts, default #{yesno(keep)}") do |b|
171
170
  keep = b
172
171
  end
173
172
  components.add_options(opts)
@@ -182,17 +181,17 @@ names differ but types match.
182
181
 
183
182
  Search is performed only at top schema level. References to equivalent types
184
183
  are not considered equivalent when references themselves are not equivalent.
185
- Any allOf, anyOf, oneOf checks merely check the refernces. Hence two different
184
+ Any allOf, anyOf, oneOf checks merely check the references. Hence two different
186
185
  allOf schemas may in practice result in equivalent types and that is not
187
186
  detected.
188
187
 
189
188
  Implicit expectation is the openapi-addschemas has been used to process the
190
- input, as inlined types in requests for example are ignored.
189
+ input, as inlined types in requests, for example, are ignored.
191
190
  )
192
191
  exit 0
193
192
  end
194
193
  end
195
- parser.parse!
194
+ parser.order!
196
195
 
197
196
  doc = load_source(input_name)
198
197
  return 2 if doc.nil?
@@ -214,4 +213,4 @@ input, as inlined types in requests for example are ignored.
214
213
  dump_result(output_name, YAML.dump(report, line_width: 80), 3)
215
214
  end
216
215
 
217
- exit(main) if (defined? $unit_test).nil?
216
+ exit(main) if defined?($unit_test).nil?
@@ -37,7 +37,7 @@ into each path object matching count as "freq".
37
37
  exit 0
38
38
  end
39
39
  end
40
- parser.parse!
40
+ parser.order!
41
41
 
42
42
  return aargh('Path log file name must be given.', 1) if paths_name.nil?
43
43
 
@@ -75,4 +75,4 @@ into each path object matching count as "freq".
75
75
  dump_result(output_name, YAML.dump(doc), 3)
76
76
  end
77
77
 
78
- exit(main) if (defined? $unit_test).nil?
78
+ exit(main) if defined?($unit_test).nil?
data/bin/openapi-merge CHANGED
@@ -52,7 +52,7 @@ end
52
52
 
53
53
  def gather_refs(doc, found)
54
54
  doc.each_pair do |key, value|
55
- if key == '$ref' && value.is_a?(String) && value.start_with?('#/components/')
55
+ if key == '$ref' # Trust all refs to be valid.
56
56
  found.add(value)
57
57
  elsif value.is_a? Hash
58
58
  gather_refs(value, found)
@@ -64,6 +64,13 @@ def gather_refs(doc, found)
64
64
  end
65
65
  end
66
66
 
67
+ def has_refd_anchor(item, refs)
68
+ return !((item.index { |v| has_refd_anchor(v, refs) }).nil?) if item.is_a?(Array)
69
+ return false unless item.is_a?(Hash)
70
+ return true if refs.member?('#' + item.fetch('$anchor', ''))
71
+ !((item.values.index { |v| has_refd_anchor(v, refs) }).nil?)
72
+ end
73
+
67
74
  def prune(merged)
68
75
  prev_refs = Set.new
69
76
  loop do # May have references from deleted so repeat until nothing deleted.
@@ -74,16 +81,16 @@ def prune(merged)
74
81
  refs.add("#/components/securitySchemes/#{key}")
75
82
  end
76
83
  end
84
+ # Add schema ref for all schemas that have referenced anchor somewhere.
85
+ (merged.dig(*%w[components schemas]) || {}).each do |name, schema|
86
+ refs.add("#/components/Schemas/#{name}") if has_refd_anchor(schema, refs)
87
+ end
77
88
  used = {}
78
89
  all = merged.fetch('components', {})
79
90
  refs.each do |ref|
80
91
  p = ref.split('/')
81
92
  p.shift(2)
82
- item = all
83
- p.each do |key|
84
- item = item.fetch(key, nil)
85
- break if item.nil?
86
- end
93
+ item = all.dig(*p)
87
94
  next if item.nil?
88
95
  sub = used
89
96
  p.each_index do |k|
@@ -105,7 +112,6 @@ def main
105
112
  output_file = nil
106
113
  keep = false
107
114
 
108
- ENV['POSIXLY_CORRECT'] = '1'
109
115
  parser = OptionParser.new do |opts|
110
116
  opts.summary_indent = ' '
111
117
  opts.summary_width = 26
@@ -130,7 +136,7 @@ allowed only to append to the merged document, not re-define anything in it.
130
136
  exit 0
131
137
  end
132
138
  end
133
- parser.parse!
139
+ parser.order!
134
140
 
135
141
  max_depths = Hash.new(0)
136
142
  max_depths['openapi'] = 1
@@ -155,4 +161,4 @@ allowed only to append to the merged document, not re-define anything in it.
155
161
  dump_result(output_file, YAML.dump(merged), 3)
156
162
  end
157
163
 
158
- exit(main) if (defined? $unit_test).nil?
164
+ exit(main) if defined?($unit_test).nil?
@@ -120,4 +120,4 @@ STR and PREFIX are expected to be parts of a path surrounded by /.
120
120
  dump_result(output_name, doc, 3)
121
121
  end
122
122
 
123
- main if defined?($unit_test).nil?
123
+ exit(main) if defined?($unit_test).nil?
@@ -38,7 +38,7 @@ later stage tools. Checks for paths that may be ambiguous.
38
38
  exit 0
39
39
  end
40
40
  end
41
- parser.parse!
41
+ parser.order!
42
42
 
43
43
  doc = load_source(input_name)
44
44
  return 2 if doc.nil?
@@ -81,4 +81,4 @@ later stage tools. Checks for paths that may be ambiguous.
81
81
  dump_result(output_name, YAML.dump(doc), 3)
82
82
  end
83
83
 
84
- exit(main) if (defined? $unit_test).nil?
84
+ exit(main) if defined?($unit_test).nil?
data/lib/apiobjects.rb CHANGED
@@ -33,15 +33,17 @@ def reference(obj, schemas, schema_path, ignored_keys = Set.new(%w[summary descr
33
33
  end
34
34
 
35
35
  class Components
36
- attr_reader :path, :prefix
36
+ attr_reader :path, :prefix, :anchor2ref, :schema_names
37
37
  attr_accessor :items, :ignored_keys
38
38
 
39
- def initialize(path, prefix, ignored_keys = %w[summary description examples example])
40
- @items = {}
39
+ def initialize(path, prefix, ignored_keys = %w[summary description examples example $anchor])
41
40
  path = "#/#{path.join('/')}/" if path.is_a?(Array)
42
41
  path = "#{path}/" unless path.end_with?('/')
43
42
  @path = path
44
43
  @prefix = prefix
44
+ @anchor2ref = {}
45
+ @schema_names = Set.new
46
+ @items = {}
45
47
  @ignored_keys = Set.new(ignored_keys)
46
48
  end
47
49
 
@@ -58,7 +60,12 @@ class Components
58
60
  %(All fields are used in object equality comparisons except:\n#{@ignored_keys.to_a.sort!.join("\n")})
59
61
  end
60
62
 
63
+ def add_schema_name(name)
64
+ @schema_names.add(name)
65
+ end
66
+
61
67
  def ref_string(name)
68
+ return nil if name.nil?
62
69
  "#{@path}#{name}"
63
70
  end
64
71
 
@@ -73,42 +80,39 @@ class Components
73
80
  cand = "#{@prefix}#{n}x"
74
81
  next if @items.key?(cand)
75
82
  @items[cand] = obj.merge
83
+ @schema_names.add(cand)
76
84
  return ref_string(cand)
77
85
  end
78
86
  end
79
- end
80
-
81
-
82
- class PathOperation
83
- attr_accessor :path, :operation, :info, :parameters
84
- attr_accessor :servers, :security, :tags
85
- attr_accessor :summary, :description
86
- end
87
87
 
88
- # One could have a convenience
89
- # method that determines how many bytes the value needs, and if it needs to be
90
- # signed.
91
-
92
- # When creating types for schemas or otherwise, the type name can be added
93
- # into the item and that way be used as an indicator that the type has been
94
- # declared or needs a declaration.
88
+ def store_anchor(obj, ref = nil)
89
+ anchor_name = obj['$anchor']
90
+ return if anchor_name.nil?
91
+ ref = obj['$ref'] if ref.nil?
92
+ raise Exception, 'ref is nil and no $ref or it is nil' if ref.nil?
93
+ @anchor2ref[anchor_name] = ref
94
+ end
95
95
 
96
- def make_path_operations(apidoc)
97
- # Check openapi
98
- # Store info as is for reference
99
- # Store servers as is for default value for PathOperation
100
- # Process components. Lazy manner, only when referenced.
101
- # Store security as is for default value for PathOperation.
102
- # Store tags as mapping from name to object for use with PathOperation.
103
- # Process paths:
104
- # Store parameters as is for default value for PathOperation.
105
- # All other fields, check if it looks like OperationObject and create a
106
- # PathOperation using it. For others, store as is for default value.
96
+ def alter_anchors
97
+ replacements = {}
98
+ @anchor2ref.each do |a, r|
99
+ next if @schema_names.member?(a)
100
+ replacements[a] = ref_string(a)
101
+ @schema_names.add(a)
102
+ end
103
+ replacements.each do |a, r|
104
+ @anchor2ref[a] = r
105
+ end
106
+ end
107
107
 
108
+ def anchor_ref_replacement(ref)
109
+ @anchor2ref[ref[1...ref.size]] || ref
110
+ end
108
111
  end
109
112
 
110
-
111
113
  class ServerPath
114
+ # Probably moves to a separate file once processpaths and frequencies receive
115
+ # some attention.
112
116
  include Comparable
113
117
 
114
118
  attr_accessor :parts
@@ -161,6 +165,8 @@ class ServerPath
161
165
  end
162
166
  end
163
167
 
168
+ # The rest probably ends up in a gem that orders schemas and does nothing else.
169
+
164
170
  # Adds all refs found in the array to refs with given required state.
165
171
  def gather_array_refs(refs, items, required)
166
172
  items.each do |s|
@@ -298,36 +304,3 @@ class SchemaOrderer
298
304
  chosen
299
305
  end
300
306
  end
301
-
302
- class HeaderOrderer
303
- end
304
-
305
- class ResponseOrderer
306
- end
307
-
308
- class PathOrderer
309
- end
310
-
311
- class Tasker
312
- attr_reader :doc
313
- def initialize(doc)
314
- @doc = doc
315
- end
316
- end
317
-
318
- class SchemaTasker < Tasker
319
- # initialize with doc
320
- # Orderer set somehow.
321
- # Method ot create one task per schema.
322
- # Method to create one task for all schemas.
323
- end
324
-
325
- class HeaderTasker < Tasker
326
- # initialize with doc
327
- # Orderer set somehow.
328
- # Method ot create one task per schema.
329
- # Method to create one task for all schemas.
330
- end
331
-
332
-
333
-
data/lib/common.rb CHANGED
@@ -12,6 +12,10 @@ def aargh(message, return_value = nil)
12
12
  return_value
13
13
  end
14
14
 
15
+ def yesno(boolean)
16
+ boolean ? 'yes' : 'no'
17
+ end
18
+
15
19
  def bury(doc, path, value)
16
20
  (path.size - 1).times do |k|
17
21
  p = path[k]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi-sourcetools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ismo Kärkkäinen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-07 00:00:00.000000000 Z
11
+ date: 2024-08-28 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14