swagger_autogenerate 1.1.1 → 1.2.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/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
|