openapi-sourcetools 0.8.1 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93117ebfa33920b762da40afe48264ead793574ad336628487d3cfb3b6019987
4
- data.tar.gz: 67cb3400cd879b3625e58b72db11cfc860a87179d069ff341a6202391f8d61f5
3
+ metadata.gz: 44ff7d64b4781fdfbab07060f6faa294a8092e342530e6cde786e50ff8f69db1
4
+ data.tar.gz: 893e593407c4734e67c83bffe77a67bb55b2ecef3970620a416580fd33109437
5
5
  SHA512:
6
- metadata.gz: af4681c719424375326d4149ede787f0d6a58d33ff28082d7c1f7b00db4b40186f6406bfe99707012aeae0ac03d4504fb14cd49b6e587f55d91c1f2cb353a3b3
7
- data.tar.gz: f6c84c0a9e6b20040fefa9611c8abf211abc1fb65c6a3c3041ce1f96f3b3482fa1789eb2eb16460c5a1d892d56fd843164ae4d7dfb9c4cdb6e3a13f26977d323
6
+ metadata.gz: 741c12063a6d5df5d090f26b80c60a7810083537076573cb10f9ba5a3ca3afc8109ab6faa305707139c881a7efa1f478d7d0de562db67b44f93c0329ae8263e4
7
+ data.tar.gz: 9e1c2bc87442258d75517beb13f99c1e0b210b7074ee622db8057e1e4ebf17c253538ba52b893b0f148baa21188a306976a9783d778e92dd769caee6eb84aef6
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2025 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/openapi/sourcetools/common'
8
+ require 'optparse'
9
+ include OpenAPISourceTools
10
+
11
+ def key(item)
12
+ "#{item['pattern']}::#{item.fetch('minLength', 0)}::#{item.fetch('maxLength', 'inf')}"
13
+ end
14
+
15
+ def find_patterns(doc, pmm)
16
+ if doc.is_a?(Hash)
17
+ if doc.key?('pattern')
18
+ item = { 'pattern' => doc['pattern'] }
19
+ item['minLength'] = doc['minLength'] if doc.key?('minLength')
20
+ item['maxLength'] = doc['maxLength'] if doc.key?('maxLength')
21
+ pmm[key(doc)] = item
22
+ return
23
+ end
24
+ doc = doc.values
25
+ end
26
+ doc.each { |v| find_patterns(v, pmm) } if doc.is_a?(Array)
27
+ end
28
+
29
+ def pattern_list2hash(list)
30
+ pmms = {}
31
+ list.each { |item| pmms[key(item)] = item }
32
+ pmms
33
+ end
34
+
35
+ def add_strings(item)
36
+ passes = []
37
+ fails = []
38
+ min_len = item.fetch('minLength', 0)
39
+ fails.push('f' * (min_len - 1)) unless min_len.zero?
40
+ max_len = item['maxLength']
41
+ fails.push('f' * (max_len + 1)) unless max_len.nil?
42
+ # Fails within length limits require considering the pattern.
43
+ # All passes require considering the pattern.
44
+ item['pass'] = passes
45
+ item['fail'] = fails
46
+ end
47
+
48
+ def merge_arrays(current, past)
49
+ if current.is_a?(Array)
50
+ if past.is_a?(Array)
51
+ return current.concat(past)
52
+ end
53
+ current
54
+ elsif past.is_a?(Array)
55
+ past
56
+ else
57
+ false
58
+ end
59
+ end
60
+
61
+ def merge_existing(pmms, existing)
62
+ pmms.each do |k, v|
63
+ ex = existing[k]
64
+ next if ex.nil?
65
+ %w[pass fail].each do |arr|
66
+ m = merge_arrays(v[arr], ex[arr])
67
+ v[arr] = m.is_a?(Array) ? m.sort!.uniq : m
68
+ end
69
+ ex.each do |ek, ev|
70
+ v[ek] = ev unless v.key?(ek)
71
+ end
72
+ end
73
+ end
74
+
75
+ def add_removed(pmms, existing)
76
+ existing.each { |k, v| pmms[k] = v unless pmms.key?(k) }
77
+ end
78
+
79
+ def compare(a, b)
80
+ d = a['pattern'] <=> b['pattern']
81
+ return d unless d.zero?
82
+ d = a.fetch('minLength', 0) <=> b.fetch('minLength', 0)
83
+ return d unless d.zero?
84
+ a.fetch('maxLength', Float::INFINITY) <=> b.fetch('maxLength', Float::INFINITY)
85
+ end
86
+
87
+ def pattern_hash2list(pmms)
88
+ pmms.values.sort { |a, b| compare(a, b) }
89
+ end
90
+
91
+ def main
92
+ array_name = 'patterns'
93
+ input_name = nil
94
+ output_name = nil
95
+ source_tests = nil
96
+ keep = false
97
+ chain = []
98
+
99
+ parser = OptionParser.new do |opts|
100
+ opts.summary_indent = ' '
101
+ opts.summary_width = 20
102
+ opts.banner = 'Usage: openapi-patterntests [options]'
103
+ opts.separator ''
104
+ opts.separator 'Options:'
105
+ opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
106
+ input_name = f
107
+ end
108
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
109
+ output_name = f
110
+ end
111
+ opts.on('-t', '--tests FILE', 'Read existing tests from FILE.') do |f|
112
+ source_tests = f
113
+ end
114
+ opts.on('-u', '--under STR', %(Top-level "#{array_name}" is under dot-separated keys.)) do |s|
115
+ chain = s.split('.').reject(&:empty?)
116
+ end
117
+ opts.on('-k', '--[no-]keep', "Keep missing test patterns, default = #{Common.yesno(keep)}.") do |b|
118
+ keep = b
119
+ end
120
+ opts.on('-h', '--help', 'Print this help and exit.') do
121
+ $stdout.puts %(#{opts}
122
+
123
+ Loads API document in OpenAPI format, extracts string patterns, and outputs
124
+ a YAML file that contains mapping from patterns to matching and not mathcing
125
+ strings for testing generated code.
126
+ )
127
+ exit 0
128
+ end
129
+ end
130
+ parser.order!
131
+
132
+ doc = Common.load_source(input_name)
133
+ return 2 if doc.nil?
134
+
135
+ if source_tests.nil?
136
+ ex = {}
137
+ pats = []
138
+ else
139
+ ex = Common.load_source(source_tests)
140
+ return 2 if ex.nil?
141
+ parent = chain.empty? ? ex : ex.dig(*chain)
142
+ return Common.aargh("Key chain #{chain.join('.')} not found in source tests.", 4) if parent.nil?
143
+ pats = parent[array_name] || []
144
+ end
145
+ chain.push(array_name)
146
+
147
+ existing = pattern_list2hash(pats)
148
+ pmms = {}
149
+ find_patterns(doc, pmms)
150
+ pmms.each_value { |item| add_strings(item) }
151
+ merge_existing(pmms, existing)
152
+ add_removed(pmms, existing) if keep
153
+ pats = pattern_hash2list(pmms)
154
+
155
+ Common.bury(ex, chain, pats)
156
+ Common.dump_result(output_name, ex, 3)
157
+ end
158
+
159
+ exit(main) if defined?($unit_test).nil?
@@ -187,5 +187,69 @@ module OpenAPISourceTools
187
187
  end
