swagger_autogenerate 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/lib/swagger_autogenerate/configuration.rb +13 -4
- data/lib/swagger_autogenerate/swagger_trace.rb +196 -33
- data/lib/swagger_autogenerate/version.rb +1 -1
- data/lib/swagger_autogenerate.rb +15 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08b276dd458b54318a75f32c666d40840602065d89a8fa26e7ffac44341e0f15'
|
4
|
+
data.tar.gz: bacd1e78fe71a2909d112901a9e6ba2900117b7e2a45522b1d25f11b542aa624
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f6c2c1c285f982a2677daf6d90040bed5682595fd65ee690ac2e01d188a37bc4afca6f4531fc4e8daad142dd5098a54d4dcb336c58ebc97e2ec305afba9eff7
|
7
|
+
data.tar.gz: 4c5d8642ac585f043cab7193e3eb9fb7fbe3c027c41d6bcdae6b17b5f6b905a4559469db54f166a9014a4283f9c1d798968a30f18c1ee38160c4579985d5626a
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -64,7 +64,7 @@ This file should contain the test scenarios for each action (e.g., index, show,
|
|
64
64
|
|
65
65
|
3) Run the spec code using the rspec command and set the environment variable SWAGGER to the desired YAML file name. For example:
|
66
66
|
```
|
67
|
-
|
67
|
+
SWAGGER_PATH='employee_apis.yaml' rspec spec/your_path/employees_controller_spec.rb
|
68
68
|
```
|
69
69
|
4) This command runs the spec file and instructs the swagger_autogenerate gem to generate Swagger YAML documentation and save it to the file named employee_apis.yaml.
|
70
70
|
5) Once the command finishes executing, you will have the Swagger YAML documentation generated based on the test scenarios in the employees_controller_spec.rb file.
|
@@ -1,15 +1,19 @@
|
|
1
1
|
module SwaggerAutogenerate
|
2
2
|
class Configuration
|
3
|
-
attr_accessor :with_config, :with_multiple_examples, :with_example_description,
|
4
|
-
:with_response_description, :
|
5
|
-
:environment_name, :security, :swagger_config, :response_status
|
3
|
+
attr_accessor :with_config, :with_multiple_examples, :with_rspec_examples, :with_example_description,
|
4
|
+
:with_response_description, :swagger_path_environment_variable, :generate_swagger_environment_variable,
|
5
|
+
:default_path, :environment_name, :security, :swagger_config, :response_status
|
6
6
|
|
7
7
|
def initialize
|
8
8
|
@with_config = true
|
9
9
|
@with_multiple_examples = true
|
10
|
+
@with_rspec_examples = true
|
11
|
+
# remove this when we do not need it any more
|
10
12
|
@with_example_description = true
|
11
13
|
@with_response_description = true
|
12
|
-
@
|
14
|
+
@swagger_path_environment_variable = 'SWAGGER_PATH'
|
15
|
+
@generate_swagger_environment_variable = 'SWAGGER_GENERATE'
|
16
|
+
@default_path = 'swagger'
|
13
17
|
@environment_name = :test
|
14
18
|
@security = default_security
|
15
19
|
@swagger_config = default_swagger_config
|
@@ -78,4 +82,9 @@ module SwaggerAutogenerate
|
|
78
82
|
def self.configure
|
79
83
|
yield(configuration)
|
80
84
|
end
|
85
|
+
|
86
|
+
def self.extract_description(full_rspec_description)
|
87
|
+
parts = full_rspec_description.split(' ')
|
88
|
+
parts&.length > 1 ? parts[2..-1].join(" ") : full_rspec_description
|
89
|
+
end
|
81
90
|
end
|
@@ -5,11 +5,12 @@ module SwaggerAutogenerate
|
|
5
5
|
def initialize(request, response)
|
6
6
|
@with_config = ::SwaggerAutogenerate.configuration.with_config
|
7
7
|
@with_multiple_examples = ::SwaggerAutogenerate.configuration.with_multiple_examples
|
8
|
-
@
|
8
|
+
@with_rspec_examples = ::SwaggerAutogenerate.configuration.with_rspec_examples
|
9
9
|
@with_response_description = ::SwaggerAutogenerate.configuration.with_response_description
|
10
10
|
@security = ::SwaggerAutogenerate.configuration.security
|
11
11
|
@swagger_config = ::SwaggerAutogenerate.configuration.swagger_config
|
12
12
|
@response_status = ::SwaggerAutogenerate.configuration.response_status
|
13
|
+
@default_path = ::SwaggerAutogenerate.configuration.default_path
|
13
14
|
@request = request
|
14
15
|
@response = response
|
15
16
|
@@paths = {}
|
@@ -20,12 +21,20 @@ module SwaggerAutogenerate
|
|
20
21
|
write_swagger_trace
|
21
22
|
end
|
22
23
|
|
23
|
-
def self.
|
24
|
-
::SwaggerAutogenerate.configuration.
|
24
|
+
def self.swagger_path_environment_variable
|
25
|
+
::SwaggerAutogenerate.configuration.swagger_path_environment_variable
|
25
26
|
end
|
26
27
|
|
27
|
-
def
|
28
|
-
SwaggerTrace.
|
28
|
+
def swagger_path_environment_variable
|
29
|
+
SwaggerTrace.swagger_path_environment_variable
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.generate_swagger_environment_variable
|
33
|
+
::SwaggerAutogenerate.configuration.generate_swagger_environment_variable
|
34
|
+
end
|
35
|
+
|
36
|
+
def generate_swagger_environment_variable
|
37
|
+
SwaggerTrace.generate_swagger_environment_variable
|
29
38
|
end
|
30
39
|
|
31
40
|
def self.environment_name
|
@@ -39,8 +48,8 @@ module SwaggerAutogenerate
|
|
39
48
|
private
|
40
49
|
|
41
50
|
attr_reader :request, :response, :current_path, :yaml_file, :configuration,
|
42
|
-
:with_config, :with_multiple_examples, :
|
43
|
-
:with_response_description, :security, :response_status, :swagger_config
|
51
|
+
:with_config, :with_multiple_examples, :with_rspec_examples,
|
52
|
+
:with_response_description, :security, :response_status, :swagger_config, :default_path
|
44
53
|
|
45
54
|
# main methods
|
46
55
|
|
@@ -88,6 +97,13 @@ module SwaggerAutogenerate
|
|
88
97
|
data['paths'] = paths
|
89
98
|
organize_result(data['paths'])
|
90
99
|
data = data.to_hash
|
100
|
+
# handel examples names
|
101
|
+
example_title = full_rspec_description.present? ? full_rspec_description : 'example-0'
|
102
|
+
old_examples = data['paths'][current_path][request.method.downcase]['responses'][response.status.to_s]['content']['application/json']['examples']
|
103
|
+
current_example = old_examples[example_title]
|
104
|
+
new_example(example_title, current_example, old_examples, data['paths'], true)
|
105
|
+
# result
|
106
|
+
|
91
107
|
result = add_quotes_to_dates(YAML.dump(data))
|
92
108
|
file.write(result)
|
93
109
|
end
|
@@ -209,17 +225,24 @@ module SwaggerAutogenerate
|
|
209
225
|
}
|
210
226
|
end
|
211
227
|
|
212
|
-
def convert_to_multipart(payload)
|
228
|
+
def convert_to_multipart(payload, main_key = nil, index = nil)
|
229
|
+
payload_keys.push(main_key) if main_key.present?
|
213
230
|
payload.each do |key, value|
|
214
231
|
if value.is_a?(Hash)
|
215
232
|
payload_keys.push(key)
|
216
233
|
convert_to_multipart(value)
|
234
|
+
elsif value.is_a?(Array)
|
235
|
+
value.each_with_index { |v, index| convert_to_multipart(v, key, index) }
|
217
236
|
else
|
218
237
|
keys = payload_keys.clone
|
219
238
|
first_key = keys.shift
|
220
|
-
|
221
|
-
|
239
|
+
if index.present?
|
240
|
+
keys.each { |inner_key| first_key = "#{first_key}[#{inner_key}][#{index}]" }
|
241
|
+
else
|
242
|
+
keys.each { |inner_key| first_key = "#{first_key}[#{inner_key}]" }
|
243
|
+
end
|
222
244
|
|
245
|
+
first_key = "#{first_key}[#{key}]"
|
223
246
|
payload_hash.merge!({ first_key => { 'type' => schema_type(value), 'example' => example(value) } })
|
224
247
|
end
|
225
248
|
end
|
@@ -230,6 +253,8 @@ module SwaggerAutogenerate
|
|
230
253
|
data.map do |key, value|
|
231
254
|
if value.is_a?(Hash)
|
232
255
|
hash_data.merge!({ key => value })
|
256
|
+
elsif value.is_a?(Array)
|
257
|
+
value.each_with_index { |v, index| convert_to_multipart(v, key) }
|
233
258
|
else
|
234
259
|
payload_hash.merge!({ key => { 'type' => schema_type(value), 'example' => example(value) } })
|
235
260
|
end
|
@@ -250,10 +275,93 @@ module SwaggerAutogenerate
|
|
250
275
|
}
|
251
276
|
end
|
252
277
|
|
278
|
+
def json_to_content_form_data(json)
|
279
|
+
{
|
280
|
+
'multipart/form-data' => {
|
281
|
+
'schema' => build_properties(json)
|
282
|
+
}
|
283
|
+
}
|
284
|
+
end
|
285
|
+
|
286
|
+
def build_properties(json)
|
287
|
+
case json
|
288
|
+
when Hash
|
289
|
+
hash_properties = json.transform_values { |value| build_properties(value) if value.present? }
|
290
|
+
hash_properties = hash_properties.delete_if { |_k, v| !v.present? }
|
291
|
+
|
292
|
+
hashs = {
|
293
|
+
'type' => 'object',
|
294
|
+
'properties' => hash_properties
|
295
|
+
}
|
296
|
+
when Array
|
297
|
+
item_schemas = json.map { |item| build_properties(item) }
|
298
|
+
merged_schema = merge_array_schemas(item_schemas)
|
299
|
+
|
300
|
+
if merged_schema[:type] == 'object'
|
301
|
+
{ 'type' => 'array', 'items' => merged_schema }
|
302
|
+
else
|
303
|
+
{ 'type' => 'array', 'items' => { 'oneOf' => item_schemas.uniq } }
|
304
|
+
end
|
305
|
+
when String
|
306
|
+
{ 'type' => 'string', 'example' => json }
|
307
|
+
when Integer
|
308
|
+
{ 'type' => 'integer', 'example' => json }
|
309
|
+
when Float
|
310
|
+
{ 'type' => 'number', 'example' => json }
|
311
|
+
when TrueClass, FalseClass
|
312
|
+
{ 'type' => 'boolean', 'example' => json }
|
313
|
+
else
|
314
|
+
{ 'type' => 'string', 'example' => json.to_s }
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def merge_array_schemas(schemas)
|
319
|
+
return {} if schemas.empty?
|
320
|
+
|
321
|
+
# Attempt to merge all schemas into a single schema
|
322
|
+
schemas.reduce do |merged, schema|
|
323
|
+
merge_properties(merged, schema)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def merge_properties(old_data, new_data)
|
328
|
+
return old_data unless old_data.is_a?(Hash) && new_data.is_a?(Hash)
|
329
|
+
|
330
|
+
merged = old_data.dup
|
331
|
+
new_data.each do |key, value|
|
332
|
+
merged[key] = if merged[key].is_a?(Hash) && value.is_a?(Hash)
|
333
|
+
merge_properties(merged[key], value)
|
334
|
+
else
|
335
|
+
value
|
336
|
+
end
|
337
|
+
end
|
338
|
+
merged
|
339
|
+
end
|
340
|
+
|
341
|
+
def content_application_json_schema_properties(data)
|
342
|
+
hash_data = {}
|
343
|
+
data.map do |key, value|
|
344
|
+
if value.is_a?(Hash)
|
345
|
+
hash_data.merge!({ key => value })
|
346
|
+
elsif value.is_a?(Array)
|
347
|
+
value.each_with_index { |v, index| convert_to_multipart(v, key, index) }
|
348
|
+
else
|
349
|
+
payload_hash.merge!({ key => { 'type' => schema_type(value), 'example' => example(value) } })
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
convert_to_multipart(hash_data)
|
354
|
+
converted_payload = @payload_hash.clone
|
355
|
+
@payload_hash = nil
|
356
|
+
@payload_keys = nil
|
357
|
+
|
358
|
+
converted_payload
|
359
|
+
end
|
360
|
+
|
253
361
|
def content_body(data)
|
254
362
|
hash = {}
|
255
363
|
# hash.merge!(content_json(data))
|
256
|
-
hash.merge!(
|
364
|
+
hash.merge!(json_to_content_form_data(data))
|
257
365
|
|
258
366
|
{ 'content' => hash }
|
259
367
|
end
|
@@ -332,10 +440,14 @@ module SwaggerAutogenerate
|
|
332
440
|
def swagger_location
|
333
441
|
return @swagger_location if instance_variable_defined?(:@swagger_location)
|
334
442
|
|
335
|
-
if ENV[
|
336
|
-
|
443
|
+
if ENV[generate_swagger_environment_variable].present?
|
444
|
+
directory_path = Rails.root.join(default_path).to_s
|
445
|
+
FileUtils.mkdir_p(directory_path) unless File.directory?(directory_path)
|
446
|
+
@swagger_location = "#{directory_path}/#{tags.first}.yaml"
|
447
|
+
elsif ENV[swagger_path_environment_variable].include?('.yaml') || ENV[swagger_path_environment_variable].include?('.yml')
|
448
|
+
@swagger_location = Rails.root.join(ENV.fetch(swagger_path_environment_variable, nil).to_s).to_s
|
337
449
|
else
|
338
|
-
directory_path = Rails.root.join(ENV.fetch(
|
450
|
+
directory_path = Rails.root.join(ENV.fetch(swagger_path_environment_variable, nil).to_s).to_s
|
339
451
|
FileUtils.mkdir_p(directory_path) unless File.directory?(directory_path)
|
340
452
|
@swagger_location = "#{directory_path}/#{tags.first}.yaml"
|
341
453
|
end
|
@@ -351,19 +463,18 @@ module SwaggerAutogenerate
|
|
351
463
|
end
|
352
464
|
|
353
465
|
def content_json_example(data)
|
354
|
-
|
466
|
+
example_title = full_rspec_description.present? ? full_rspec_description : 'example-0'
|
467
|
+
|
468
|
+
{
|
355
469
|
'application/json' => {
|
356
470
|
'schema' => { 'type' => 'object' },
|
357
471
|
'examples' => {
|
358
|
-
|
472
|
+
example_title => {
|
359
473
|
'value' => data
|
360
474
|
}
|
361
475
|
}
|
362
476
|
}
|
363
477
|
}
|
364
|
-
hash['application/json']['examples']['example-0']['description'] = "payload => #{example_description}" if with_example_description && !example_description.empty?
|
365
|
-
|
366
|
-
hash
|
367
478
|
end
|
368
479
|
|
369
480
|
def example_description
|
@@ -396,20 +507,48 @@ module SwaggerAutogenerate
|
|
396
507
|
@payload_hash ||= {}
|
397
508
|
end
|
398
509
|
|
399
|
-
def new_example
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
last_example =
|
405
|
-
last_example
|
406
|
-
|
407
|
-
|
510
|
+
def new_example(example_title, current_example, old_examples, all_paths = yaml_file['paths'], with_schema_properties = false)
|
511
|
+
if !old_examples.value?(current_example)
|
512
|
+
last_example = handel_name_last_example(old_examples)
|
513
|
+
last_example ||= example_title
|
514
|
+
last_example = example_title unless with_multiple_examples
|
515
|
+
all_paths[current_path][request.method.downcase]['responses'][response.status.to_s]['content']['application/json']['examples'][last_example] = current_example
|
516
|
+
add_properties_to_schema(last_example, all_paths[current_path])
|
517
|
+
elsif with_schema_properties
|
518
|
+
add_properties_to_schema(full_rspec_description.present? ? full_rspec_description : 'example-0', all_paths[current_path])
|
408
519
|
end
|
409
520
|
|
410
521
|
true
|
411
522
|
end
|
412
523
|
|
524
|
+
def handel_name_last_example(old_examples)
|
525
|
+
last_example = old_examples.keys.last
|
526
|
+
last_example += '-1' if json_example_plus_one(last_example) == last_example
|
527
|
+
json_example_plus_one(last_example)
|
528
|
+
end
|
529
|
+
|
530
|
+
def add_properties_to_schema(last_example, main_path = yaml_file['paths'][current_path])
|
531
|
+
parameters = {}
|
532
|
+
parameters.merge!(request_parameters.values.first, query_parameters.values.first, path_parameters.values.first)
|
533
|
+
hash = {
|
534
|
+
last_example => build_properties(parameters.as_json)
|
535
|
+
}
|
536
|
+
|
537
|
+
main_path[request.method.downcase]['responses'][response.status.to_s].deep_merge!(
|
538
|
+
{
|
539
|
+
'content' => {
|
540
|
+
'application/json' => {
|
541
|
+
'schema' => {
|
542
|
+
'description' => 'These are the payloads for each example',
|
543
|
+
'type' => 'object',
|
544
|
+
'properties' => hash
|
545
|
+
}
|
546
|
+
}
|
547
|
+
}
|
548
|
+
}
|
549
|
+
)
|
550
|
+
end
|
551
|
+
|
413
552
|
def apply_yaml_file_changes
|
414
553
|
(check_path || check_method || check_status) &&
|
415
554
|
(check_parameters || check_parameter) &&
|
@@ -438,24 +577,29 @@ module SwaggerAutogenerate
|
|
438
577
|
def check_path
|
439
578
|
unless old_paths.key?(current_path)
|
440
579
|
yaml_file['paths'].merge!({ current_path => paths[current_path] })
|
580
|
+
update_example_title(true)
|
441
581
|
end
|
442
582
|
end
|
443
583
|
|
444
584
|
def check_method
|
445
585
|
unless old_paths[current_path].key?(request.method.downcase)
|
446
586
|
yaml_file['paths'][current_path][request.method.downcase] = { 'responses' => swagger_response }
|
587
|
+
update_example_title(true)
|
447
588
|
end
|
448
589
|
end
|
449
590
|
|
450
591
|
def check_status
|
592
|
+
example_title = full_rspec_description.present? ? full_rspec_description : 'example-0'
|
451
593
|
if old_paths[current_path][request.method.downcase]['responses'].present?
|
452
594
|
if old_paths[current_path][request.method.downcase]['responses']&.key?(response.status.to_s)
|
453
|
-
|
595
|
+
update_example_title
|
454
596
|
else
|
455
597
|
yaml_file['paths'][current_path][request.method.downcase]['responses'].merge!(swagger_response)
|
598
|
+
update_example_title(true)
|
456
599
|
end
|
457
600
|
else
|
458
601
|
yaml_file['paths'][current_path][request.method.downcase]['responses'] = swagger_response
|
602
|
+
update_example_title
|
459
603
|
end
|
460
604
|
end
|
461
605
|
|
@@ -481,12 +625,31 @@ module SwaggerAutogenerate
|
|
481
625
|
|
482
626
|
def check_request_body
|
483
627
|
if paths[current_path][request.method.downcase]['requestBody'].present?
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
628
|
+
param_current_hash = paths[current_path][request.method.downcase]['requestBody']['content']['multipart/form-data']['schema']['properties']
|
629
|
+
param_current_file = yaml_file['paths'][current_path][request.method.downcase]['requestBody']['content']['multipart/form-data']['schema']['properties']
|
630
|
+
if param_current_hash.present? && param_current_file.present?
|
631
|
+
param_names = param_current_hash.keys - param_current_file.keys
|
632
|
+
param_names.each do |param_name|
|
633
|
+
param = paths[current_path][request.method.downcase]['requestBody']['content']['multipart/form-data']['schema']['properties'].select { |parameter| parameter == param_name }
|
634
|
+
yaml_file['paths'][current_path][request.method.downcase]['requestBody']['content']['multipart/form-data']['schema']['properties'].merge!(param)
|
635
|
+
end
|
488
636
|
end
|
489
637
|
end
|
490
638
|
end
|
639
|
+
|
640
|
+
def update_example_title(with_schema_properties = false)
|
641
|
+
example_title = full_rspec_description.present? ? full_rspec_description : 'example-0'
|
642
|
+
current_example = swagger_response[response.status.to_s]['content']['application/json']['examples'][example_title]
|
643
|
+
old_examples = old_paths[current_path][request.method.downcase]['responses'][response.status.to_s]['content']['application/json']['examples']
|
644
|
+
new_example(example_title, current_example, old_examples, yaml_file['paths'], with_schema_properties)
|
645
|
+
end
|
646
|
+
|
647
|
+
def full_rspec_description
|
648
|
+
with_rspec_examples ? SwaggerAutogenerate::SwaggerTrace.rspec_description : nil
|
649
|
+
end
|
650
|
+
|
651
|
+
class << self
|
652
|
+
attr_accessor :rspec_description
|
653
|
+
end
|
491
654
|
end
|
492
655
|
end
|
data/lib/swagger_autogenerate.rb
CHANGED
@@ -8,14 +8,24 @@ module SwaggerAutogenerate
|
|
8
8
|
def process_action(*args)
|
9
9
|
super
|
10
10
|
|
11
|
-
if
|
11
|
+
if SwaggerAutogenerate.allow_swagger?
|
12
12
|
SwaggerTrace.new(request, response).call
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
16
|
+
|
17
|
+
def self.allow_swagger?
|
18
|
+
(ENV[SwaggerTrace.swagger_path_environment_variable].present? || ENV[SwaggerTrace.generate_swagger_environment_variable].present?) &&
|
19
|
+
Rails.env.send("#{SwaggerTrace.environment_name.to_s}?")
|
20
|
+
end
|
16
21
|
end
|
17
22
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
23
|
+
if defined?(RSpec) && SwaggerAutogenerate.allow_swagger?
|
24
|
+
require 'rspec/rails'
|
25
|
+
|
26
|
+
RSpec.configure do |config|
|
27
|
+
config.before(:each) do |example|
|
28
|
+
SwaggerAutogenerate::SwaggerTrace.rspec_description = SwaggerAutogenerate.extract_description(example.metadata[:full_description])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: swagger_autogenerate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- MohammedBuraiah
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-08 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Generating Swagger YAML Automatically Based on Existing Test Cases in
|
14
14
|
Ruby on Rails
|