auto-rswag 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7bd7f5d1bb6327a80a89497075356aafd2cdcba798b8d7258f2d31c682666a95
4
+ data.tar.gz: b509baea5844020859126427811398fa40e79496a0d5e66ef3a881c08c841c39
5
+ SHA512:
6
+ metadata.gz: db3929e25c816dcd3710c081eed70c089112149d1befb1570de3a84b9306dd4897191b6643a1c47e4c74e9391a4d1b150195172be6d2fbf05f50e33bbc5fdc2e
7
+ data.tar.gz: 4ba7b0c799057de66e6ed2c8cc852cd67e8c15053344da60a453c0aa3dfeae861e4bacf14e07161cfd876d8f2a138145234afff7efc31cec7d741afc000992ef
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in auto-rswag.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,63 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ auto-rswag (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.0)
10
+ coderay (1.1.2)
11
+ coveralls (0.8.23)
12
+ json (>= 1.8, < 3)
13
+ simplecov (~> 0.16.1)
14
+ term-ansicolor (~> 1.3)
15
+ thor (>= 0.19.4, < 2.0)
16
+ tins (~> 1.6)
17
+ docile (1.3.2)
18
+ jaro_winkler (1.5.4)
19
+ json (2.3.0)
20
+ method_source (1.0.0)
21
+ parallel (1.19.1)
22
+ parser (2.7.0.5)
23
+ ast (~> 2.4.0)
24
+ pry (0.13.0)
25
+ coderay (~> 1.1)
26
+ method_source (~> 1.0)
27
+ rainbow (3.0.0)
28
+ rexml (3.2.4)
29
+ rspec (0.9.4)
30
+ rubocop (0.80.1)
31
+ jaro_winkler (~> 1.5.1)
32
+ parallel (~> 1.10)
33
+ parser (>= 2.7.0.1)
34
+ rainbow (>= 2.2.2, < 4.0)
35
+ rexml
36
+ ruby-progressbar (~> 1.7)
37
+ unicode-display_width (>= 1.4.0, < 1.7)
38
+ ruby-progressbar (1.10.1)
39
+ simplecov (0.16.1)
40
+ docile (~> 1.1)
41
+ json (>= 1.8, < 3)
42
+ simplecov-html (~> 0.10.0)
43
+ simplecov-html (0.10.2)
44
+ sync (0.5.0)
45
+ term-ansicolor (1.7.1)
46
+ tins (~> 1.0)
47
+ thor (1.0.1)
48
+ tins (1.24.1)
49
+ sync
50
+ unicode-display_width (1.6.1)
51
+
52
+ PLATFORMS
53
+ ruby
54
+
55
+ DEPENDENCIES
56
+ auto-rswag!
57
+ coveralls (>= 0.8.23)
58
+ pry (~> 0)
59
+ rspec (~> 0)
60
+ rubocop (>= 0.80.1)
61
+
62
+ BUNDLED WITH
63
+ 2.0.2
data/lib/auto_rswag.rb ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative 'printer.rb'
5
+ require_relative 'auto_rswag_helper.rb'
6
+ require_relative 'doc_writer.rb'
7
+
8
+ # This module hooks into the Rspec test to retrieve useful
9
+ # metadata and submit it to AutoRswagHelper for conversion.
10
+ module AutoRswag
11
+ def update_documentation
12
+ $title = metadata[:response][:schema]['$ref'].split('/').last
13
+
14
+ after do
15
+ payload = AutoRswagHelper.convert_response(response)
16
+ AutoRswagHelper.map_fields(payload)
17
+ docs = SwaggerPrinter.print_swagger(payload, $title)
18
+ DocWriter.write_docs(docs, $title)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This class performs conversions of the response data
4
+ # based on test metadata.
5
+ class AutoRswagHelper
6
+ class << self
7
+ def convert_response(response)
8
+ return response if [Array, Hash].include?(response.class)
9
+
10
+ body = response.body
11
+ body = body.respond_to?(:read) ? body.read : body
12
+
13
+ JSON.parse(body)
14
+ rescue StandardError
15
+ body
16
+ end
17
+
18
+ def map_fields(object)
19
+ if object.is_a? Array
20
+ object = {
21
+ type: :array,
22
+ items: map_fields(object.first)
23
+ }
24
+ elsif object.is_a?(Hash)
25
+ object = {
26
+ type: :object,
27
+ properties: map_object_keys(object)
28
+ }
29
+ end
30
+ object
31
+ end
32
+
33
+ def map_object_keys(object)
34
+ object.keys.each do |key|
35
+ value = object.delete(key)
36
+ converted_key = convert(key)
37
+ if value.is_a? Hash
38
+ object[converted_key] = {
39
+ type: :object,
40
+ properties: value
41
+ }
42
+ map_fields(value)
43
+ elsif value.is_a? Array
44
+ object[converted_key] = {
45
+ type: :array,
46
+ items: [value.first]
47
+ }
48
+ map_fields(value)
49
+ else
50
+ object[converted_key] = parse_field(value)
51
+ end
52
+ end
53
+ end
54
+
55
+ def convert(key)
56
+ key.to_sym
57
+ end
58
+
59
+ def parse_field(value)
60
+ type = value.nil? ? :string : value.class.to_s.downcase.to_sym
61
+ {
62
+ type: type,
63
+ example: value,
64
+ 'x-nullable' => true
65
+ }
66
+ end
67
+ end
68
+ end
data/lib/doc_writer.rb ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This class writes the parsed documenation to the swagger_helper file.
4
+ class DocWriter
5
+ class << self
6
+ def default_docs_path
7
+ Rails.root.join('spec', 'swagger_helper.rb').to_s
8
+ end
9
+
10
+ def write_docs(docs, fragment_title)
11
+ old_documentation = File.read(default_docs_path)
12
+ new_documentation_fragment = example_hash(fragment_title, old_documentation)
13
+ return if new_documentation_fragment.nil?
14
+
15
+ new_documentation = old_documentation.sub(new_documentation_fragment, docs)
16
+ File.open(default_docs_path, 'w') { |f| f.write(new_documentation) }
17
+ end
18
+
19
+ def example_hash(fragment_title, old_documentation)
20
+ match = ''
21
+ iterator = 1
22
+ while true do
23
+ regex = /(#{fragment_title})(.*?\}){#{iterator}}/
24
+ new_match_data = old_documentation.match(regex)
25
+ new_match = new_match_data.present? ? new_match_data[0] : nil
26
+ return unless new_match != match
27
+
28
+ match = new_match
29
+ begin
30
+ return match if eval("{#{match}}")
31
+ rescue SyntaxError
32
+ end
33
+
34
+ iterator += 1
35
+ end
36
+ end
37
+ end
38
+ end
data/lib/printer.rb ADDED
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This class walks through the payload object and
4
+ # builds a prettified representation in rswag format.
5
+ class SwaggerPrinter
6
+ class << self
7
+ def print_swagger(object, test_title)
8
+ @indent ||= 2
9
+ line = "#{test_title}: {\n"
10
+ line += print_values(object)
11
+ line + end_wrap
12
+ end
13
+
14
+ def wrap_hash
15
+ line = ' ' * @indent + "type: :object,\n"
16
+ line += ' ' * @indent + "properties: {\n"
17
+ @indent += 2
18
+ line
19
+ end
20
+
21
+ def wrap_array
22
+ line = ' ' * @indent + "type: :array,\n"
23
+ line += ' ' * @indent + "items: {\n"
24
+ @indent += 2
25
+ line
26
+ end
27
+
28
+ def end_wrap
29
+ line = ''
30
+ while @indent > 2
31
+ @indent -= 2
32
+ line += ' ' * @indent + "}\n"
33
+ end
34
+ line + '}'
35
+ end
36
+
37
+ def print_values(object)
38
+ return wrap_array + print_values(object.first) if object.is_a?(Array)
39
+
40
+ output = wrap_hash
41
+ object.each_with_index do |(key, val), i|
42
+ line = if val[:type] == :object
43
+ print_hash(key, val)
44
+ elsif val[:type] == :array
45
+ print_array(key, val)
46
+ else
47
+ print_line(key, val)
48
+ end
49
+ comma = i == object.keys.size - 1 ? '' : ','
50
+ line += "#{comma}\n"
51
+ output += line
52
+ end
53
+ output
54
+ end
55
+
56
+ def print_hash(key, val)
57
+ line = ' ' * @indent + "#{key}: {\n"
58
+ @indent += 2
59
+ line += print_values(val[:properties])
60
+ @indent -= 2
61
+ line += ' ' * @indent + "}\n"
62
+ @indent -= 2
63
+ line += ' ' * @indent + '}'
64
+ line
65
+ end
66
+
67
+ def print_array(key, val)
68
+ line = ' ' * @indent + "#{key}: {\n"
69
+ @indent += 2
70
+ line += wrap_array
71
+ line += print_values(val[:items].first)
72
+ @indent -= 2
73
+ line += ' ' * @indent + "}\n"
74
+ @indent -= 2
75
+ line += ' ' * @indent + '}'
76
+ line
77
+ end
78
+
79
+ def print_line(key, val)
80
+ line = ' ' * @indent + "#{key}: { "
81
+ val.each_with_index do |(val_key, val_val), j|
82
+ next if val_key == :type && val.any? { |k, v| k == :example && v == nil }
83
+ val_comma = j == val.keys.size - 1 ? '' : ','
84
+ line += "#{escape_key(val_key)}: #{prettify_value(val, val_key, val_val)}#{val_comma} "
85
+ end
86
+ line + '}'
87
+ end
88
+
89
+ def escape_key(key)
90
+ return key unless key.to_s.include?('-')
91
+
92
+ "'#{key.to_s}'"
93
+ end
94
+
95
+ def prettify_value(type, key, val)
96
+ return ':' + val.to_s if key == :type
97
+ return val if key != :example
98
+ return 'nil' if val.nil?
99
+
100
+ type[:type] == :string ? "'#{val}'" : val
101
+ end
102
+ end
103
+ end
data/rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ Metrics/MethodLength:
2
+ Max: 25
3
+
4
+ Metrics/AbcSize:
5
+ Max: 25
6
+
7
+ Metrics/LineLength:
8
+ Max: 99
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: auto-rswag
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tyler Porter
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-03-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: coveralls
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.23
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.23
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 0.80.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.80.1
69
+ description: Convert raw JSON payloads to Rswag configuration to automate endpoint
70
+ documentation
71
+ email: tyler.b.porter@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - Gemfile
77
+ - Gemfile.lock
78
+ - lib/auto_rswag.rb
79
+ - lib/auto_rswag_helper.rb
80
+ - lib/doc_writer.rb
81
+ - lib/printer.rb
82
+ - rubocop.yml
83
+ homepage: https://rubygems.org/gems/auto-rswag
84
+ licenses:
85
+ - MIT
86
+ metadata:
87
+ source_code_uri: https://github.com/pawptart/auto-rswag
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubygems_version: 3.0.3
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: JSON to Rswag configuration
107
+ test_files: []