188
188
  out
189
189
  end
190
+
191
+ # Single server variable object.
192
+ class ServerVariableObject
193
+ include Comparable
194
+
195
+ attr_reader :name, :default, :enum
196
+
197
+ def initialize(name, variable_object)
198
+ @name = name
199
+ @default = variable_object['default']
200
+ @enum = (variable_object['enum'] || []).sort!
201
+ end
202
+
203
+ def <=>(other)
204
+ d = @name <=> other.name
205
+ return d unless d.zero?
206
+ d = @default <=> other.default
207
+ return d unless d.zero?
208
+ @enum <=> other.enum
209
+ end
210
+ end
211
+
212
+ # Single server object with variables.
213
+ class ServerObject
214
+ include Comparable
215
+
216
+ attr_reader :url, :variables
217
+
218
+ def initialize(server_object)
219
+ @url = server_object['url']
220
+ vs = server_object['variables'] || {}
221
+ @variables = vs.keys.sort!.map do |name|
222
+ obj = vs[name]
223
+ ServerVariableObject.new(name, obj)
224
+ end
225
+ end
226
+
227
+ def <=>(other)
228
+ d = @url <=> other.url
229
+ return d unless d.zero?
230
+ if @variables.nil? || other.variables.nil?
231
+ return -1 if @variables.nil?
232
+ return 1 if other.variables.nil?
233
+ end
234
+ @variables <=> other.variables
235
+ end
236
+ end
237
+
238
+ # Combines servers array with set identifier.
239
+ class ServerAlternatives
240
+ include Comparable
241
+
242
+ attr_reader :servers
243
+ attr_accessor :set_id
244
+
245
+ def initialize(server_objects)
246
+ @servers = server_objects.map { |so| ServerObject.new(so) }
247
+ @servers.sort!
248
+ end
249
+
250
+ def <=>(other)
251
+ @servers <=> other.servers
252
+ end
253
+ end
190
254
  end
