open_api_import 0.11.6 → 0.12.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.
@@ -1,4 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ using OpenApiImportStringExt
4
+
1
5
  class OpenApiImport
6
+ class ParseError < StandardError; end
7
+
8
+ VERSION = "0.12.0"
9
+
10
+ extend LibOpenApiImport
11
+
2
12
  ##############################################################################################
3
13
  # Import a Swagger or Open API file and create a Ruby Request Hash file including all requests and responses.
4
14
  # The http methods that will be treated are: 'get','post','put','delete', 'patch'.
@@ -18,8 +28,10 @@ class OpenApiImport
18
28
  # tags: It will be used the tags key to create the module name, for example the tags: [users,list] will create the module UsersList and all the requests from all modules in the same file.
19
29
  # tags_file: It will be used the tags key to create the module name, for example the tags: [users,list] will create the module UsersList and and each module will be in a new requests file.
20
30
  # fixed: all the requests will be under the module Requests
31
+ # @param return_data [Boolean]. (default: false) Instead of writing files, return a Hash of {filename => content}.
21
32
  ##############################################################################################
22
- def self.from(swagger_file, create_method_name: :operation_id, include_responses: true, mock_response: false, name_for_module: :path, silent: false, create_constants: false)
33
+ def self.from(swagger_file, create_method_name: :operation_id, include_responses: true, mock_response: false,
34
+ name_for_module: :path, silent: false, create_constants: false, return_data: false)
23
35
  begin
24
36
  f = File.new("#{swagger_file}_open_api_import.log", "w")
25
37
  f.sync = true
@@ -35,59 +47,53 @@ class OpenApiImport
35
47
  @logger.info "swagger_file: #{swagger_file}, include_responses: #{include_responses}, mock_response: #{mock_response}\n"
36
48
  @logger.info "create_method_name: #{create_method_name}, name_for_module: #{name_for_module}\n"
37
49
 
38
- file_to_convert = if swagger_file["./"].nil?
39
- swagger_file
40
- else
41
- Dir.pwd.to_s + "/" + swagger_file.gsub("./", "")
42
- end
50
+ file_to_convert = File.expand_path(swagger_file)
43
51
  unless File.exist?(file_to_convert)
44
52
  raise "The file #{file_to_convert} doesn't exist"
45
53
  end
46
54
 
47
- file_errors = file_to_convert + ".errors.log"
48
- File.delete(file_errors) if File.exist?(file_errors)
55
+ file_errors = "#{file_to_convert}.errors.log"
56
+ FileUtils.rm_f(file_errors)
49
57
  import_errors = ""
50
58
  required_constants = []
51
59
 
52
60
  begin
53
61
  definition = OasParser::Definition.resolve(swagger_file)
54
- rescue Exception => stack
62
+ rescue StandardError => e
55
63
  message = "There was a problem parsing the Open Api document using the oas_parser_reborn gem. The execution was aborted.\n"
56
64
  message += "Visit the github for oas_parser_reborn gem for bugs and more info: https://github.com/MarioRuiz/oas_parser_reborn\n"
57
- message += "Error: #{stack.message}"
58
- puts message
65
+ message += "Error: #{e.message}"
59
66
  @logger.fatal message
60
- @logger.fatal stack.backtrace.join("\n")
61
- exit!
67
+ @logger.fatal e.backtrace.join("\n")
68
+ raise ParseError, message
62
69
  end
63
70
 
64
71
  raw = definition.raw.deep_symbolize_keys
65
72
 
66
- if raw.key?(:openapi) && (raw[:openapi].to_f > 0)
73
+ if raw.key?(:openapi) && raw[:openapi].to_f.positive?
67
74
  raw[:swagger] = raw[:openapi]
68
75
  end
69
76
  if raw[:swagger].to_f < 2.0
70
77
  raise "Unsupported Swagger version. Only versions >= 2.0 are valid."
71
78
  end
72
79
 
73
- base_host = ""
74
80
  base_path = ""
75
81
 
76
- base_host = raw[:host] if raw.key?(:host)
82
+ raw[:host] if raw.key?(:host)
77
83
  base_path = raw[:basePath] if raw.key?(:basePath)
78
84
  module_name = raw[:info][:title].camel_case
79
85
  module_version = "V#{raw[:info][:version].to_s.snake_case}"
80
86
 
81
87
  output = []
82
88
  output_header = []
83
- output_header << "#" * 50
89
+ output_header << ("#" * 50)
84
90
  output_header << "# #{raw[:info][:title]}"
85
91
  output_header << "# version: #{raw[:info][:version]}"
86
92
  output_header << "# description: "
87
93
  raw[:info][:description].to_s.split("\n").each do |d|
88
94
  output_header << "# #{d}" unless d == ""
