openapi_sdk_generator_gem 0.1.0 → 0.1.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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2b7199578a41927a7b94a00e995cab096eb77c148e6837a53e55bc5f2ce02845
|
|
4
|
+
data.tar.gz: f758e337a3dde8a1fe681d62e83d9bfb8c0abeb6460c56a1a2a163615aa9dafe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4e5ee24a140baed8a34b3c668f0da4dcb7676a481644d19502d3fa2755b975a64fc6357983772c17ee0b1bf225adc18c801cefe917fb320f7b888d7462718302
|
|
7
|
+
data.tar.gz: 4093d3f8b34ee6d799f25b1b42119d54122de01e1af15cd2ac4c0978c3869437eacdaf04f1d87ea85ef5a7fce2dd218ea9f827b79c363cc969bf665abe94c064
|
data/bin/openapi-sdk-generator
CHANGED
|
@@ -11,7 +11,7 @@ parser = OptionParser.new do |opts|
|
|
|
11
11
|
opts.separator "Generate SDK from OpenAPI specification"
|
|
12
12
|
opts.separator ""
|
|
13
13
|
|
|
14
|
-
opts.on("-i", "--input
|
|
14
|
+
opts.on("-i", "--input FILE_OR_URL", "OpenAPI specification file (JSON/YAML) or URL") do |v|
|
|
15
15
|
options[:input] = v
|
|
16
16
|
end
|
|
17
17
|
|
|
@@ -25,6 +25,13 @@ parser = OptionParser.new do |opts|
|
|
|
25
25
|
|
|
26
26
|
opts.on("-h", "--help", "Show this help message") do
|
|
27
27
|
puts opts
|
|
28
|
+
puts ""
|
|
29
|
+
puts "Examples:"
|
|
30
|
+
puts " # From local file"
|
|
31
|
+
puts " openapi-sdk-generator -i petstore.yaml -o ./output -l ruby"
|
|
32
|
+
puts ""
|
|
33
|
+
puts " # From URL"
|
|
34
|
+
puts " openapi-sdk-generator -i https://example.com/api-spec.yaml -o ./output -l javascript"
|
|
28
35
|
exit
|
|
29
36
|
end
|
|
30
37
|
|
|
@@ -10,15 +10,13 @@ module OpenapiSdkGenerator
|
|
|
10
10
|
def write_to_directory(output_dir)
|
|
11
11
|
FileUtils.mkdir_p(output_dir)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
client_content = generate_client
|
|
15
15
|
File.write(File.join(output_dir, 'client.js'), client_content)
|
|
16
16
|
|
|
17
|
-
# Write package.json
|
|
18
17
|
package_json = generate_package_json
|
|
19
18
|
File.write(File.join(output_dir, 'package.json'), package_json)
|
|
20
19
|
|
|
21
|
-
# Write README
|
|
22
20
|
readme_content = generate_readme
|
|
23
21
|
File.write(File.join(output_dir, 'README.md'), readme_content)
|
|
24
22
|
end
|
|
@@ -12,18 +12,15 @@ module OpenapiSdkGenerator
|
|
|
12
12
|
FileUtils.mkdir_p(output_dir)
|
|
13
13
|
FileUtils.mkdir_p(File.join(output_dir, 'models'))
|
|
14
14
|
|
|
15
|
-
# Write client file
|
|
16
15
|
client_content = generate_client
|
|
17
16
|
File.write(File.join(output_dir, 'client.rb'), client_content)
|
|
18
17
|
|
|
19
|
-
# Write model files
|
|
20
18
|
parser.models.each do |name, model|
|
|
21
19
|
model_content = generate_model(model)
|
|
22
20
|
filename = "#{sanitize_name(name)}.rb"
|
|
23
21
|
File.write(File.join(output_dir, 'models', filename), model_content)
|
|
24
22
|
end
|
|
25
23
|
|
|
26
|
-
# Write README
|
|
27
24
|
readme_content = generate_readme
|
|
28
25
|
File.write(File.join(output_dir, 'README.md'), readme_content)
|
|
29
26
|
end
|
|
@@ -93,9 +90,6 @@ module OpenapiSdkGenerator
|
|
|
93
90
|
end
|
|
94
91
|
end
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
93
|
# Converts parameters into Ruby method signature
|
|
100
94
|
def format_method_params(params)
|
|
101
95
|
return "" unless params.is_a?(Array) && !params.empty?
|
|
@@ -109,7 +103,7 @@ module OpenapiSdkGenerator
|
|
|
109
103
|
end.compact.join(", ")
|
|
110
104
|
end
|
|
111
105
|
|
|
112
|
-
#
|
|
106
|
+
# /pets/{petId} → /pets/#{pet_id}
|
|
113
107
|
def format_url_path(path, params = [])
|
|
114
108
|
return path unless params.is_a?(Array) && !params.empty?
|
|
115
109
|
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
require 'net/http'
|
|
3
|
+
require 'openssl'
|
|
4
|
+
|
|
1
5
|
module OpenapiSdkGenerator
|
|
2
6
|
class Parser
|
|
3
7
|
attr_reader :spec, :api_info, :base_url, :endpoints, :models
|
|
@@ -26,18 +30,75 @@ module OpenapiSdkGenerator
|
|
|
26
30
|
private
|
|
27
31
|
|
|
28
32
|
def load_spec
|
|
29
|
-
content =
|
|
30
|
-
|
|
33
|
+
content = fetch_content
|
|
34
|
+
parse_content(content)
|
|
35
|
+
rescue => e
|
|
36
|
+
raise Error, "Failed to load OpenAPI spec: #{e.message}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def fetch_content
|
|
40
|
+
if url?(@file_path)
|
|
41
|
+
fetch_from_url(@file_path)
|
|
42
|
+
else
|
|
43
|
+
fetch_from_file(@file_path)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def url?(path)
|
|
48
|
+
path =~ /\A#{URI::DEFAULT_PARSER.make_regexp(%w[http https])}\z/
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def fetch_from_url(url)
|
|
52
|
+
|
|
53
|
+
uri = URI.parse(url)
|
|
54
|
+
|
|
55
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
56
|
+
http.use_ssl = (uri.scheme == 'https')
|
|
57
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
58
|
+
|
|
59
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
|
60
|
+
response = http.request(request)
|
|
61
|
+
|
|
62
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
63
|
+
raise Error, "Failed to fetch URL: #{response.code} #{response.message}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
response.body
|
|
67
|
+
rescue SocketError, Net::OpenTimeout, Net::ReadTimeout => e
|
|
68
|
+
raise Error, "Network error while fetching URL: #{e.message}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def fetch_from_file(file_path)
|
|
72
|
+
unless File.exist?(file_path)
|
|
73
|
+
raise Error, "File not found: #{file_path}"
|
|
74
|
+
end
|
|
75
|
+
File.read(file_path)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def parse_content(content)
|
|
79
|
+
if looks_like_json?(content)
|
|
31
80
|
JSON.parse(content)
|
|
32
|
-
elsif
|
|
81
|
+
elsif looks_like_yaml?(content)
|
|
33
82
|
YAML.load(content)
|
|
34
83
|
else
|
|
35
|
-
|
|
84
|
+
begin
|
|
85
|
+
JSON.parse(content)
|
|
86
|
+
rescue JSON::ParserError
|
|
87
|
+
YAML.load(content)
|
|
88
|
+
end
|
|
36
89
|
end
|
|
37
|
-
rescue => e
|
|
90
|
+
rescue JSON::ParserError, Psych::SyntaxError => e
|
|
38
91
|
raise Error, "Failed to parse OpenAPI spec: #{e.message}"
|
|
39
92
|
end
|
|
40
93
|
|
|
94
|
+
def looks_like_json?(content)
|
|
95
|
+
content.strip.start_with?('{', '[')
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def looks_like_yaml?(content)
|
|
99
|
+
content =~ /^\w+:/
|
|
100
|
+
end
|
|
101
|
+
|
|
41
102
|
def parse_spec
|
|
42
103
|
parse_info
|
|
43
104
|
parse_servers
|
|
@@ -120,16 +181,45 @@ module OpenapiSdkGenerator
|
|
|
120
181
|
def parse_schemas
|
|
121
182
|
components = @spec['components'] || {}
|
|
122
183
|
schemas = components['schemas'] || {}
|
|
123
|
-
|
|
184
|
+
|
|
124
185
|
schemas.each do |name, schema|
|
|
186
|
+
resolved_schema = resolve_schema(schema)
|
|
187
|
+
|
|
125
188
|
@models[name] = {
|
|
126
189
|
name: name,
|
|
127
|
-
type:
|
|
128
|
-
properties: parse_properties(
|
|
129
|
-
required:
|
|
190
|
+
type: resolved_schema['type'],
|
|
191
|
+
properties: parse_properties(resolved_schema['properties']),
|
|
192
|
+
required: resolved_schema['required'] || []
|
|
130
193
|
}
|
|
131
194
|
end
|
|
132
195
|
end
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def resolve_schema(schema)
|
|
199
|
+
if schema['$ref']
|
|
200
|
+
return resolve_schema(ref_to_schema(schema['$ref']))
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
if schema['allOf']
|
|
204
|
+
merged = { 'properties' => {}, 'required' => [] }
|
|
205
|
+
|
|
206
|
+
schema['allOf'].each do |subschema|
|
|
207
|
+
resolved = resolve_schema(subschema)
|
|
208
|
+
merged['properties'].merge!(resolved['properties'] || {})
|
|
209
|
+
merged['required'] |= (resolved['required'] || [])
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
return merged
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
schema
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def ref_to_schema(ref)
|
|
220
|
+
ref_path = ref.sub('#/components/schemas/', '')
|
|
221
|
+
@spec['components']['schemas'][ref_path]
|
|
222
|
+
end
|
|
133
223
|
|
|
134
224
|
def parse_properties(properties)
|
|
135
225
|
return {} unless properties
|
|
@@ -29,7 +29,7 @@ module OpenapiSdkGenerator
|
|
|
29
29
|
generator.write_to_directory(@options[:output])
|
|
30
30
|
|
|
31
31
|
puts " SDK generated successfully!"
|
|
32
|
-
puts "
|
|
32
|
+
puts "Output directory: #{@options[:output]}"
|
|
33
33
|
rescue => e
|
|
34
34
|
puts " Error: #{e.message}"
|
|
35
35
|
exit 1
|
|
@@ -37,13 +37,20 @@ module OpenapiSdkGenerator
|
|
|
37
37
|
|
|
38
38
|
private
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
def validate_options!
|
|
41
|
+
raise Error, "Input is required" unless @options[:input]
|
|
42
|
+
raise Error, "Output directory is required" unless @options[:output]
|
|
43
|
+
raise Error, "Language is required (ruby or javascript)" unless @options[:language]
|
|
44
|
+
|
|
45
|
+
unless url?(@options[:input]) || File.exist?(@options[:input])
|
|
46
|
+
raise Error, "Input must be a valid file path or URL"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def url?(value)
|
|
52
|
+
value.start_with?("http://", "https://")
|
|
45
53
|
end
|
|
46
|
-
|
|
47
54
|
def create_generator(language, parser)
|
|
48
55
|
case language.downcase
|
|
49
56
|
when 'ruby'
|