191
255
  end
@@ -76,6 +76,47 @@ module OpenAPISourceTools
76
76
  end
77
77
  uniqs.keys.sort!.map { |k| uniqs[k] }
78
78
  end
79
+
80
+ def response_codes(responses_object)
81
+ responses_object.keys.sort! do |a, b|
82
+ ad = a.downcase
83
+ bd = b.downcase
84
+ if ad == 'default'
85
+ 1
86
+ elsif bd == 'default'
87
+ -1
88
+ else
89
+ ax = ad.end_with?('x')
90
+ bx = bd.end_with?('x')
91
+ if ax && bx || !ax && !bx
92
+ a <=> b # Both numbers or patterns.
93
+ else
94
+ ax ? 1 : -1
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ def response_code_condition(code, var: 'code', op_and: '&&', op_lte: '<=', op_eq: '==')
101
+ low = []
102
+ high = []
103
+ code.downcase.each_char do |c|
104
+ if c == 'x'
105
+ low.push('0')
106
+ high.push('9')
107
+ else
108
+ low.push(c)
109
+ high.push(c)
110
+ end
111
+ end
112
+ low = low.join
113
+ high = high.join
114
+ if low == high
115
+ "#{var} #{op_eq} #{low}"
116
+ else
117
+ "(#{low} #{op_lte} #{var}) #{op_and} (#{var} #{op_lte} #{high})"
118
+ end
119
+ end
79
120
  end
80
121
 
81
122
  # Task class to add an Helper instance to Gen.h, for convenience.
@@ -46,13 +46,15 @@ module OpenAPISourceTools
46
46
  indent = 0
47
47
  blocks.each do |block|
48
48
  if block.nil?
49
- indent = 0
49
+ next
50
50
  elsif block.is_a?(Integer)
51
51
  indent += block
52
+ indent = 0 if indent.negative?
52
53
  elsif block.is_a?(TrueClass)
53
54
  indent += @config.indent_step
54
55
  elsif block.is_a?(FalseClass)
55
56
  indent -= @config.indent_step
57
+ indent = 0 if indent.negative?
56
58
  else
57
59
  block = block.to_s unless block.is_a?(String)
58
60
  if block.empty?
@@ -5,7 +5,7 @@
5
5
 
6
6
  module OpenAPISourceTools
7
7
  NAME = 'openapi-sourcetools'
8
- VERSION = '0.8.1'
8
+ VERSION = '0.9.0'
9
9
 
10
10
  def self.info(separator = ': ')
11
11
  "#{NAME}#{separator}#{VERSION}"
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.8.1
4
+ version: 0.9.0
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: 2025-01-31 00:00:00.000000000 Z
11
+ date: 2025-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deep_merge
@@ -47,6 +47,7 @@ executables:
47
47
  - openapi-generate
48
48
  - openapi-merge
49
49
  - openapi-modifypaths
50
+ - openapi-patterntests
50
51
  - openapi-processpaths
51
52
  extensions: []
52
53
  extra_rdoc_files: []
@@ -62,6 +63,7 @@ files:
62
63
  - bin/openapi-generate
63
64
  - bin/openapi-merge
64
65
  - bin/openapi-modifypaths
66
+ - bin/openapi-patterntests
65
67
  - bin/openapi-processpaths
66
68
  - lib/openapi/sourcetools.rb
67
69
  - lib/openapi/sourcetools/apiobjects.rb