swgr2rb 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|