openapi-sourcetools 0.9.2 → 0.11.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 +9 -25
- data/bin/openapi-addparameters +11 -10
- data/bin/openapi-addresponses +8 -24
- data/bin/openapi-addschemas +7 -9
- data/bin/openapi-addsecurityschemes +2 -2
- data/bin/openapi-checkschemas +2 -2
- data/bin/openapi-clearrefs +46 -0
- data/bin/openapi-frequencies +2 -2
- data/bin/openapi-generate +2 -2
- data/bin/openapi-merge +34 -14
- data/bin/openapi-modifypaths +2 -2
- data/bin/openapi-patterntests +2 -2
- data/bin/openapi-processpaths +2 -2
- data/lib/openapi/sourcetools/apiobjects.rb +183 -1
- data/lib/openapi/sourcetools/common.rb +17 -2
- data/lib/openapi/sourcetools/generate.rb +1 -0
- data/lib/openapi/sourcetools/output.rb +23 -2
- data/lib/openapi/sourcetools/version.rb +2 -2
- metadata +5 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2b790daa9dc40626f360f32ba98853c2b5410e5214d0d2ef32334df1858a9262
|
|
4
|
+
data.tar.gz: 842e1b54d72995112b38f678f3b0f853d9e0e6ea03652d56797715cb215e2c35
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5fa1f5ac001251e2d5b56b701bac7d8489efd01fdbbfc2884a0be84c7c5f86dd1a85d1e1cff6120ae09d0d462912711a70ad269d2c5c740c4639a6e495057e78
|
|
7
|
+
data.tar.gz: ec9dc7d99bfcd12387d163a88c7502052c44cc0159c7bd5865d86a17af0511e979224289ec56f7f049400f4280e02fa583498a553791be9f1b762153cac4b903
|
data/LICENSE.txt
CHANGED
data/bin/openapi-addheaders
CHANGED
|
@@ -1,37 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Copyright © 2024-
|
|
4
|
+
# Copyright © 2024-2026 Ismo Kärkkäinen
|
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
6
|
|
|
7
7
|
require_relative '../lib/openapi/sourcetools/apiobjects'
|
|
8
8
|
require_relative '../lib/openapi/sourcetools/common'
|
|
9
9
|
require 'optparse'
|
|
10
10
|
require 'yaml'
|
|
11
|
-
include OpenAPISourceTools
|
|
12
11
|
|
|
13
12
|
|
|
14
|
-
def replace_headers(obj, components)
|
|
15
|
-
return unless obj.is_a?(Hash)
|
|
16
|
-
obj.each do |k, v|
|
|
17
|
-
if k == 'headers'
|
|
18
|
-
v.keys.sort!.each do |name|
|
|
19
|
-
r = v[name]
|
|
20
|
-
next unless r.is_a?(Hash) # Could complain.
|
|
21
|
-
next if r.key?('$ref')
|
|
22
|
-
v[name] = { '$ref' => components.reference(r) }
|
|
23
|
-
end
|
|
24
|
-
else
|
|
25
|
-
replace_headers(v, components)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
13
|
def main
|
|
31
14
|
input_name = nil
|
|
32
15
|
output_name = nil
|
|
33
16
|
path = %w[components headers]
|
|
34
|
-
components = ApiObjects::Components.new(path, 'Header')
|
|
17
|
+
components = OpenAPISourceTools::ApiObjects::Components.new(path, 'Header')
|
|
18
|
+
replacer = OpenAPISourceTools::ApiObjects::ValueSubValueReplacer.new('headers', components)
|
|
35
19
|
|
|
36
20
|
parser = OptionParser.new do |opts|
|
|
37
21
|
opts.summary_indent = ' '
|
|
@@ -59,15 +43,15 @@ replaces the original with reference.
|
|
|
59
43
|
end
|
|
60
44
|
parser.order!
|
|
61
45
|
|
|
62
|
-
doc = Common.load_source(input_name)
|
|
46
|
+
doc = OpenAPISourceTools::Common.load_source(input_name)
|
|
63
47
|
return 2 if doc.nil?
|
|
64
48
|
|
|
65
49
|
components.items = doc.dig(*path) || {}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Common.bury(doc, path, components.items) unless components.items.empty?
|
|
50
|
+
replacer.replace(doc.dig('components', 'responses') || {})
|
|
51
|
+
replacer.replace(doc.fetch('paths', {}))
|
|
52
|
+
OpenAPISourceTools::Common.bury(doc, path, components.items) unless components.items.empty?
|
|
69
53
|
|
|
70
|
-
Common.dump_result(output_name, doc, 3)
|
|
54
|
+
OpenAPISourceTools::Common.dump_result(output_name, doc, 3)
|
|
71
55
|
end
|
|
72
56
|
|
|
73
|
-
exit(main) if
|
|
57
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
data/bin/openapi-addparameters
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Copyright © 2024-
|
|
4
|
+
# Copyright © 2024-2026 Ismo Kärkkäinen
|
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
6
|
|
|
7
7
|
require_relative '../lib/openapi/sourcetools/apiobjects'
|
|
8
8
|
require_relative '../lib/openapi/sourcetools/common'
|
|
9
9
|
require 'optparse'
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
|
|
12
12
|
def replace_parameter(p, components)
|
|
13
13
|
return p unless p.is_a?(Hash) # Could complain.
|
|
14
14
|
return p if p.key?('$ref')
|
|
15
|
-
|
|
15
|
+
components.to_reference_object(p)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def replace_parameters(obj, components)
|
|
@@ -46,7 +46,7 @@ def add_operations_parameters(paths, referenced)
|
|
|
46
46
|
# Below that are operations objects.
|
|
47
47
|
paths.each_value do |item|
|
|
48
48
|
top_nameloc2ref = nameloc2ref(item['parameters'] || [], referenced)
|
|
49
|
-
oos = ApiObjects.operation_objects(item)
|
|
49
|
+
oos = OpenAPISourceTools::ApiObjects.operation_objects(item)
|
|
50
50
|
oos.each_value do |operation|
|
|
51
51
|
nl2r = nameloc2ref(operation['parameters'] || [], referenced)
|
|
52
52
|
params = top_nameloc2ref.merge(nl2r)
|
|
@@ -61,7 +61,7 @@ def main
|
|
|
61
61
|
output_name = nil
|
|
62
62
|
add2operations = false
|
|
63
63
|
path = %w[components parameters]
|
|
64
|
-
components = ApiObjects::Components.new(path, 'Parameter')
|
|
64
|
+
components = OpenAPISourceTools::ApiObjects::Components.new(path, 'Parameter')
|
|
65
65
|
|
|
66
66
|
parser = OptionParser.new do |opts|
|
|
67
67
|
opts.summary_indent = ' '
|
|
@@ -87,7 +87,8 @@ replaces the original with reference.
|
|
|
87
87
|
|
|
88
88
|
Optionally adds parameters to all operations objects by copying parameters
|
|
89
89
|
from path item and replacing ones referred to in operations object. The
|
|
90
|
-
path item parameters are left in place. Even empty arrays are added.
|
|
90
|
+
path item parameters are left in place. Even empty arrays are added. This
|
|
91
|
+
feature is intended to simplify code generation.
|
|
91
92
|
|
|
92
93
|
#{components.help}
|
|
93
94
|
)
|
|
@@ -96,17 +97,17 @@ path item parameters are left in place. Even empty arrays are added.
|
|
|
96
97
|
end
|
|
97
98
|
parser.order!
|
|
98
99
|
|
|
99
|
-
doc = Common.load_source(input_name)
|
|
100
|
+
doc = OpenAPISourceTools::Common.load_source(input_name)
|
|
100
101
|
return 2 if doc.nil?
|
|
101
102
|
|
|
102
103
|
components.items = doc.dig(*path) || {}
|
|
103
104
|
replace_parameters(doc.fetch('paths', {}), components)
|
|
104
|
-
Common.bury(doc, path, components.items) unless components.items.empty?
|
|
105
|
+
OpenAPISourceTools::Common.bury(doc, path, components.items) unless components.items.empty?
|
|
105
106
|
# If nothing has parameters, this adds empty arrays.
|
|
106
107
|
# Consistency might be desired for code generation templates.
|
|
107
108
|
add_operations_parameters(doc.fetch('paths', {}), components.items) if add2operations
|
|
108
109
|
|
|
109
|
-
Common.dump_result(output_name, doc, 3)
|
|
110
|
+
OpenAPISourceTools::Common.dump_result(output_name, doc, 3)
|
|
110
111
|
end
|
|
111
112
|
|
|
112
|
-
exit(main)
|
|
113
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
data/bin/openapi-addresponses
CHANGED
|
@@ -1,36 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Copyright © 2024-
|
|
4
|
+
# Copyright © 2024-2026 Ismo Kärkkäinen
|
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
6
|
|
|
7
7
|
require_relative '../lib/openapi/sourcetools/apiobjects'
|
|
8
8
|
require_relative '../lib/openapi/sourcetools/common'
|
|
9
9
|
require 'optparse'
|
|
10
|
-
include OpenAPISourceTools
|
|
11
10
|
|
|
12
11
|
|
|
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
12
|
def main
|
|
30
13
|
input_name = nil
|
|
31
14
|
output_name = nil
|
|
32
15
|
path = %w[components responses]
|
|
33
|
-
components = ApiObjects::Components.new(path, 'Response')
|
|
16
|
+
components = OpenAPISourceTools::ApiObjects::Components.new(path, 'Response')
|
|
17
|
+
replacer = OpenAPISourceTools::ApiObjects::ValueSubValueReplacer.new('responses', components)
|
|
34
18
|
|
|
35
19
|
parser = OptionParser.new do |opts|
|
|
36
20
|
opts.summary_indent = ' '
|
|
@@ -58,14 +42,14 @@ replaces the original with reference.
|
|
|
58
42
|
end
|
|
59
43
|
parser.order!
|
|
60
44
|
|
|
61
|
-
doc = Common.load_source(input_name)
|
|
45
|
+
doc = OpenAPISourceTools::Common.load_source(input_name)
|
|
62
46
|
return 2 if doc.nil?
|
|
63
47
|
|
|
64
48
|
components.items = doc.dig(*path) || {}
|
|
65
|
-
|
|
66
|
-
Common.bury(doc, path, components.items) unless components.items.empty?
|
|
49
|
+
replacer.replace(doc.fetch('paths', {}))
|
|
50
|
+
OpenAPISourceTools::Common.bury(doc, path, components.items) unless components.items.empty?
|
|
67
51
|
|
|
68
|
-
Common.dump_result(output_name, doc, 3)
|
|
52
|
+
OpenAPISourceTools::Common.dump_result(output_name, doc, 3)
|
|
69
53
|
end
|
|
70
54
|
|
|
71
|
-
exit(main) if
|
|
55
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
data/bin/openapi-addschemas
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Copyright © 2024-
|
|
4
|
+
# Copyright © 2024-2026 Ismo Kärkkäinen
|
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
6
|
|
|
7
7
|
require_relative '../lib/openapi/sourcetools/apiobjects'
|
|
8
8
|
require_relative '../lib/openapi/sourcetools/common'
|
|
9
9
|
require 'optparse'
|
|
10
|
-
include OpenAPISourceTools
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
def remove_subitem(obj, path)
|
|
@@ -27,7 +26,6 @@ def replace_inlines(obj, components, top_level_name = nil)
|
|
|
27
26
|
return true unless obj.is_a?(Hash)
|
|
28
27
|
if obj.key?('$ref')
|
|
29
28
|
components.store_anchor(obj)
|
|
30
|
-
# Here would be the place to get rid of other keys if so desired.
|
|
31
29
|
return true
|
|
32
30
|
end
|
|
33
31
|
# Is inlined, process parts recursively.
|
|
@@ -58,7 +56,7 @@ def replace_inlines(obj, components, top_level_name = nil)
|
|
|
58
56
|
end
|
|
59
57
|
r = components.ref_string(top_level_name) || components.reference(obj)
|
|
60
58
|
components.store_anchor(obj, r)
|
|
61
|
-
|
|
59
|
+
components.to_reference_object(obj, r) if top_level_name.nil?
|
|
62
60
|
true
|
|
63
61
|
end
|
|
64
62
|
|
|
@@ -82,7 +80,7 @@ def main
|
|
|
82
80
|
input_name = nil
|
|
83
81
|
output_name = nil
|
|
84
82
|
path = %w[components schemas]
|
|
85
|
-
components = ApiObjects::Components.new(path, 'Schema')
|
|
83
|
+
components = OpenAPISourceTools::ApiObjects::Components.new(path, 'Schema')
|
|
86
84
|
|
|
87
85
|
parser = OptionParser.new do |opts|
|
|
88
86
|
opts.summary_indent = ' '
|
|
@@ -109,7 +107,7 @@ Loads API document in OpenAPI format and adds a schema for each inline type.
|
|
|
109
107
|
end
|
|
110
108
|
parser.order!
|
|
111
109
|
|
|
112
|
-
doc = Common.load_source(input_name)
|
|
110
|
+
doc = OpenAPISourceTools::Common.load_source(input_name)
|
|
113
111
|
return 2 if doc.nil?
|
|
114
112
|
|
|
115
113
|
# Find schema object and remove it temporarily to prevent being
|
|
@@ -122,10 +120,10 @@ Loads API document in OpenAPI format and adds a schema for each inline type.
|
|
|
122
120
|
end
|
|
123
121
|
return 4 unless replace_inlines(doc, components)
|
|
124
122
|
components.alter_anchors
|
|
125
|
-
Common.bury(doc, path, components.items) unless components.items.empty?
|
|
123
|
+
OpenAPISourceTools::Common.bury(doc, path, components.items) unless components.items.empty?
|
|
126
124
|
replace_anchor_refs(doc, components)
|
|
127
125
|
|
|
128
|
-
Common.dump_result(output_name, doc, 3)
|
|
126
|
+
OpenAPISourceTools::Common.dump_result(output_name, doc, 3)
|
|
129
127
|
end
|
|
130
128
|
|
|
131
|
-
exit(main) if
|
|
129
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Copyright © 2025 Ismo Kärkkäinen
|
|
4
|
+
# Copyright © 2025-2026 Ismo Kärkkäinen
|
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
6
|
|
|
7
7
|
require_relative '../lib/openapi/sourcetools/apiobjects'
|
|
@@ -105,4 +105,4 @@ replaces the original with reference.
|
|
|
105
105
|
Common.dump_result(output_name, doc, 3)
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
-
exit(main)
|
|
108
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
data/bin/openapi-checkschemas
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Copyright © 2024-
|
|
4
|
+
# Copyright © 2024-2026 Ismo Kärkkäinen
|
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
6
|
|
|
7
7
|
require_relative '../lib/openapi/sourcetools/apiobjects'
|
|
@@ -214,4 +214,4 @@ input, as inlined types in requests, for example, are ignored.
|
|
|
214
214
|
Common.dump_result(output_name, YAML.dump(report, line_width: 80), 3)
|
|
215
215
|
end
|
|
216
216
|
|
|
217
|
-
exit(main) if
|
|
217
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Copyright © 2026 Ismo Kärkkäinen
|
|
5
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
|
+
|
|
7
|
+
require_relative '../lib/openapi/sourcetools/apiobjects'
|
|
8
|
+
require_relative '../lib/openapi/sourcetools/common'
|
|
9
|
+
require 'optparse'
|
|
10
|
+
require 'yaml'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main
|
|
14
|
+
input_name = nil
|
|
15
|
+
output_name = nil
|
|
16
|
+
retainer = OpenAPISourceTools::ApiObjects::ValueRetainer.new('$ref')
|
|
17
|
+
|
|
18
|
+
parser = OptionParser.new do |opts|
|
|
19
|
+
opts.summary_indent = ' '
|
|
20
|
+
opts.summary_width = 26
|
|
21
|
+
opts.banner = 'Usage: openapi-clearrefs [options]'
|
|
22
|
+
opts.separator ''
|
|
23
|
+
opts.separator 'Options:'
|
|
24
|
+
opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
|
|
25
|
+
input_name = f
|
|
26
|
+
end
|
|
27
|
+
opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
|
|
28
|
+
output_name = f
|
|
29
|
+
end
|
|
30
|
+
opts.on('-h', '--help', 'Print this help and exit.') do
|
|
31
|
+
$stdout.puts %(#{opts}
|
|
32
|
+
Loads API document in OpenAPI format and from objects with '$ref' key, deletes
|
|
33
|
+
all other keys.
|
|
34
|
+
)
|
|
35
|
+
exit 0
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
parser.order!
|
|
39
|
+
|
|
40
|
+
doc = OpenAPISourceTools::Common.load_source(input_name)
|
|
41
|
+
return 2 if doc.nil?
|
|
42
|
+
retainer.process(doc)
|
|
43
|
+
OpenAPISourceTools::Common.dump_result(output_name, doc, 3)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
data/bin/openapi-frequencies
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Copyright © 2021-
|
|
4
|
+
# Copyright © 2021-2026 Ismo Kärkkäinen
|
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
6
|
|
|
7
7
|
require_relative '../lib/openapi/sourcetools/apiobjects'
|
|
@@ -73,4 +73,4 @@ into each path object matching count as "x-openapi-sourcetools-freq".
|
|
|
73
73
|
Common.dump_result(output_name, doc, 3)
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
-
exit(main)
|
|
76
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
data/bin/openapi-generate
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Copyright © 2021-
|
|
4
|
+
# Copyright © 2021-2026 Ismo Kärkkäinen
|
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
6
|
|
|
7
7
|
require_relative '../lib/openapi/sourcetools/common'
|
|
@@ -58,4 +58,4 @@ After all generators have loaded succesfully, tasks are run.
|
|
|
58
58
|
gen.run
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
exit(main)
|
|
61
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
data/bin/openapi-merge
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Copyright © 2021-
|
|
4
|
+
# Copyright © 2021-2026 Ismo Kärkkäinen
|
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
6
|
|
|
7
7
|
require_relative '../lib/openapi/sourcetools/common'
|
|
@@ -34,18 +34,23 @@ def add_missing(existing, incoming, field)
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
def add_undefined(merged, incoming, filename, path, max_depths)
|
|
37
|
+
def add_undefined(merged, incoming, filename, path, max_depths, firsts, lasts)
|
|
38
38
|
incoming.each_pair do |key, value|
|
|
39
39
|
unless merged.key? key
|
|
40
40
|
merged[key] = value
|
|
41
41
|
next
|
|
42
42
|
end
|
|
43
|
+
next if firsts.member?(key) # When key appears the second or more times.
|
|
44
|
+
if lasts.member?(key) # If last is kept, always copy value.
|
|
45
|
+
merged[key] = value
|
|
46
|
+
next
|
|
47
|
+
end
|
|
43
48
|
m = merged[key]
|
|
44
49
|
raise_se("Path #{path_combo(path, false)} merged type #{m.class} differs from type #{value.class} in #{filename}") unless m.instance_of?(value.class)
|
|
45
50
|
raise_se("Re-definition of #{key} #{path_combo(path)} in #{filename}") if too_deep(path, max_depths)
|
|
46
51
|
if m.is_a? Hash # paths or similar
|
|
47
52
|
path.push key
|
|
48
|
-
add_undefined(m, value, filename, path, max_depths)
|
|
53
|
+
add_undefined(m, value, filename, path, max_depths, firsts, lasts)
|
|
49
54
|
path.pop
|
|
50
55
|
elsif m.is_a? Array
|
|
51
56
|
case key
|
|
@@ -121,6 +126,8 @@ end
|
|
|
121
126
|
def main
|
|
122
127
|
output_file = nil
|
|
123
128
|
keep = false
|
|
129
|
+
firsts = Set.new
|
|
130
|
+
lasts = Set.new
|
|
124
131
|
|
|
125
132
|
parser = OptionParser.new do |opts|
|
|
126
133
|
opts.summary_indent = ' '
|
|
@@ -137,6 +144,12 @@ def main
|
|
|
137
144
|
opts.on('-p', '--prune', 'Prune all unreferenced objects under components (default).') do
|
|
138
145
|
keep = false
|
|
139
146
|
end
|
|
147
|
+
opts.on('-f', '--first KEY', 'Keep only the first occurrence of top-level KEY.') do |key|
|
|
148
|
+
firsts.add(key)
|
|
149
|
+
end
|
|
150
|
+
opts.on('-l', '--last KEY', 'Keep only the last occurrence of top-level KEY.') do |key|
|
|
151
|
+
lasts.add(key)
|
|
152
|
+
end
|
|
140
153
|
opts.on('-h', '--help', 'Print this help and exit.') do
|
|
141
154
|
$stdout.puts %(#{opts}
|
|
142
155
|
|
|
@@ -148,20 +161,27 @@ allowed only to append to the merged document, not re-define anything in it.
|
|
|
148
161
|
end
|
|
149
162
|
parser.order!
|
|
150
163
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
max_depths
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
164
|
+
overlap = firsts & lasts
|
|
165
|
+
unless overlap.empty?
|
|
166
|
+
return Common.aargh("Key can only be in first or last, not both: #{overlap.to_a.sort!.join(' ')}", 1)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
max_depths = Hash.new(0).merge!({
|
|
170
|
+
'openapi' => 1,
|
|
171
|
+
'info' => 1,
|
|
172
|
+
'servers' => 1,
|
|
173
|
+
'paths' => 2, # Allows get, post, etc. from different files.
|
|
174
|
+
'webhooks' => 2,
|
|
175
|
+
'components' => 2,
|
|
176
|
+
'security' => 1,
|
|
177
|
+
'tags' => 1
|
|
178
|
+
})
|
|
179
|
+
|
|
160
180
|
merged = {}
|
|
161
181
|
ARGV.each do |filename|
|
|
162
182
|
s = Common.load_source(filename)
|
|
163
183
|
return 2 if s.nil?
|
|
164
|
-
add_undefined(merged, s, filename, [], max_depths)
|
|
184
|
+
add_undefined(merged, s, filename, [], max_depths, firsts, lasts)
|
|
165
185
|
rescue StandardError => e
|
|
166
186
|
return Common.aargh(e.to_s, 4)
|
|
167
187
|
end
|
|
@@ -171,4 +191,4 @@ allowed only to append to the merged document, not re-define anything in it.
|
|
|
171
191
|
Common.dump_result(output_file, merged, 3)
|
|
172
192
|
end
|
|
173
193
|
|
|
174
|
-
exit(main) if
|
|
194
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
data/bin/openapi-modifypaths
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Copyright © 2024-
|
|
4
|
+
# Copyright © 2024-2026 Ismo Kärkkäinen
|
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
6
|
|
|
7
7
|
require_relative '../lib/openapi/sourcetools/common'
|
|
@@ -121,4 +121,4 @@ STR and PREFIX are expected to be parts of a path surrounded by /.
|
|
|
121
121
|
Common.dump_result(output_name, doc, 3)
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
-
exit(main) if
|
|
124
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
data/bin/openapi-patterntests
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Copyright © 2025 Ismo Kärkkäinen
|
|
4
|
+
# Copyright © 2025-2026 Ismo Kärkkäinen
|
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
6
|
|
|
7
7
|
require_relative '../lib/openapi/sourcetools/common'
|
|
@@ -163,4 +163,4 @@ strings for testing generated code.
|
|
|
163
163
|
Common.dump_result(output_name, ex, 3)
|
|
164
164
|
end
|
|
165
165
|
|
|
166
|
-
exit(main) if
|
|
166
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
data/bin/openapi-processpaths
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Copyright © 2021-
|
|
4
|
+
# Copyright © 2021-2026 Ismo Kärkkäinen
|
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
6
6
|
|
|
7
7
|
require_relative '../lib/openapi/sourcetools/apiobjects'
|
|
@@ -70,4 +70,4 @@ key. Checks for paths that may be ambiguous.
|
|
|
70
70
|
Common.dump_result(output_name, doc, 3)
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
exit(main)
|
|
73
|
+
exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
|
|
@@ -37,10 +37,99 @@ module OpenAPISourceTools
|
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
# Any value with given key has all but retained keys removed.
|
|
41
|
+
class ValueRetainer
|
|
42
|
+
attr_reader :key
|
|
43
|
+
attr_accessor :retain
|
|
44
|
+
|
|
45
|
+
def initialize(trigger_key)
|
|
46
|
+
@key = trigger_key
|
|
47
|
+
@retain = [ trigger_key ]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def process(obj)
|
|
51
|
+
if obj.is_a?(Array)
|
|
52
|
+
obj.each { |item| process(item) }
|
|
53
|
+
return
|
|
54
|
+
end
|
|
55
|
+
return unless obj.is_a?(Hash)
|
|
56
|
+
if obj.key?(@key)
|
|
57
|
+
obj.delete_if { |k, _v| !@retain.include?(k) }
|
|
58
|
+
end
|
|
59
|
+
obj.each_value do |value|
|
|
60
|
+
process(value)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# A replacer for value of a given key.
|
|
66
|
+
class ValueReplacer
|
|
67
|
+
attr_reader :key, :components
|
|
68
|
+
|
|
69
|
+
def initialize(trigger_key, components)
|
|
70
|
+
@key = trigger_key
|
|
71
|
+
@components = components
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def replace(obj)
|
|
75
|
+
if obj.is_a?(Array)
|
|
76
|
+
obj.each { |item| replace(item) }
|
|
77
|
+
return
|
|
78
|
+
end
|
|
79
|
+
return unless obj.is_a?(Hash)
|
|
80
|
+
obj.each do |key, value|
|
|
81
|
+
if key == @key
|
|
82
|
+
if value.is_a?(Array)
|
|
83
|
+
value.each do |item|
|
|
84
|
+
next unless item.is_a?(Hash)
|
|
85
|
+
next if item.key?('$ref')
|
|
86
|
+
@components.to_reference_object(item)
|
|
87
|
+
end
|
|
88
|
+
elsif value.is_a?(Hash)
|
|
89
|
+
next if value.key?('$ref')
|
|
90
|
+
@components.to_reference_object(value)
|
|
91
|
+
end
|
|
92
|
+
else
|
|
93
|
+
replace(value)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# A replacer for sub-values of a value of a given key.
|
|
100
|
+
class ValueSubValueReplacer
|
|
101
|
+
attr_reader :key, :components
|
|
102
|
+
|
|
103
|
+
def initialize(trigger_key, components)
|
|
104
|
+
@key = trigger_key
|
|
105
|
+
@components = components
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def replace(obj)
|
|
109
|
+
if obj.is_a?(Array)
|
|
110
|
+
obj.each { |item| replace(item) }
|
|
111
|
+
return
|
|
112
|
+
end
|
|
113
|
+
return unless obj.is_a?(Hash)
|
|
114
|
+
obj.each do |key, value|
|
|
115
|
+
if key == @key && value.is_a?(Hash)
|
|
116
|
+
value.keys.sort!.each do |sub_key|
|
|
117
|
+
sub_value = value[sub_key]
|
|
118
|
+
next unless sub_value.is_a?(Hash)
|
|
119
|
+
next if sub_value.key?('$ref')
|
|
120
|
+
@components.to_reference_object(value[sub_key])
|
|
121
|
+
end
|
|
122
|
+
else
|
|
123
|
+
replace(value)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
40
129
|
# A component in the API specification for reference and anchor handling.
|
|
41
130
|
class Components
|
|
42
131
|
attr_reader :path, :prefix, :anchor2ref, :schema_names
|
|
43
|
-
attr_accessor :items, :ignored_keys
|
|
132
|
+
attr_accessor :items, :ignored_keys, :retain_ignored
|
|
44
133
|
|
|
45
134
|
def initialize(path, prefix, ignored_keys = %w[summary description examples example $anchor])
|
|
46
135
|
path = "#/#{path.join('/')}/" if path.is_a?(Array)
|
|
@@ -51,6 +140,7 @@ module OpenAPISourceTools
|
|
|
51
140
|
@schema_names = Set.new
|
|
52
141
|
@items = {}
|
|
53
142
|
@ignored_keys = Set.new(ignored_keys)
|
|
143
|
+
@retain_ignored = false
|
|
54
144
|
end
|
|
55
145
|
|
|
56
146
|
def add_options(opts)
|
|
@@ -60,6 +150,9 @@ module OpenAPISourceTools
|
|
|
60
150
|
opts.on('--ignore FIELD', 'Ignore FIELD in comparisons.') do |f|
|
|
61
151
|
@ignored_keys.add(f)
|
|
62
152
|
end
|
|
153
|
+
opts.on('--retain-ignored', 'Retain ignored fields in reference object.') do
|
|
154
|
+
@retain_ignored = true
|
|
155
|
+
end
|
|
63
156
|
end
|
|
64
157
|
|
|
65
158
|
def help
|
|
@@ -91,6 +184,17 @@ module OpenAPISourceTools
|
|
|
91
184
|
end
|
|
92
185
|
end
|
|
93
186
|
|
|
187
|
+
def to_reference_object(obj, ref = nil)
|
|
188
|
+
ref = reference(obj) if ref.nil?
|
|
189
|
+
if @retain_ignored
|
|
190
|
+
obj.delete_if { |k, _v| !@ignored_keys.member?(k) }
|
|
191
|
+
else
|
|
192
|
+
obj.clear
|
|
193
|
+
end
|
|
194
|
+
obj['$ref'] = ref
|
|
195
|
+
obj
|
|
196
|
+
end
|
|
197
|
+
|
|
94
198
|
def store_anchor(obj, ref = nil)
|
|
95
199
|
anchor_name = obj['$anchor']
|
|
96
200
|
return if anchor_name.nil?
|
|
@@ -251,5 +355,83 @@ module OpenAPISourceTools
|
|
|
251
355
|
@servers <=> other.servers
|
|
252
356
|
end
|
|
253
357
|
end
|
|
358
|
+
|
|
359
|
+
# Simple holder if security scheme objects.
|
|
360
|
+
class SecurityScheme
|
|
361
|
+
include Comparable
|
|
362
|
+
|
|
363
|
+
attr_reader :name, :scheme
|
|
364
|
+
|
|
365
|
+
def initialize(name, scheme)
|
|
366
|
+
@name = name
|
|
367
|
+
@scheme = scheme
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def <=>(other)
|
|
371
|
+
@name <=> other.name
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# Simple holder that determines where parameters are placed.
|
|
376
|
+
class SecurityRequirementObject
|
|
377
|
+
include Comparable
|
|
378
|
+
|
|
379
|
+
attr_reader :reqs, :extras
|
|
380
|
+
|
|
381
|
+
def initialize(reqs, schemes)
|
|
382
|
+
@reqs = reqs
|
|
383
|
+
@extras = {}
|
|
384
|
+
@reqs.each do |name, scopes|
|
|
385
|
+
scheme = schemes[name].scheme
|
|
386
|
+
case scheme['type']
|
|
387
|
+
when 'apiKey'
|
|
388
|
+
if scheme['in'] == 'header'
|
|
389
|
+
h = @extras[:headers] || {}
|
|
390
|
+
h[scheme['name']] = scopes
|
|
391
|
+
@extras[:headers] = h
|
|
392
|
+
elsif scheme['in'] == 'query'
|
|
393
|
+
q = @extras[:query] || {}
|
|
394
|
+
q[scheme['name']] = scopes
|
|
395
|
+
@extras[:query] = q
|
|
396
|
+
end
|
|
397
|
+
when 'http', 'oauth2', 'openIdConnect'
|
|
398
|
+
h = @extras[:headers] || {}
|
|
399
|
+
h['authorization'] = scopes
|
|
400
|
+
@extras[:headers] = h
|
|
401
|
+
else
|
|
402
|
+
raise "Unknown security type: #{scheme['type']} for #{name}"
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def <=>(other)
|
|
408
|
+
d = @reqs.size <=> other.reqs.size
|
|
409
|
+
return d unless d.zero?
|
|
410
|
+
d = @reqs.keys.sort! <=> other.reqs.keys.sort!
|
|
411
|
+
return d unless d.zero?
|
|
412
|
+
@reqs.keys.sort!.each do |name|
|
|
413
|
+
d = @reqs[name].sort <=> other.reqs[name].sort
|
|
414
|
+
return d unless d.zero?
|
|
415
|
+
end
|
|
416
|
+
0
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# Available possibilities.
|
|
421
|
+
class SecurityAlternatives
|
|
422
|
+
include Comparable
|
|
423
|
+
|
|
424
|
+
attr_reader :sros, :schemes
|
|
425
|
+
attr_accessor :set_id
|
|
426
|
+
|
|
427
|
+
def initialize(sros, schemes)
|
|
428
|
+
@sros = sros
|
|
429
|
+
@schemes = schemes
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def <=>(other)
|
|
433
|
+
@sros.sort <=> other.sros.sort
|
|
434
|
+
end
|
|
435
|
+
end
|
|
254
436
|
end
|
|
255
437
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Copyright © 2021-
|
|
3
|
+
# Copyright © 2021-2026 Ismo Kärkkäinen
|
|
4
4
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
5
5
|
|
|
6
6
|
require 'pathname'
|
|
@@ -56,6 +56,19 @@ module OpenAPISourceTools
|
|
|
56
56
|
parts
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
# Occasionally saved YAML has aliases. This is added to avoid them.
|
|
60
|
+
def self.ensure_separate(obj)
|
|
61
|
+
if obj.is_a?(Array)
|
|
62
|
+
return obj.map { |v| ensure_separate(v) }
|
|
63
|
+
end
|
|
64
|
+
return obj unless obj.is_a?(Hash)
|
|
65
|
+
out = {}
|
|
66
|
+
obj.each do |key, value|
|
|
67
|
+
out[key] = ensure_separate(value)
|
|
68
|
+
end
|
|
69
|
+
out
|
|
70
|
+
end
|
|
71
|
+
|
|
59
72
|
def self.load_source(input)
|
|
60
73
|
YAML.safe_load(input.nil? ? $stdin : File.read(input))
|
|
61
74
|
rescue Errno::ENOENT
|
|
@@ -65,7 +78,9 @@ module OpenAPISourceTools
|
|
|
65
78
|
end
|
|
66
79
|
|
|
67
80
|
def self.dump_result(output, doc, error_return)
|
|
68
|
-
|
|
81
|
+
# Safe load did not allow aliases, but saved output may have them.
|
|
82
|
+
# Observed with empty object or array.
|
|
83
|
+
doc = YAML.dump(ensure_separate(doc), line_width: 1_000_000) unless doc.is_a?(String)
|
|
69
84
|
if output.nil?
|
|
70
85
|
$stdout.puts doc
|
|
71
86
|
else
|
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Copyright © 2024-
|
|
3
|
+
# Copyright © 2024-2026 Ismo Kärkkäinen
|
|
4
4
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
5
5
|
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'yaml'
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
module OpenAPISourceTools
|
|
8
11
|
# Output configuration settings for easy storage.
|
|
9
12
|
# You can have it in configuration and pass hash to initialize.
|
|
10
13
|
class OutputConfiguration
|
|
11
14
|
attr_reader :indent_character, :indent_step
|
|
12
|
-
attr_reader :tab, :tab_replaces_count
|
|
15
|
+
attr_reader :tab, :tab_replaces_count, :prettified_json
|
|
13
16
|
|
|
14
17
|
def initialize(cfg = {})
|
|
15
18
|
@indent_character = cfg['indent_character'] || ' '
|
|
16
19
|
@indent_step = cfg['indent_step'] || 4
|
|
17
20
|
@tab = cfg['tab'] || "\t"
|
|
18
21
|
@tab_replaces_count = cfg['tab_replaces_count'] || 0
|
|
22
|
+
@prettified_json = {
|
|
23
|
+
array_nl: "\n",
|
|
24
|
+
object_nl: "\n",
|
|
25
|
+
indent: ' ',
|
|
26
|
+
space: ' '
|
|
27
|
+
}.merge(cfg['prettified_json'] || {})
|
|
19
28
|
end
|
|
20
29
|
end
|
|
21
30
|
|
|
@@ -81,5 +90,17 @@ module OpenAPISourceTools
|
|
|
81
90
|
@last_indent = indent
|
|
82
91
|
indented.join(separator)
|
|
83
92
|
end
|
|
93
|
+
|
|
94
|
+
def yaml(something)
|
|
95
|
+
YAML.dump(something)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def json(something)
|
|
99
|
+
JSON.generate(something)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def pretty_json(something)
|
|
103
|
+
JSON.generate(something, @config.prettified_json)
|
|
104
|
+
end
|
|
84
105
|
end
|
|
85
106
|
end
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Copyright © 2025 Ismo Kärkkäinen
|
|
3
|
+
# Copyright © 2025-2026 Ismo Kärkkäinen
|
|
4
4
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
|
5
5
|
|
|
6
6
|
module OpenAPISourceTools
|
|
7
7
|
NAME = 'openapi-sourcetools'
|
|
8
|
-
VERSION = '0.
|
|
8
|
+
VERSION = '0.11.0'
|
|
9
9
|
|
|
10
10
|
def self.info(separator = ': ')
|
|
11
11
|
"#{NAME}#{separator}#{VERSION}"
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: openapi-sourcetools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ismo Kärkkäinen
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: deep_merge
|
|
@@ -43,6 +42,7 @@ executables:
|
|
|
43
42
|
- openapi-addschemas
|
|
44
43
|
- openapi-addsecurityschemes
|
|
45
44
|
- openapi-checkschemas
|
|
45
|
+
- openapi-clearrefs
|
|
46
46
|
- openapi-frequencies
|
|
47
47
|
- openapi-generate
|
|
48
48
|
- openapi-merge
|
|
@@ -59,6 +59,7 @@ files:
|
|
|
59
59
|
- bin/openapi-addschemas
|
|
60
60
|
- bin/openapi-addsecurityschemes
|
|
61
61
|
- bin/openapi-checkschemas
|
|
62
|
+
- bin/openapi-clearrefs
|
|
62
63
|
- bin/openapi-frequencies
|
|
63
64
|
- bin/openapi-generate
|
|
64
65
|
- bin/openapi-merge
|
|
@@ -82,7 +83,6 @@ licenses:
|
|
|
82
83
|
- UPL-1.0
|
|
83
84
|
metadata:
|
|
84
85
|
rubygems_mfa_required: 'true'
|
|
85
|
-
post_install_message:
|
|
86
86
|
rdoc_options: []
|
|
87
87
|
require_paths:
|
|
88
88
|
- lib
|
|
@@ -97,8 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
97
97
|
- !ruby/object:Gem::Version
|
|
98
98
|
version: '0'
|
|
99
99
|
requirements: []
|
|
100
|
-
rubygems_version: 3.
|
|
101
|
-
signing_key:
|
|
100
|
+
rubygems_version: 3.6.9
|
|
102
101
|
specification_version: 4
|
|
103
102
|
summary: Tools for creating source code from API specification.
|
|
104
103
|
test_files: []
|