swgr2rb 1.0.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 +7 -0
- data/LICENSE +20 -0
- data/README.md +69 -0
- data/assets/Gemfile +8 -0
- data/assets/README.md +94 -0
- data/assets/configs/conductor_sender_configs.yaml +4 -0
- data/assets/endpoint_object_models/config_loaders/base_config.rb +28 -0
- data/assets/endpoint_object_models/config_loaders/conductor_config_loader.rb +18 -0
- data/assets/endpoint_object_models/json_validator/json_data_validator.rb +44 -0
- data/assets/endpoint_object_models/json_validator/json_schema_validator.rb +52 -0
- data/assets/endpoint_object_models/json_validator/json_validator.rb +34 -0
- data/assets/endpoint_object_models/loader.rb +16 -0
- data/assets/endpoint_object_models/object_models/base_endpoint_object_model.rb +22 -0
- data/assets/endpoint_object_models/object_models/base_endpoint_object_model_constants.rb +2 -0
- data/assets/endpoint_object_models/object_models/base_endpoint_object_model_methods.rb +82 -0
- data/assets/endpoint_object_models/prototypes/json_schema_data_types.rb +3 -0
- data/assets/endpoint_object_models/prototypes/request.rb +31 -0
- data/assets/features/step_definitions/base_steps.rb +56 -0
- data/assets/features/support/env.rb +9 -0
- data/assets/features/support/instance_variables.rb +9 -0
- data/assets/features/support/world.rb +12 -0
- data/assets/request_sender/conductor_sender/conductor_sender.rb +53 -0
- data/assets/request_sender/conductor_sender/response.rb +105 -0
- data/assets/request_sender/loader.rb +16 -0
- data/bin/swgr2rb +8 -0
- data/lib/cli/cli_options_parser.rb +139 -0
- data/lib/endpoint_class_config_generator/endpoint_class_config.rb +25 -0
- data/lib/endpoint_class_config_generator/endpoint_class_config_generator.rb +91 -0
- data/lib/endpoint_class_config_generator/json_paths_parser_methods.rb +68 -0
- data/lib/endpoint_class_config_generator/json_schema_definitions_parser_methods.rb +59 -0
- data/lib/endpoint_class_generator/endpoint_class_generator.rb +168 -0
- data/lib/endpoint_class_generator/endpoint_classes_generator.rb +76 -0
- data/lib/endpoint_class_generator/ruby_file_generator.rb +52 -0
- data/lib/endpoint_class_generator/ruby_file_generator_constants.rb +106 -0
- data/lib/endpoint_class_generator/schema_module_generator.rb +49 -0
- data/lib/json_fetcher/swagger_json_fetcher.rb +45 -0
- data/lib/prototypes/json_schema_data_types.rb +11 -0
- data/lib/prototypes/swgr2rb_error.rb +13 -0
- data/lib/request_sender/conductor_sender.rb +28 -0
- data/lib/request_sender/request.rb +31 -0
- data/lib/request_sender/response.rb +115 -0
- data/lib/scaffold_generator/feature_file_generator.rb +62 -0
- data/lib/scaffold_generator/scaffold_generator.rb +51 -0
- data/lib/scaffold_generator/scaffold_generator_constants.rb +19 -0
- data/lib/swgr2rb.rb +53 -0
- metadata +206 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
class Request
|
2
|
+
attr_reader :type, :headers, :body, :subdomain, :endpoint_name
|
3
|
+
|
4
|
+
def initialize(endpoint_name, type, headers, body)
|
5
|
+
@endpoint_name = endpoint_name
|
6
|
+
@type = type
|
7
|
+
@headers = headers
|
8
|
+
@body = body
|
9
|
+
process_request_type
|
10
|
+
end
|
11
|
+
|
12
|
+
def url
|
13
|
+
generate_url
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def process_request_type
|
19
|
+
if type.split(' ').length > 1
|
20
|
+
@type = type.split(' ').join('_')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def generate_url
|
25
|
+
configs = ConductorConfigLoader.load
|
26
|
+
|
27
|
+
"#{configs.connection.protocol}"\
|
28
|
+
"#{configs.connection.host}:#{configs.connection.port}"\
|
29
|
+
"#{endpoint_name}"
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
Given(/^I send "([^"]*)" request to "([^"]*)"(?: with using results of ([^"]*) request to "([^"]*)" endpoint)?$/) do |request_type, endpoint_name, sub_request_type, sub_endpoint_name|
|
2
|
+
request_type = request_type.sub(' ', '_')
|
3
|
+
endpoint_instances["#{request_type}_#{endpoint_name}"] = eval("#{endpoint_name.split(' ').map(&:capitalize).join}.new")
|
4
|
+
if sub_request_type && sub_endpoint_name
|
5
|
+
sub_results = endpoint_instances["#{sub_request_type}_#{sub_endpoint_name}"].results
|
6
|
+
else
|
7
|
+
sub_results = {}
|
8
|
+
end
|
9
|
+
endpoint_instances["#{request_type}_#{endpoint_name}"].send_request(request_type, nil, sub_results)
|
10
|
+
end
|
11
|
+
|
12
|
+
Given(/^I send "([^"]*)" request to "([^"]*)" with the following properties(?: with using results of ([^"]*) request to "([^"]*)" endpoint)?:$/) do |request_type, endpoint_name, sub_request_type, sub_endpoint_name, table|
|
13
|
+
request_type = request_type.sub(' ', '_')
|
14
|
+
endpoint_instances["#{request_type}_#{endpoint_name}"] = eval("#{endpoint_name.split(' ').map(&:capitalize).join}.new")
|
15
|
+
if sub_request_type && sub_endpoint_name
|
16
|
+
sub_results = endpoint_instances["#{sub_request_type}_#{sub_endpoint_name}"].results
|
17
|
+
else
|
18
|
+
sub_results = {}
|
19
|
+
end
|
20
|
+
endpoint_instances["#{request_type}_#{endpoint_name}"].send_request(request_type,
|
21
|
+
table.hashes.first, sub_results)
|
22
|
+
end
|
23
|
+
|
24
|
+
Given(/^I send "([^"]*)" request to "([^"]*)"(?: with the following properties)? with using results of the following requests:$/) do |request_type, endpoint_name, table|
|
25
|
+
request_type = request_type.sub(' ', '_')
|
26
|
+
endpoint_instances["#{request_type}_#{endpoint_name}"] = eval("#{endpoint_name.split(' ').map(&:capitalize).join}.new")
|
27
|
+
sub_results = {}
|
28
|
+
table.hashes.first[:sub_requests].split(/;\s*/).map do |sub_request_str|
|
29
|
+
match_data = sub_request_str.match(/^(?<request_type>\w+) (?<endpoint_name>.+)$/)
|
30
|
+
sub_results[match_data[:endpoint_name]] = endpoint_instances["#{match_data[:request_type]}_#{match_data[:endpoint_name]}"].results
|
31
|
+
end
|
32
|
+
params = table.hashes.first.reject { |k, _v| k.to_sym == :sub_requests }
|
33
|
+
endpoint_instances["#{request_type}_#{endpoint_name}"].send_request(request_type, params, sub_results)
|
34
|
+
end
|
35
|
+
|
36
|
+
And(/^the response schema for "([^"]*)" request to "([^"]*)" endpoint should be valid$/) do |request_type, endpoint_name|
|
37
|
+
request_type = request_type.sub(' ', '_')
|
38
|
+
endpoint_instances["#{request_type}_#{endpoint_name}"].validate_response_schema
|
39
|
+
end
|
40
|
+
|
41
|
+
And(/^the error response for "([^"]*)" request to "([^"]*)" endpoint should be valid with ([^"]*) code$/) do |request_type, endpoint_name, error_code|
|
42
|
+
request_type = request_type.sub(' ', '_')
|
43
|
+
endpoint_instances["#{request_type}_#{endpoint_name}"].validate_error_response(error_code.to_i)
|
44
|
+
end
|
45
|
+
|
46
|
+
And(/^I wait (\d+) seconds/) do |seconds|
|
47
|
+
sleep(seconds)
|
48
|
+
end
|
49
|
+
|
50
|
+
And(/^I (do|do not do) this: (.*[^:])$/) do |do_or_not, step_str|
|
51
|
+
step step_str if do_or_not == 'do'
|
52
|
+
end
|
53
|
+
|
54
|
+
And(/^I (do|do not do) this: (.*:)$/) do |do_or_not, step_str, table|
|
55
|
+
step step_str, table if do_or_not == 'do'
|
56
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require_relative '../../endpoint_object_models/loader'
|
2
|
+
require_relative '../../request_sender/loader'
|
3
|
+
require_relative 'instance_variables'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
class World
|
7
|
+
include InstanceVariables
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
initialize_instance_variables
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require_relative 'response'
|
3
|
+
|
4
|
+
class ConductorSender
|
5
|
+
class << self
|
6
|
+
def send_request(request)
|
7
|
+
response = send("send_#{request.type}_request",
|
8
|
+
request)
|
9
|
+
|
10
|
+
Response.new(response)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def send_post_request(request)
|
16
|
+
HTTParty.post(request.url, body: request.body, headers: request.headers, verify: false)
|
17
|
+
end
|
18
|
+
|
19
|
+
def send_multipart_post_request(request)
|
20
|
+
cmd = "curl -k -i --verbose --request POST "\
|
21
|
+
" --form 'file=@#{request.body[:filePath]}'"\
|
22
|
+
" -H 'Content-Type: #{request.headers[:'Content-Type']}'"\
|
23
|
+
" -H 'Authorization: #{request.headers[:'Authorization']}'"\
|
24
|
+
" #{request.url}"
|
25
|
+
|
26
|
+
`#{cmd}`
|
27
|
+
end
|
28
|
+
|
29
|
+
def send_get_request(request)
|
30
|
+
HTTParty.get(request.url, query: request.body, headers: request.headers, verify: false)
|
31
|
+
end
|
32
|
+
|
33
|
+
def send_put_request(request)
|
34
|
+
HTTParty.put(request.url, body: request.body, headers: request.headers, verify: false)
|
35
|
+
end
|
36
|
+
|
37
|
+
def send_delete_request(request)
|
38
|
+
HTTParty.delete(request.url, query: request.body, headers: request.headers, verify: false)
|
39
|
+
end
|
40
|
+
|
41
|
+
def send_head_request(request)
|
42
|
+
HTTParty.head(request.url, query: request.body, headers: request.headers, verify: false)
|
43
|
+
end
|
44
|
+
|
45
|
+
def send_patch_request(request)
|
46
|
+
HTTParty.patch(request.url, query: request.body, headers: request.headers, verify: false)
|
47
|
+
end
|
48
|
+
|
49
|
+
def send_options_request(request)
|
50
|
+
HTTParty.options(request.url, query: request.body, headers: request.headers, verify: false)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
class Response
|
2
|
+
attr_accessor :code, :headers, :body
|
3
|
+
|
4
|
+
def initialize(response)
|
5
|
+
@response = response
|
6
|
+
check_response
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def check_response
|
12
|
+
case @response
|
13
|
+
when String
|
14
|
+
begin
|
15
|
+
hash_ = JSON.parse(@response)
|
16
|
+
if hash_
|
17
|
+
@code = 200
|
18
|
+
@headers = ''
|
19
|
+
@body = convert_hash(hash_)
|
20
|
+
return
|
21
|
+
end
|
22
|
+
rescue
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if @response.nil? || (@response.respond_to?(:body) && %w[null true false].include?(@response.body))
|
27
|
+
handle_nil_response
|
28
|
+
else
|
29
|
+
handle_response
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def convert_hash(base_hash)
|
34
|
+
return base_hash unless base_hash.is_a? Hash
|
35
|
+
temp_hash = base_hash.dup
|
36
|
+
base_hash.each do |k, v|
|
37
|
+
case v
|
38
|
+
when Hash
|
39
|
+
temp_hash[k.to_sym] = convert_hash(v)
|
40
|
+
when Array
|
41
|
+
temp_hash[k.to_sym] = v.map { |v_el| convert_hash(v_el) }
|
42
|
+
else
|
43
|
+
temp_hash = temp_hash.inject({}) { |memo, (k1, v1)| memo[k1.to_sym] = v1; memo }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
temp_hash
|
47
|
+
end
|
48
|
+
|
49
|
+
def handle_nil_response
|
50
|
+
@code = @response.code
|
51
|
+
@headers = @response.headers
|
52
|
+
|
53
|
+
if @code == 204
|
54
|
+
raise 'Received non-null body in 204 No Content response' unless @response.body.nil?
|
55
|
+
@body = nil
|
56
|
+
else
|
57
|
+
case @response.body
|
58
|
+
when 'null', ''
|
59
|
+
@body = {}
|
60
|
+
when 'true'
|
61
|
+
@body = true
|
62
|
+
when 'false'
|
63
|
+
@body = false
|
64
|
+
else
|
65
|
+
raise 'Not implemented behavior for the empty response'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def handle_response
|
71
|
+
case @response
|
72
|
+
when String
|
73
|
+
@code = (@response.match /(HTTP Status|HTTP\/2) \d{3}/).to_s.split(' ')[-1].to_i
|
74
|
+
content_type = if @response.match?(/content-type: (.*);/)
|
75
|
+
@response.match(/content-type: (.*);/)[1]
|
76
|
+
else
|
77
|
+
'text/html'
|
78
|
+
end
|
79
|
+
@headers = {
|
80
|
+
"ContentType": content_type
|
81
|
+
}
|
82
|
+
@body = if @response.match?(/{.*}/)
|
83
|
+
convert_hash(JSON.parse(@response.match(/{.*}/)[0]))
|
84
|
+
else
|
85
|
+
@response
|
86
|
+
end
|
87
|
+
return
|
88
|
+
else
|
89
|
+
@code = @response.code
|
90
|
+
@headers = @response.headers
|
91
|
+
end
|
92
|
+
if @headers&.content_type == 'application/json'
|
93
|
+
@body = if @response.parsed_response.is_a? Array
|
94
|
+
@response.parsed_response.map { |i| convert_hash(i)}
|
95
|
+
elsif @response.parsed_response.is_a? Integer
|
96
|
+
@response.parsed_response
|
97
|
+
else
|
98
|
+
convert_hash(@response.parsed_response)
|
99
|
+
end
|
100
|
+
else
|
101
|
+
# parsed response is string
|
102
|
+
@body = @response.parsed_response
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
puts 'Loading Request sender component'
|
4
|
+
|
5
|
+
def recursive_require(dir)
|
6
|
+
Dir["#{File.dirname(__FILE__)}/#{dir}/**/*.rb"].each do |f|
|
7
|
+
require(f)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
dirs = Dir.entries(File.dirname(__FILE__)).select { |entry| File.directory? File.join(File.dirname(__FILE__), entry) and !(entry == '.' || entry == '..') }
|
12
|
+
dirs.each do |dir|
|
13
|
+
recursive_require(dir)
|
14
|
+
end
|
15
|
+
|
16
|
+
puts 'Done loading Request sender component'
|
data/bin/swgr2rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'swgr2rb'
|
5
|
+
|
6
|
+
module Swgr2rb
|
7
|
+
# CliOptionsParser parses arguments received from
|
8
|
+
# command line and generates parameters for endpoint generation.
|
9
|
+
class CliOptionsParser
|
10
|
+
def initialize
|
11
|
+
@params = default_params
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse(args)
|
15
|
+
option_parser = OptionParser.new do |parser|
|
16
|
+
define_options(parser)
|
17
|
+
end
|
18
|
+
parse_options(option_parser, args)
|
19
|
+
path = parse_swagger_path_from_args(args)
|
20
|
+
[path, @params]
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def default_params
|
26
|
+
{
|
27
|
+
target_dir: ScaffoldGeneratorConstants::ENDPOINT_MODELS_DIR,
|
28
|
+
component: 'component1',
|
29
|
+
update_only: false,
|
30
|
+
rewrite_schemas: true,
|
31
|
+
from_scratch: false
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def define_options(opts)
|
36
|
+
define_banner(opts)
|
37
|
+
define_target_dir_option(opts)
|
38
|
+
define_component_option(opts)
|
39
|
+
define_update_only_option(opts)
|
40
|
+
define_rewrite_schemas_option(opts)
|
41
|
+
define_from_scratch_option(opts)
|
42
|
+
define_help_option(opts)
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_options(options_parser, args)
|
46
|
+
options_parser.parse(args)
|
47
|
+
rescue OptionParser::ParseError => e
|
48
|
+
raise Swgr2rbError, e.message
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_swagger_path_from_args(args)
|
52
|
+
path = args[0]
|
53
|
+
if path.nil? || !(url?(path) || json_file_path?(path))
|
54
|
+
raise Swgr2rbError,
|
55
|
+
"Provided Swagger URL/file path '#{path}' is neither "\
|
56
|
+
'a URL nor a path of an existing JSON file'
|
57
|
+
end
|
58
|
+
path.to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
def define_banner(opts)
|
62
|
+
opts.banner = "Usage:\tswgr2rb SWAGGER_URL|FILE_PATH [OPTIONS]"
|
63
|
+
opts.separator("\nTo generate a new testing framework from scratch:\n\t"\
|
64
|
+
'swgr2rb SWAGGER_URL|FILE_PATH --from-scratch'\
|
65
|
+
" [-c COMPONENT]\n\n"\
|
66
|
+
"To update an existing testing framework:\n\t"\
|
67
|
+
'swgr2rb SWAGGER_URL|FILE_PATH [-t TARGET_DIR]'\
|
68
|
+
"[-c COMPONENT]\n\t"\
|
69
|
+
' [--[no-]update-only] [--[no-]rewrite-schemas]')
|
70
|
+
opts.separator('')
|
71
|
+
opts.separator('Options:')
|
72
|
+
end
|
73
|
+
|
74
|
+
def define_target_dir_option(opts)
|
75
|
+
opts.on('-t TARGET_DIR', '--target-dir TARGET_DIR', String,
|
76
|
+
'Target directory for endpoint object models',
|
77
|
+
"(the directory that contains components' folders).",
|
78
|
+
"Default: #{@params[:target_dir]}.") do |dir|
|
79
|
+
@params[:target_dir] = dir
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def define_component_option(opts)
|
84
|
+
opts.on('-c COMPONENT', '--component COMPONENT', String,
|
85
|
+
'Component name for endpoint classes. For a new',
|
86
|
+
'project, a directory named like this will be created',
|
87
|
+
'inside the target directory, and all the generated',
|
88
|
+
'endpoint object models will be located inside.',
|
89
|
+
"Default: #{@params[:component]}.") do |component|
|
90
|
+
@params[:component] = component
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def define_update_only_option(opts)
|
95
|
+
opts.on('--[no-]update-only', TrueClass,
|
96
|
+
'Do not create new files, only update existing. This',
|
97
|
+
'option is useful when there are new (previously',
|
98
|
+
'untested) endpoints in Swagger. '\
|
99
|
+
"Default: #{@params[:update_only]}.") do |update_only|
|
100
|
+
@params[:update_only] = update_only
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def define_rewrite_schemas_option(opts)
|
105
|
+
opts.on('--[no-]rewrite-schemas', TrueClass,
|
106
|
+
'Rewrite schema modules (located in',
|
107
|
+
'TARGET_DIR/COMPONENT/object_model_schemas)',
|
108
|
+
'if they already exist. '\
|
109
|
+
"Default: #{@params[:rewrite_schemas]}.") do |rewrite_schemas|
|
110
|
+
@params[:rewrite_schemas] = rewrite_schemas
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def define_from_scratch_option(opts)
|
115
|
+
opts.on('--from-scratch', TrueClass,
|
116
|
+
'Generate new testing framework. Will create',
|
117
|
+
"a directory named 'harness' and generate the scaffold",
|
118
|
+
'of the framework inside. '\
|
119
|
+
"Default: #{@params[:from_scratch]}.") do |from_scratch|
|
120
|
+
@params[:from_scratch] = from_scratch
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def define_help_option(opts)
|
125
|
+
opts.on('-h', '--help', 'Prints this help') do
|
126
|
+
puts opts
|
127
|
+
exit
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def url?(path)
|
132
|
+
URI.extract(path).present?
|
133
|
+
end
|
134
|
+
|
135
|
+
def json_file_path?(path)
|
136
|
+
path.end_with?('.json') && File.exist?(path)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|