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
data/lib/helper.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright © 2024 Ismo Kärkkäinen
|
4
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
5
|
+
|
6
|
+
require_relative 'task'
|
7
|
+
|
8
|
+
|
9
|
+
class Helper
|
10
|
+
attr_reader :doc, :parents
|
11
|
+
attr_accessor :parent_parameters
|
12
|
+
|
13
|
+
# Stores the nearesh Hash for each Hash.
|
14
|
+
def store_parents(obj, parent = nil)
|
15
|
+
if obj.is_a?(Hash)
|
16
|
+
@parents[obj.object_id] = parent
|
17
|
+
obj.each do |k, v|
|
18
|
+
store_parents(v, obj)
|
19
|
+
end
|
20
|
+
elsif obj.is_a?(Array)
|
21
|
+
obj.each do |v|
|
22
|
+
store_parents(v, parent)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(doc)
|
28
|
+
@doc = doc
|
29
|
+
# For each hash in doc, set parent?
|
30
|
+
# Build an object_id to parent object mapping and use parent method?
|
31
|
+
@parents = {}
|
32
|
+
store_parents(@doc)
|
33
|
+
end
|
34
|
+
|
35
|
+
def parent(object)
|
36
|
+
@parents[object.object_id]
|
37
|
+
end
|
38
|
+
|
39
|
+
COMPONENTS = '#/components/'
|
40
|
+
|
41
|
+
def category_and_name(ref_or_obj)
|
42
|
+
ref = ref_or_obj.is_a?(Hash) ? ref_or_obj['$ref'] : ref_or_obj
|
43
|
+
return nil unless ref.is_a?(String)
|
44
|
+
return nil unless ref.start_with?(Helper::COMPONENTS)
|
45
|
+
idx = ref.index('/', Helper::COMPONENTS.size)
|
46
|
+
return nil if idx.nil?
|
47
|
+
category = ref[Helper::COMPONENTS.size...idx]
|
48
|
+
[ category, ref[(idx + 1)...ref.size] ]
|
49
|
+
end
|
50
|
+
|
51
|
+
def dereference(ref_or_obj)
|
52
|
+
cn = category_and_name(ref_or_obj)
|
53
|
+
return nil if cn.nil?
|
54
|
+
cs = @doc.dig('components', cn.first) || {}
|
55
|
+
cs[cn.last]
|
56
|
+
end
|
57
|
+
|
58
|
+
def basename(ref_or_obj)
|
59
|
+
cn = category_and_name(ref_or_obj)
|
60
|
+
return nil if cn.nil?
|
61
|
+
cn.last
|
62
|
+
end
|
63
|
+
|
64
|
+
def parameters(operation_object, empty_unless_local = false)
|
65
|
+
return [] if empty_unless_local && !operation_object.key?('parameters')
|
66
|
+
cps = @doc.dig('components', 'parameters') || {}
|
67
|
+
uniqs = {}
|
68
|
+
path_item_object = parent(operation_object)
|
69
|
+
[path_item_object, operation_object].each do |p|
|
70
|
+
p.fetch('parameters', []).each do |param|
|
71
|
+
r = basename(param)
|
72
|
+
r = cps[r] if r.is_a?(String)
|
73
|
+
uniqs["#{r['name']}:#{r['in']}"] = param
|
74
|
+
end
|
75
|
+
end
|
76
|
+
uniqs.keys.sort!.map { |k| uniqs[k] }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
class HelperTask
|
82
|
+
include TaskInterface
|
83
|
+
|
84
|
+
def generate(context_binding)
|
85
|
+
Gen.h = Helper.new(Gen.doc)
|
86
|
+
end
|
87
|
+
|
88
|
+
def output_name
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
|
92
|
+
def discard
|
93
|
+
true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
data/lib/loaders.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright © 2024 Ismo Kärkkäinen
|
4
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
5
|
+
|
6
|
+
module Loaders
|
7
|
+
|
8
|
+
GEM_PREFIX = 'gem:'
|
9
|
+
|
10
|
+
def self.gem_loader(name)
|
11
|
+
return false unless name.downcase.start_with?(GEM_PREFIX)
|
12
|
+
begin
|
13
|
+
require(name.slice(GEM_PREFIX.size...name.size))
|
14
|
+
rescue LoadError => e
|
15
|
+
raise StandardError, "Failed to require #{name}\n#{e.to_s}"
|
16
|
+
rescue Exception => e
|
17
|
+
raise StandardError, "Problem with #{name}\n#{e.to_s}"
|
18
|
+
end
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
RUBY_EXT = '.rb'
|
23
|
+
|
24
|
+
def self.ruby_loader(name)
|
25
|
+
return false unless name.downcase.end_with?(RUBY_EXT)
|
26
|
+
origwd = Dir.pwd
|
27
|
+
d = File.dirname(name)
|
28
|
+
Dir.chdir(d) unless d == '.'
|
29
|
+
begin
|
30
|
+
require(File.join(Dir.pwd, File.basename(name)))
|
31
|
+
rescue LoadError => e
|
32
|
+
raise StandardError, "Failed to require #{name}\n#{e.to_s}"
|
33
|
+
rescue Exception => e
|
34
|
+
raise StandardError, "Problem with #{name}\n#{e.to_s}"
|
35
|
+
end
|
36
|
+
Dir.chdir(origwd) unless d == '.'
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.loaders
|
41
|
+
[ method(:gem_loader), method(:ruby_loader) ]
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.document
|
45
|
+
%(
|
46
|
+
- #{Loaders::GEM_PREFIX}gem_name : requires the gem.
|
47
|
+
- ruby_file#{Loaders::RUBY_EXT} : changes to Ruby file directory and requires the file.
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/lib/task.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright © 2024 Ismo Kärkkäinen
|
4
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
5
|
+
|
6
|
+
require 'erb'
|
7
|
+
require_relative 'common'
|
8
|
+
|
9
|
+
|
10
|
+
module TaskInterface
|
11
|
+
def generate(context_binding)
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
def output_name
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
def discard
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def executable
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Task
|
29
|
+
include TaskInterface
|
30
|
+
|
31
|
+
attr_reader :src, :template, :template_name
|
32
|
+
attr_accessor :name, :executable, :discard, :x
|
33
|
+
|
34
|
+
def initialize(src, template, template_name)
|
35
|
+
@src = src
|
36
|
+
@template = template
|
37
|
+
@template_name = template_name
|
38
|
+
if @template.nil?
|
39
|
+
raise ArgumentError, "template_name or template must be given" if @template_name.nil?
|
40
|
+
begin
|
41
|
+
@template = File.read(@template_name)
|
42
|
+
rescue Errno::ENOENT
|
43
|
+
raise StandardError, "Could not load #{@template_name}"
|
44
|
+
rescue StandardError => e
|
45
|
+
raise StandardError, "#{e}\nFailed to read #{@template_name}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
@name = nil
|
49
|
+
@executable = false
|
50
|
+
@discard = false
|
51
|
+
@x = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# If this is overridden to perform some processing but not to produce output,
|
55
|
+
# set @discard = true and return value will be ignored. No other methods are
|
56
|
+
# called in that case.
|
57
|
+
def internal_generate(context_binding)
|
58
|
+
ERB.new(@template).result(context_binding)
|
59
|
+
end
|
60
|
+
|
61
|
+
# You can override this instead of internal_generate if you do not need the
|
62
|
+
# exception handling.
|
63
|
+
def generate(context_binding)
|
64
|
+
n = @template_name.nil? ? '' : "#{@template_name} "
|
65
|
+
internal_generate(context_binding)
|
66
|
+
rescue SyntaxError => e
|
67
|
+
aargh("Template #{n}syntax error: #{e.full_message}", 5)
|
68
|
+
rescue Exception => e
|
69
|
+
aargh("Template #{n}error: #{e.full_message}", 6)
|
70
|
+
end
|
71
|
+
|
72
|
+
# This is only called when generate produced output that is not discarded.
|
73
|
+
def output_name
|
74
|
+
return @name unless @name.nil?
|
75
|
+
# Using template name may show where name assignment is missing.
|
76
|
+
# Name assignment may also be missing in the task creation stage.
|
77
|
+
return File.basename(@template_name) unless @template_name.nil?
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class WriteTask
|
83
|
+
include TaskInterface
|
84
|
+
|
85
|
+
attr_reader :name, :contents, :executable
|
86
|
+
|
87
|
+
def initialize(name, contents, executable = false)
|
88
|
+
raise ArgumentError, "name and contents must be given" if name.nil? || contents.nil?
|
89
|
+
@name = name
|
90
|
+
@contents = contents
|
91
|
+
@executable = executable
|
92
|
+
end
|
93
|
+
|
94
|
+
def generate(context_binding)
|
95
|
+
@contents
|
96
|
+
end
|
97
|
+
|
98
|
+
def output_name
|
99
|
+
@name
|
100
|
+
end
|
101
|
+
end
|
metadata
CHANGED
@@ -1,37 +1,56 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openapi-sourcetools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.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:
|
11
|
+
date: 2024-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
|
15
|
-
Tools for
|
15
|
+
Tools for handling API specification in OpenAPI format. Replacement of
|
16
|
+
duplicate definitions with references. Other checks. Does not validate
|
17
|
+
the document against OpenAPI format specification.
|
16
18
|
email: ismokarkkainen@icloud.com
|
17
19
|
executables:
|
18
|
-
- openapi-
|
20
|
+
- openapi-addheaders
|
21
|
+
- openapi-addparameters
|
22
|
+
- openapi-addresponses
|
23
|
+
- openapi-addschemas
|
24
|
+
- openapi-checkschemas
|
19
25
|
- openapi-frequencies
|
26
|
+
- openapi-generate
|
20
27
|
- openapi-merge
|
21
28
|
- openapi-processpaths
|
22
29
|
extensions: []
|
23
30
|
extra_rdoc_files: []
|
24
31
|
files:
|
25
32
|
- LICENSE.txt
|
33
|
+
- bin/openapi-addheaders
|
34
|
+
- bin/openapi-addparameters
|
35
|
+
- bin/openapi-addresponses
|
36
|
+
- bin/openapi-addschemas
|
37
|
+
- bin/openapi-checkschemas
|
26
38
|
- bin/openapi-frequencies
|
27
|
-
- bin/openapi-
|
39
|
+
- bin/openapi-generate
|
28
40
|
- bin/openapi-merge
|
29
41
|
- bin/openapi-processpaths
|
42
|
+
- lib/apiobjects.rb
|
30
43
|
- lib/common.rb
|
44
|
+
- lib/gen.rb
|
45
|
+
- lib/generate.rb
|
46
|
+
- lib/helper.rb
|
47
|
+
- lib/loaders.rb
|
48
|
+
- lib/task.rb
|
31
49
|
homepage: https://xn--ismo-krkkinen-gfbd.fi/openapi-sourcetools/index.html
|
32
50
|
licenses:
|
33
51
|
- UPL-1.0
|
34
|
-
metadata:
|
52
|
+
metadata:
|
53
|
+
rubygems_mfa_required: 'true'
|
35
54
|
post_install_message:
|
36
55
|
rdoc_options: []
|
37
56
|
require_paths:
|
@@ -40,14 +59,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
40
59
|
requirements:
|
41
60
|
- - ">="
|
42
61
|
- !ruby/object:Gem::Version
|
43
|
-
version:
|
62
|
+
version: 3.0.0
|
44
63
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
64
|
requirements:
|
46
65
|
- - ">="
|
47
66
|
- !ruby/object:Gem::Version
|
48
67
|
version: '0'
|
49
68
|
requirements: []
|
50
|
-
rubygems_version: 3.
|
69
|
+
rubygems_version: 3.2.33
|
51
70
|
signing_key:
|
52
71
|
specification_version: 4
|
53
72
|
summary: Tools for creating source code from API specification.
|
data/bin/openapi-generatecode
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
# Copyright © 2021 Ismo Kärkkäinen
|
5
|
-
# Licensed under Universal Permissive License. See LICENSE.txt.
|
6
|
-
|
7
|
-
require_relative '../lib/common.rb'
|
8
|
-
require 'optparse'
|
9
|
-
require 'yaml'
|
10
|
-
require 'erb'
|
11
|
-
|
12
|
-
|
13
|
-
default_env(:out, '')
|
14
|
-
default_env(:in, '')
|
15
|
-
default_env(:template, '')
|
16
|
-
|
17
|
-
ENV['POSIXLY_CORRECT'] = '1'
|
18
|
-
parser = OptionParser.new do |opts|
|
19
|
-
opts.summary_indent = ' '
|
20
|
-
opts.summary_width = 26
|
21
|
-
opts.banner = 'Usage: openapi-generatecode [options] [additions...]'
|
22
|
-
opts.separator ''
|
23
|
-
opts.separator 'Options (equivalent environment variable and value in parentheses):'
|
24
|
-
opts.on('-i', '--input FILE', 'Read processed API from FILE, not stdin (IN=FILE).') do |f|
|
25
|
-
env(:in, f)
|
26
|
-
end
|
27
|
-
opts.on('-o', '--output FILE', 'Output result to FILE, not stdout (OUT=FILE).') do |f|
|
28
|
-
env(:out, f)
|
29
|
-
end
|
30
|
-
opts.on('-t', '--template FILE', 'Read template from FILE (TEMPLATE=FILE).') do |f|
|
31
|
-
env(:template, f)
|
32
|
-
end
|
33
|
-
opts.on('-h', '--help', 'Print this help and exit.') do
|
34
|
-
$stdout.puts %(#{opts}
|
35
|
-
|
36
|
-
Loads ERB template and optional additions to a context along with the processed
|
37
|
-
API document and produces the template result.
|
38
|
-
)
|
39
|
-
exit 0
|
40
|
-
end
|
41
|
-
end
|
42
|
-
parser.parse!
|
43
|
-
|
44
|
-
aargh('Template file name must be given.', 1) if env(:template).empty?
|
45
|
-
|
46
|
-
def load_content(name)
|
47
|
-
name.empty? ? $stdin.read : File.read(name)
|
48
|
-
rescue Errno::ENOENT
|
49
|
-
aargh("Could not load #{name || 'stdin'}", 2)
|
50
|
-
rescue StandardError => e
|
51
|
-
aargh("#{e}\nFailed to read #{name || 'stdin'}", 2)
|
52
|
-
end
|
53
|
-
|
54
|
-
class Generator
|
55
|
-
attr_accessor :addition, :order, :full_name_order, :template, :document
|
56
|
-
|
57
|
-
def initialize(document_content, template_content)
|
58
|
-
@addition = Hash.new
|
59
|
-
@order = []
|
60
|
-
@full_name_order = []
|
61
|
-
@document = document_content
|
62
|
-
@template = template_content
|
63
|
-
end
|
64
|
-
|
65
|
-
def get_binding
|
66
|
-
binding
|
67
|
-
end
|
68
|
-
|
69
|
-
def add(name, content, strip_suffix)
|
70
|
-
@full_name_order.push({ filename: name, contents: content })
|
71
|
-
name = File.basename(name)
|
72
|
-
@order.push name
|
73
|
-
if strip_suffix
|
74
|
-
idx = name.rindex('.')
|
75
|
-
name = name.slice(0, idx) unless idx.nil?
|
76
|
-
end
|
77
|
-
@addition[name] = content
|
78
|
-
end
|
79
|
-
end
|
80
|
-
t = load_content(env(:template))
|
81
|
-
d = load_content(env(:in))
|
82
|
-
begin
|
83
|
-
$generator = Generator.new(YAML.safe_load(d), t)
|
84
|
-
rescue StandardError => e
|
85
|
-
aargh('Failed to parse API document.', 3)
|
86
|
-
end
|
87
|
-
|
88
|
-
ARGV.each do |name|
|
89
|
-
aargh('Addition file name is empty.', 1) if name.empty?
|
90
|
-
if name.end_with? '.rb'
|
91
|
-
begin
|
92
|
-
require File.absolute_path(name)
|
93
|
-
rescue SyntaxError => e
|
94
|
-
aargh("Syntax error in addition #{name}: #{e.to_s}", 4)
|
95
|
-
rescue StandardError => e
|
96
|
-
aargh("Failed to require addition #{name}: #{e.to_s}", 5)
|
97
|
-
end
|
98
|
-
else
|
99
|
-
c = load_content(name)
|
100
|
-
begin
|
101
|
-
d = YAML.safe_load(c)
|
102
|
-
rescue StandardError
|
103
|
-
d = c
|
104
|
-
end
|
105
|
-
$generator.add(name, d, name.upcase.end_with?('.YAML') || name.upcase.end_with?('.JSON'))
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
begin
|
110
|
-
out = ERB.new($generator.template).result($generator.get_binding)
|
111
|
-
rescue SyntaxError => e
|
112
|
-
aargh("Template syntax error: #{e.to_s}", 6)
|
113
|
-
rescue StandardError => e
|
114
|
-
aargh("Template error: #{e.to_s}", 7)
|
115
|
-
end
|
116
|
-
|
117
|
-
output = env(:out)
|
118
|
-
if output.empty?
|
119
|
-
output = $stdout
|
120
|
-
else
|
121
|
-
begin
|
122
|
-
output = File.open(output, 'w')
|
123
|
-
rescue StandardError
|
124
|
-
aargh("Failed to open for writing: #{output}", 1)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
output.write(out)
|
128
|
-
output.close
|