89
95
  end
90
- output_header << "#" * 50
96
+ output_header << ("#" * 50)
91
97
 
92
98
  output_header << "module Swagger"
93
99
  output_header << "module #{module_name}"
@@ -102,10 +108,9 @@ class OpenApiImport
102
108
  raw = path.raw.deep_symbolize_keys
103
109
 
104
110
  if raw.key?(:parameters)
105
- raw.each do |met, cont|
111
+ raw.each_key do |met|
106
112
  if met != :parameters
107
113
  if raw[met].key?(:parameters)
108
- #in case parameters for all methods in path is present
109
114
  raw[met][:parameters] = raw[met][:parameters] + raw[:parameters]
110
115
  else
111
116
  raw[met][:parameters] = raw[:parameters]
@@ -124,85 +129,76 @@ class OpenApiImport
124
129
  params_data = []
125
130
  description_parameters = []
126
131
  data_form = []
132
+ data_form_hash = {}
127
133
  data_required = []
128
- #todo: add nested one.true.three to data_read_only
129
134
  data_read_only = []
130
135
  data_default = []
131
136
  data_examples = []
137
+ data_examples_hashes = []
132
138
  data_pattern = []
133
139
  responses = []
134
140
 
135
- # for the case operationId is missing
136
141
  cont[:operationId] = "undefined" unless cont.key?(:operationId)
137
142
 
138
143
  if create_method_name == :path
139
- method_name = (met.to_s + "_" + path.path.to_s).snake_case
144
+ method_name = "#{met}_#{path.path}".snake_case
140
145
  method_name.chop! if method_name[-1] == "_"
141
146
  elsif create_method_name == :operation_id
142
- if (name_for_module == :tags or name_for_module == :tags_file) and cont.key?(:tags) and cont[:tags].is_a?(Array) and cont[:tags].size > 0
147
+ if ((name_for_module == :tags) || (name_for_module == :tags_file)) && cont.key?(:tags) && cont[:tags].is_a?(Array) && cont[:tags].size.positive?
143
148
  metnametmp = cont[:operationId].gsub(/^#{cont[:tags].join}[\s_]*/, "")
144
- cont[:tags].join.split(" ").each do |tag|
145
- metnametmp.gsub!(/^#{tag}[\s_]*/i, "")
149
+ cont[:tags].join.split.each do |tag|
150
+ metnametmp = metnametmp.gsub(/^#{tag}[\s_]*/i, "")
146
151
  end
147
152
  metnametmp = met if metnametmp == ""
148
153
  else
149
154
  metnametmp = cont[:operationId]
150
155
  end
151
156
  method_name = metnametmp.to_s.snake_case
152
- else
153
- if (name_for_module == :tags or name_for_module == :tags_file) and cont.key?(:tags) and cont[:tags].is_a?(Array) and cont[:tags].size > 0
154
- method_name = cont[:operationId].gsub(/^#{cont[:tags].join}[\s_]*/, "")
155
- cont[:tags].join.split(" ").each do |tag|
156
- method_name.gsub!(/^#{tag}[\s_]*/i, "")
157
- end
158
- method_name = met if method_name == ""
159
- else
160
- method_name = cont[:operationId]
157
+ elsif ((name_for_module == :tags) || (name_for_module == :tags_file)) && cont.key?(:tags) && cont[:tags].is_a?(Array) && cont[:tags].size.positive?
158
+ method_name = cont[:operationId].gsub(/^#{cont[:tags].join}[\s_]*/, "")
159
+ cont[:tags].join.split.each do |tag|
160
+ method_name = method_name.gsub(/^#{tag}[\s_]*/i, "")
161
161
  end
162
+ method_name = met if method_name == ""
163
+ else
164
+ method_name = cont[:operationId]
162
165
  end
163
166
  path_txt = path.path.dup.to_s
164
167
  if [:path, :path_file, :tags, :tags_file].include?(name_for_module)
165
168
  old_module_requests = module_requests
166
169
  if [:path, :path_file].include?(name_for_module)
167
- # to remove version from path fex: /v1/Customer
168
- path_requests = path_txt.gsub(/^\/v[\d\.]*\//i, "")
169
- # to remove version from path fex: /1.0/Customer
170
- path_requests = path_requests.gsub(/^\/[\d\.]*\//i, "")
170
+ path_requests = path_txt.gsub(%r{^/v[\d.]*/}i, "")
171
+ path_requests = path_requests.gsub(%r{^/[\d.]*/}i, "")
171
172
  if (path_requests == path_txt) && (path_txt.scan("/").size == 1)
172
- # no folder in path
173
173
  module_requests = "Root"
174
174
  else
175
175
  res_path = path_requests.scan(/(\w+)/)
176
176
  module_requests = res_path[0][0].camel_case
177
177
  end
178
+ elsif cont.key?(:tags) && cont[:tags].is_a?(Array) && cont[:tags].size.positive?
179
+ module_requests = cont[:tags].join(" ").camel_case
178
180
  else
179
- if cont.key?(:tags) and cont[:tags].is_a?(Array) and cont[:tags].size > 0
180
- module_requests = cont[:tags].join(" ").camel_case
181
- else
182
- module_requests = "Undefined"
183
- end
181
+ module_requests = "Undefined"
184
182
  end
185
183
 
186
- # to remove from method_name: v1_list_regions and add it to module
187
184
  if /^(?<vers>v\d+)/i =~ method_name
188
- method_name.gsub!(/^#{vers}_?/, "")
185
+ method_name = method_name.gsub(/^#{vers}_?/, "")
189
186
  module_requests = (vers.capitalize + module_requests).camel_case unless module_requests.start_with?(vers)
190
187
  end
191
188
 
192
189
  if old_module_requests != module_requests
193
- output << "end" unless old_module_requests == "" or name_for_module == :path_file or name_for_module == :tags_file
194
- if name_for_module == :path or name_for_module == :tags
195
- # to add the end for the previous module unless is the first one
190
+ output << "end" unless (old_module_requests == "") || (name_for_module == :path_file) || (name_for_module == :tags_file)
191
+ if (name_for_module == :path) || (name_for_module == :tags)
196
192
  output << "module #{module_requests}"
197
- else #:path_file, :tags_file
193
+ else # :path_file, :tags_file
198
194
  if old_module_requests != ""
199
195
  unless files.key?(old_module_requests)
200
- files[old_module_requests] = Array.new
196
+ files[old_module_requests] = []
201
197
  end
202
198
  files[old_module_requests].concat(output)
203
- output = Array.new
199
+ output = []
204
200
  end
205
- output << "module #{module_requests}" unless files.key?(module_requests) # don't add in case already existed
201
+ output << "module #{module_requests}" unless files.key?(module_requests)
206
202
  end
207
203
  end
208
204
  end
@@ -210,60 +206,57 @@ class OpenApiImport
210
206
  output << ""
211
207
  output << "# operationId: #{cont[:operationId]}, method: #{met}"
212
208
  output << "# summary: #{cont[:summary].split("\n").join("\n# ")}" if cont.key?(:summary)
213
- if !cont[:description].to_s.split("\n").empty?
209
+ if cont[:description].to_s.split("\n").empty?
210
+ output << "# description: #{cont[:description]}"
211
+ else
214
212
  output << "# description: "
215
213
  cont[:description].to_s.split("\n").each do |d|
216
214
  output << "# #{d}" unless d == ""
217
215
  end
218
- else
219
- output << "# description: #{cont[:description]}"
220
216
  end
221
217
 
222
218
  mock_example = []
223
219
 
224
220
  if include_responses && cont.key?(:responses) && cont[:responses].is_a?(Hash)
225
221
  cont[:responses].each do |k, v|
226
- response_example = []
227
222
  response_example = get_response_examples(v)
228
223
 
229
224
  data_pattern += get_patterns("", v[:schema]) if v.key?(:schema)
230
225
  data_pattern.uniq!
231
- v[:description] = v[:description].to_s.gsub("'", %q(\\\'))
232
- if !response_example.empty?
226
+ resp_description = v[:description].to_s.gsub("'", %q(\\\'))
227
+ if response_example.empty?
228
+ responses << "'#{k}': { message: '#{resp_description}'}, "
229
+ else
233
230
  responses << "'#{k}': { "
234
- responses << "message: '#{v[:description]}', "
231
+ responses << "message: '#{resp_description}', "
235
232
  responses << "data: "
236
233
  responses << response_example
237
234
  responses << "},"
238
- if mock_response and mock_example.size == 0
235
+ if mock_response && mock_example.empty?
239
236
  mock_example << "code: '#{k}',"
240
- mock_example << "message: '#{v[:description]}',"
237
+ mock_example << "message: '#{resp_description}',"
241
238
  mock_example << "data: "
242
239
  mock_example << response_example
243
240
  end
244
- else
245
- responses << "'#{k}': { message: '#{v[:description]}'}, "
246
241
  end
247
242
  end
248
243
  end
249
- # todo: for open api 3.0 add the new Link feature: https://swagger.io/docs/specification/links/
250
- # todo: for open api 3.0 is not getting the required params in all cases
251
244
 
252
- # for the case open api 3 with cont.requestBody.content.'applicatin/json'.schema
253
- # example: petstore-expanded.yaml operationId=addPet
254
- if cont.key?(:requestBody) and cont[:requestBody].key?(:content) and
255
- cont[:requestBody][:content].key?(:'application/json') and cont[:requestBody][:content][:'application/json'].key?(:schema)
245
+ if cont.key?(:requestBody) && cont[:requestBody].key?(:content) &&
246
+ cont[:requestBody][:content].key?(:"application/json") && cont[:requestBody][:content][:"application/json"].key?(:schema)
256
247
  cont[:parameters] = [] unless cont.key?(:parameters)
257
- cont[:parameters] << { in: "body", schema: cont[:requestBody][:content][:'application/json'][:schema] }
248
+ cont[:parameters] << { in: "body", schema: cont[:requestBody][:content][:"application/json"][:schema] }
258
249
  end
259
250
 
260
251
  data_examples_all_of = false
261
252
  if cont.key?(:parameters) && cont[:parameters].is_a?(Array)
262
253
  cont[:parameters].each do |p|
263
- if p.keys.include?(:schema) and p[:schema].include?(:type)
254
+ if p.keys.include?(:schema) && p[:schema].include?(:type)
264
255
  type = p[:schema][:type]
256
+ type = Array(type).reject { |t| t == "null" }.first if type.is_a?(Array)
265
257
  elsif p.keys.include?(:type)
266
258
  type = p[:type]
259
+ type = Array(type).reject { |t| t == "null" }.first if type.is_a?(Array)
267
260
  else
268
261
  type = ""
269
262
  end
@@ -271,10 +264,10 @@ class OpenApiImport
271
264
  if p[:in] == "path"
272
265
  if create_method_name == :operationId
273
266
  param_name = p[:name]
274
- path_txt.gsub!("{#{param_name}}", "\#{#{param_name}}")
267
+ path_txt = path_txt.gsub("{#{param_name}}", "\#{#{param_name}}")
275
268
  else
276
269
  param_name = p[:name].to_s.snake_case
277
- path_txt.gsub!("{#{p[:name]}}", "\#{#{param_name}}")
270
+ path_txt = path_txt.gsub("{#{p[:name]}}", "\#{#{param_name}}")
278
271
  end
279
272
  unless params_path.include?(param_name)
280
273
  if create_constants
@@ -283,7 +276,6 @@ class OpenApiImport
283
276
  else
284
277
  params_path << param_name
285
278
  end
286
- #params_required << param_name if p[:required].to_s=="true"
287
279
  @logger.warn "Description key is missing for #{met} #{path.path} #{p[:name]}" if p[:description].nil?
288
280
  description_parameters << "# #{p[:name]}: (#{type}) #{"(required)" if p[:required].to_s == "true"} #{p[:description].to_s.split("\n").join("\n#\t\t\t")}"
289
281
  end
@@ -292,22 +284,22 @@ class OpenApiImport
292
284
  params_required << p[:name] if p[:required].to_s == "true"
293
285
  @logger.warn "Description key is missing for #{met} #{path.path} #{p[:name]}" if p[:description].nil?
294
286
  description_parameters << "# #{p[:name]}: (#{type}) #{"(required)" if p[:required].to_s == "true"} #{p[:description].to_s.split("\n").join("\n#\t\t\t")}"
295
- elsif p[:in] == "formData" or p[:in] == "formdata"
296
- #todo: take in consideration: default, required
297
- #todo: see if we should add the required as params to the method and not required as options
298
- #todo: set on data the required fields with the values from args
299
-
287
+ elsif (p[:in] == "formData") || (p[:in] == "formdata")
300
288
  description_parameters << "# #{p[:name]}: (#{p[:type]}) #{p[:description].split("\n").join("\n#\t\t\t")}"
301
289
 
302
290
  case p[:type]
303
291
  when /^string$/i
304
292
  data_form << "#{p[:name]}: ''"
293
+ data_form_hash[p[:name].to_sym] = ""
305
294
  when /^boolean$/i
306
295
  data_form << "#{p[:name]}: true"
296
+ data_form_hash[p[:name].to_sym] = true
307
297
  when /^number$/i
308
298
  data_form << "#{p[:name]}: 0"
299
+ data_form_hash[p[:name].to_sym] = 0
309
300
  when /^integer$/i
310
301
  data_form << "#{p[:name]}: 0"
302
+ data_form_hash[p[:name].to_sym] = 0
311
303
  else
312
304
  puts "! on formData not supported type #{p[:type]}"
313
305
  end
@@ -319,18 +311,19 @@ class OpenApiImport
319
311
  bodies = p[:schema][:anyOf]
320
312
  elsif p[:schema].key?(:allOf)
321
313
  data_examples_all_of, bodies = get_data_all_of_bodies(p)
322
- bodies.unshift(p[:schema]) if p[:schema].key?(:required) or p.key?(:required) #todo: check if this 'if' is necessary
323
- data_examples_all_of = true # because we are on data and allOf already
314
+ bodies.unshift(p[:schema]) if p[:schema].key?(:required) || p.key?(:required)
315
+ data_examples_all_of = true
324
316
  else
325
317
  bodies = [p[:schema]]
326
318
  end
327
319
 
328
320
  params_data = []
321
+ params_data_hash = {}
329
322
 
330
323
  bodies.each do |body|
331
324
  data_required += get_required_data(body)
332
325
  all_properties = []
333
- all_properties << body[:properties] if body.keys.include?(:properties) and body[:properties].size > 0
326
+ all_properties << body[:properties] if body.keys.include?(:properties) && body[:properties].size.positive?
334
327
  if body.key?(:allOf)
335
328
  body[:allOf].each do |item|
336
329
  all_properties << item[:properties] if item.key?(:properties)
@@ -338,30 +331,28 @@ class OpenApiImport
338
331
  end
339
332
 
340
333
  all_properties.each do |props|
341
- props.each { |dpk, dpv|
334
+ props.each do |dpk, dpv|
342
335
  if dpv.keys.include?(:example)
343
- if dpv[:example].is_a?(Array) and dpv.type != "array"
336
+ if dpv[:example].is_a?(Array) && (dpv.type != "array")
344
337
  valv = dpv[:example][0]
345
338
  else
346
339
  valv = dpv[:example].to_s
347
340
  end
348
- else
349
- if dpv.type == "object"
350
- if dpv.key?(:properties)
351
- valv = get_examples(dpv[:properties], :key_value, true).join("\n")
352
- else
353
- valv = "{}"
354
- end
355
- elsif dpv.type == "array"
356
- if dpv.key?(:items)
357
- valv = get_examples({ dpk => dpv }, :only_value)
358
- valv = valv.join("\n")
359
- else
360
- valv = "[]"
361
- end
341
+ elsif dpv.type == "object"
342
+ if dpv.key?(:properties)
343
+ valv = get_examples(dpv[:properties], :key_value, true).join("\n")
344
+ else
345
+ valv = "{}"
346
+ end
347
+ elsif dpv.type == "array"
348
+ if dpv.key?(:items)
349
+ valv = get_examples({ dpk => dpv }, :only_value)
350
+ valv = valv.join("\n")
362
351
  else
363
- valv = ""
352
+ valv = "[]"
364
353
  end
354
+ else
355
+ valv = ""
365
356
  end
366
357
 
367
358
  if dpv.keys.include?(:description)
@@ -372,7 +363,7 @@ class OpenApiImport
372
363
  data_pattern.uniq!
373
364
  dpkeys = []
374
365
  data_pattern.reject! do |dp|
375
- dpkey = dp.scan(/^'[\w\.]+'/)
366
+ dpkey = dp.scan(/^'[\w.]+'/)
376
367
 
377
368
  if dpkeys.include?(dpkey)
378
369
  true
@@ -382,7 +373,7 @@ class OpenApiImport
382
373
  end
383
374
  end
384
375
 
385
- if dpv.keys.include?(:readOnly) and dpv[:readOnly] == true
376
+ if dpv.keys.include?(:readOnly) && (dpv[:readOnly] == true)
386
377
  data_read_only << dpk
387
378
  end
388
379
  if dpv.keys.include?(:default)
@@ -395,28 +386,34 @@ class OpenApiImport
395
386
  end
396
387
  end
397
388
 
398
- #todo: consider check default and insert it
399
- #todo: remove array from here and add the option to get_examples for the case thisisthekey: ['xxxx']
400
- if dpv.key?(:type) and dpv[:type] != "array"
389
+ params_data_hash[dpk] = build_example_value(dpv)
390
+
391
+ if dpv.key?(:type) && (dpv[:type] != "array")
401
392
  params_data << get_examples({ dpk => dpv }, :only_value, true).join
402
- params_data[-1].chop!.chop! if params_data[-1].to_s[-2..-1] == ", "
393
+ params_data[-1].chop!.chop! if params_data[-1].to_s[-2..] == ", "
403
394
  params_data.pop if params_data[-1].match?(/^\s*$/im)
404
395
  else
405
396
  if valv.to_s == ""
406
397
  valv = '""'
407
398
  elsif valv.include?('"')
408
- valv.gsub!('"', "'") unless valv.include?("'")
399
+ valv = valv.gsub('"', "'") unless valv.include?("'")
409
400
  end
410
401
  params_data << "#{dpk}: #{valv}"
411
402
  end
412
- }
413
- if params_data.size > 0
414
- if data_examples_all_of == true and data_examples.size > 0
403
+ end
404
+ if params_data.size.positive?
405
+ if (data_examples_all_of == true) && data_examples.size.positive?
415
406
  data_examples[0] += params_data
416
407
  else
417
408
  data_examples << params_data
418
409
  end
410
+ if (data_examples_all_of == true) && data_examples_hashes.size.positive?
411
+ data_examples_hashes[0].merge!(params_data_hash)
412
+ else
413
+ data_examples_hashes << params_data_hash.dup
414
+ end
419
415
  params_data = []
416
+ params_data_hash = {}
420
417
  end
421
418
  end
422
419
  end
@@ -427,7 +424,7 @@ class OpenApiImport
427
424
  end
428
425
  end
429
426
  elsif p[:in] == "header"
430
- #todo: see how we can treat those cases
427
+ # TODO: see how we can treat those cases
431
428
  else
432
429
  puts "! not imported data with :in:#{p[:in]} => #{p.inspect}"
433
430
  end
@@ -450,15 +447,13 @@ class OpenApiImport
450
447
  required_constants << pr.to_s.snake_case.upcase
451
448
  end
452
449
  end
453
- else
454
- if params_query.include?(pr)
455
- if create_method_name == :operationId
456
- path_txt += "#{pr}=\#{#{pr}}&"
457
- params << "#{pr}"
458
- else
459
- path_txt += "#{pr}=\#{#{pr.to_s.snake_case}}&"
460
- params << "#{pr.to_s.snake_case}"
461
- end
450
+ elsif params_query.include?(pr)
451
+ if create_method_name == :operationId
452
+ path_txt += "#{pr}=\#{#{pr}}&"
453
+ params << pr.to_s
454
+ else
455
+ path_txt += "#{pr}=\#{#{pr.to_s.snake_case}}&"
456
+ params << pr.to_s.snake_case.to_s
462
457
  end
463
458
  end
464
459
  end
@@ -476,23 +471,17 @@ class OpenApiImport
476
471
  end
477
472
  end
478
473
 
479
- if description_parameters.size > 0
474
+ if description_parameters.size.positive?
480
475
  output << "# parameters description: "
481
476
  output << description_parameters.uniq
482
477
  end
483
478
 
484
- #for the case we still have some parameters on path that were not in 'parameters'
485
- if path_txt.scan(/[^#]{\w+}/).size > 0
479
+ if path_txt.scan(/[^#]{\w+}/).size.positive?
486
480
  paramst = []
487
481
  prms = path_txt.scan(/[^#]{(\w+)}/)
488
482
  prms.each do |p|
489
- #if create_constants
490
- # paramst<<"#{p[0].to_s.snake_case}: #{p[0].to_s.snake_case.upcase}"
491
- # required_constants << p[0].to_s.snake_case.upcase
492
- #else
493
483
  paramst << p[0].to_s.snake_case
494
- #end
495
- path_txt.gsub!("{#{p[0]}}", "\#{#{p[0].to_s.snake_case}}")
484
+ path_txt = path_txt.gsub("{#{p[0]}}", "\#{#{p[0].to_s.snake_case}}")
496
485
  end
497
486
  paramst.concat params
498
487
  params = paramst
@@ -533,6 +522,7 @@ class OpenApiImport
533
522
 
534
523
  unless data_form.empty?
535
524
  data_examples << data_form
525
+ data_examples_hashes << data_form_hash unless data_form_hash.empty?
536
526
  end
537
527
 
538
528
  unless data_examples.empty?
@@ -540,18 +530,15 @@ class OpenApiImport
540
530
  reqdata = []
541
531
  begin
542
532
  data_examples[0].uniq!
543
- data_ex = eval("{#{data_examples[0].join(", ")}}")
544
- rescue SyntaxError
545
- data_ex = {}
546
- @logger.warn "Syntax error: #{met} for path: #{path.path} evaluating data_examples[0] => #{data_examples[0].inspect}"
547
- rescue
533
+ data_ex = data_examples_hashes[0] || {}
534
+ rescue StandardError => e
548
535
  data_ex = {}
549
- @logger.warn "Syntax error: #{met} for path: #{path.path} evaluating data_examples[0] => #{data_examples[0].inspect}"
536
+ @logger.warn "Error processing data examples: #{met} for path: #{path.path} => #{e.message}"
550
537
  end
551
- if (data_required.grep(/\./)).empty?
552
- reqdata = filter(data_ex, data_required) #not nested
538
+ if data_required.grep(/\./).empty?
539
+ reqdata = filter(data_ex, data_required)
553
540
  else
554
- reqdata = filter(data_ex, data_required, true) #nested
541
+ reqdata = filter(data_ex, data_required, true)
555
542
  end
556
543
  unless reqdata.empty?
557
544
  reqdata.uniq!
@@ -560,16 +547,15 @@ class OpenApiImport
560
547
  output += phsd
561
548
  end
562
549
  end
563
- unless data_read_only.empty? or !data_required.empty?
550
+ unless data_read_only.empty? || !data_required.empty?
564
551
  reqdata = []
565
- #remove read only fields from :data
566
552
  data_examples[0].each do |edata|
567
553
  read_only = false
568
554
  data_read_only.each do |rdata|
569
- if edata.scan(/^#{rdata}:/).size > 0
555
+ if edata.scan(/^#{rdata}:/).size.positive?
570
556
  read_only = true
571
557
  break
572
- elsif edata.scan(/:/).size == 0
558
+ elsif edata.scan(":").empty?
573
559
  break
574
560
  end
575
561
  end
@@ -614,24 +600,29 @@ class OpenApiImport
614
600
  end
615
601
  output_footer = []
616
602
 
617
- output_footer << "end" unless (module_requests == "") && ([:path, :path_file, :tags, :tags_file].include?(name_for_module))
603
+ output_footer << "end" unless (module_requests == "") && [:path, :path_file, :tags, :tags_file].include?(name_for_module)
618
604
  output_footer << "end" << "end" << "end"
619
- if files.size == 0 and !create_constants
605
+
606
+ generated_files = {}
607
+
608
+ if files.empty? && !create_constants
620
609
  output = output_header + output + output_footer
621
610
  output_txt = output.join("\n")
622
- requests_file_path = file_to_convert + ".rb"
623
- File.open(requests_file_path, "w") { |file| file.write(output_txt) }
624
- res_rufo = `rufo #{requests_file_path}`
625
- message = "** Requests file: #{swagger_file}.rb that contains the code of the requests after importing the Swagger file"
626
- puts message unless silent
627
- @logger.info message
628
- @logger.error " Error formating with rufo" unless res_rufo.to_s.match?(/\AFormat:.+$\s*\z/)
629
- @logger.error " Syntax Error: #{`ruby -c #{requests_file_path}`}" unless `ruby -c #{requests_file_path}`.include?("Syntax OK")
611
+ requests_file_path = "#{file_to_convert}.rb"
612
+ if return_data
613
+ generated_files[requests_file_path] = output_txt
614
+ else
615
+ File.write(requests_file_path, output_txt)
616
+ format_and_check_file(requests_file_path, @logger)
617
+ message = "** Requests file: #{swagger_file}.rb that contains the code of the requests after importing the Swagger file"
618
+ puts message unless silent
619
+ @logger.info message
620
+ end
630
621
  else
631
622
  unless files.key?(module_requests)
632
- files[module_requests] = Array.new
623
+ files[module_requests] = []
633
624
  end
634
- files[module_requests].concat(output) #for the last one
625
+ files[module_requests].concat(output)
635
626
 
636
627
  requires_txt = ""
637
628
  message = "** Generated files that contain the code of the requests after importing the Swagger file: "
@@ -640,19 +631,22 @@ class OpenApiImport
640
631
  files.each do |mod, out_mod|
641
632
  output = output_header + out_mod + output_footer
642
633
  output_txt = output.join("\n")
643
- requests_file_path = file_to_convert + "_" + mod + ".rb"
634
+ requests_file_path = "#{file_to_convert}_#{mod}.rb"
644
635
  requires_txt += "require_relative '#{File.basename(swagger_file)}_#{mod}'\n"
645
- File.open(requests_file_path, "w") { |file| file.write(output_txt) }
646
- res_rufo = `rufo #{requests_file_path}`
647
- message = " - #{requests_file_path}"
648
- puts message unless silent
649
- @logger.info message
650
- @logger.error " Error formating with rufo" unless res_rufo.to_s.match?(/\AFormat:.+$\s*\z/)
651
- @logger.error " Syntax Error: #{`ruby -c #{requests_file_path}`}" unless `ruby -c #{requests_file_path}`.include?("Syntax OK")
636
+ if return_data
637
+ generated_files[requests_file_path] = output_txt
638
+ else
639
+ File.write(requests_file_path, output_txt)
640
+ format_and_check_file(requests_file_path, @logger)
641
+ display_path = "#{swagger_file}_#{mod}.rb"
642
+ message = " - #{display_path}"
643
+ puts message unless silent
644
+ @logger.info message
645
+ end
652
646
  end
653
647
 
654
- requests_file_path = file_to_convert + ".rb"
655
- if required_constants.size > 0
648
+ requests_file_path = "#{file_to_convert}.rb"
649
+ if required_constants.size.positive?
656
650
  rconsts = "# Required constants\n"
657
651
  required_constants.uniq!
658
652
  required_constants.each do |rq|
@@ -663,37 +657,88 @@ class OpenApiImport
663
657
  rconsts = ""
664
658
  end
665
659
 
666
- File.open(requests_file_path, "w") { |file| file.write(rconsts + requires_txt) }
667
- res_rufo = `rufo #{requests_file_path}`
668
- message = "** File that contains all the requires for all Request files: \n"
669
- message += " - #{requests_file_path} "
670
- puts message unless silent
671
- @logger.info message
672
- @logger.error " Error formating with rufo" unless res_rufo.to_s.match?(/\AFormat:.+$\s*\z/)
673
- @logger.error " Syntax Error: #{`ruby -c #{requests_file_path}`}" unless `ruby -c #{requests_file_path}`.include?("Syntax OK")
660
+ if return_data
661
+ generated_files[requests_file_path] = rconsts + requires_txt
662
+ else
663
+ File.write(requests_file_path, rconsts + requires_txt)
664
+ format_and_check_file(requests_file_path, @logger)
665
+ message = "** File that contains all the requires for all Request files: \n"
666
+ message += " - #{swagger_file}.rb "
667
+ puts message unless silent
668
+ @logger.info message
669
+ end
674
670
  end
675
671
 
672
+ return generated_files if return_data
673
+
676
674
  begin
677
- res = eval(output_txt)
678
- rescue Exception => stack
679
- import_errors += "\n\nResult evaluating the ruby file generated: \n" + stack.to_s
675
+ load File.expand_path(requests_file_path)
676
+ rescue StandardError => e
677
+ import_errors += "\n\nResult evaluating the ruby file generated: \n#{e}"
680
678
  end
681
679
 
682
- if import_errors.to_s != ""
683
- File.open(file_errors, "w") { |file| file.write(import_errors) }
684
- message = "* It seems there was a problem importing the Swagger file #{file_to_convert}\n"
680
+ if import_errors.to_s == ""
681
+ true
682
+ else
683
+ File.write(file_errors, import_errors)
684
+ message = "* It seems there was a problem importing the Swagger file #{swagger_file}\n"
685
685
  message += "* Take a look at the detected errors at #{file_errors}\n"
686
686
  warn message
687
687
  @logger.fatal message
688
- return false
689
- else
690
- return true
688
+ false
689
+ end
690
+ rescue ParseError
691
+ raise
692
+ rescue StandardError => e
693
+ puts e.message
694
+ @logger.fatal e.message
695
+ @logger.fatal e.backtrace
696
+ puts e.backtrace
697
+ end
698
+ end
699
+
700
+ private_class_method def self.format_and_check_file(file_path, logger)
701
+ escaped_path = Shellwords.shellescape(file_path)
702
+ res_rufo = `rufo #{escaped_path}`
703
+ logger.error " Error formatting with rufo" unless res_rufo.to_s.match?(/\AFormat:.+$\s*\z/)
704
+ syntax_result = `ruby -c #{escaped_path} 2>&1`
705
+ logger.error " Syntax Error: #{syntax_result}" unless syntax_result.include?("Syntax OK")
706
+ rescue Errno::ENOENT => e
707
+ logger.error " Could not run formatter/syntax checker: #{e.message}"
708
+ end
709
+
710
+ private_class_method def self.build_example_value(dpv)
711
+ if dpv.key?(:example)
712
+ dpv[:example]
713
+ elsif dpv.key?(:examples) && dpv[:examples].is_a?(Array) && !dpv[:examples].empty?
714
+ dpv[:examples].first
715
+ elsif dpv.key?(:type)
716
+ effective_type = dpv[:type]
717
+ effective_type = Array(effective_type).reject { |t| t == "null" }.first if effective_type.is_a?(Array)
718
+ case effective_type.to_s.downcase
719
+ when "string" then dpv[:format] || "string"
720
+ when "integer" then 0
721
+ when "number"
722
+ %w[float double decimal].include?(dpv[:format].to_s.downcase) ? 0.0 : 0
723
+ when "boolean" then true
724
+ when "object"
725
+ if dpv.key?(:properties)
726
+ result = {}
727
+ dpv[:properties].each { |k, v| result[k] = build_example_value(v) }
728
+ result
729
+ else
730
+ {}
731
+ end
732
+ when "array"
733
+ if dpv.key?(:items) && dpv[:items].is_a?(Hash)
734
+ [build_example_value(dpv[:items])]
735
+ else
736
+ []
737
+ end
738
+ else dpv[:format] || ""
691
739
  end
692
- rescue StandardError => stack
693
- puts stack.message
694
- @logger.fatal stack.message
695
- @logger.fatal stack.backtrace
696
- puts stack.backtrace
740
+ else
741
+ ""
697
742
  end
698
743
  end
699
744
  end