response_mate 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +15 -0
- data/.travis.yml +10 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +107 -0
- data/LICENSE +20 -0
- data/README.md +107 -0
- data/Rakefile +12 -0
- data/bin/response_mate +5 -0
- data/environment.yml.sample +4 -0
- data/lib/response_mate.rb +43 -0
- data/lib/response_mate/cli.rb +58 -0
- data/lib/response_mate/commands.rb +1 -0
- data/lib/response_mate/commands/base.rb +12 -0
- data/lib/response_mate/commands/clear.rb +24 -0
- data/lib/response_mate/commands/export.rb +29 -0
- data/lib/response_mate/commands/inspect.rb +42 -0
- data/lib/response_mate/commands/list.rb +48 -0
- data/lib/response_mate/commands/record.rb +27 -0
- data/lib/response_mate/commands/setup.rb +24 -0
- data/lib/response_mate/connection.rb +36 -0
- data/lib/response_mate/core.rb +29 -0
- data/lib/response_mate/environment.rb +22 -0
- data/lib/response_mate/exporter.rb +18 -0
- data/lib/response_mate/exporters/postman.rb +61 -0
- data/lib/response_mate/helpers/application.rb +3 -0
- data/lib/response_mate/http.rb +41 -0
- data/lib/response_mate/inspector.rb +54 -0
- data/lib/response_mate/manifest.rb +68 -0
- data/lib/response_mate/manifest_parser.rb +7 -0
- data/lib/response_mate/oauth.rb +17 -0
- data/lib/response_mate/recorder.rb +39 -0
- data/lib/response_mate/request.rb +2 -0
- data/lib/response_mate/tape.rb +18 -0
- data/lib/response_mate/version.rb +3 -0
- data/oauth.yml.sample +12 -0
- data/requests.yml.sample +16 -0
- data/response_mate.gemspec +41 -0
- data/roadmap.md +24 -0
- data/spec/fixtures/two_keys.yml.erb +8 -0
- data/spec/lib/response_mate/cli_spec.rb +108 -0
- data/spec/lib/response_mate/core_spec.rb +65 -0
- data/spec/lib/response_mate/recorder_spec.rb +103 -0
- data/spec/lib/response_mate_spec.rb +7 -0
- data/spec/spec_helper.rb +32 -0
- metadata +336 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
module ResponseMate
|
2
|
+
module Commands
|
3
|
+
class ResponseMate::Commands::Export < Base
|
4
|
+
|
5
|
+
def initialize(args, options)
|
6
|
+
super(args, options)
|
7
|
+
@type = args.first || 'requests'
|
8
|
+
|
9
|
+
@options[:manifest] = ResponseMate::Manifest.new(options[:requests_manifest])
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
unless options[:format].present?
|
14
|
+
@options[:format] = choose { |menu|
|
15
|
+
menu.prompt = 'Please pick one of the available formats'
|
16
|
+
menu.choice(:postman)
|
17
|
+
}.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
output = ResponseMate::Exporter.new(options).export
|
21
|
+
if options[:pretty]
|
22
|
+
puts JSON.pretty_generate(output)
|
23
|
+
else
|
24
|
+
puts output.to_json
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ResponseMate
|
2
|
+
module Commands
|
3
|
+
class ResponseMate::Commands::Inspect < Base
|
4
|
+
attr_reader :inspector
|
5
|
+
attr_accessor :history
|
6
|
+
|
7
|
+
def initialize(args, options)
|
8
|
+
super(args, options)
|
9
|
+
|
10
|
+
@options[:manifest] = ResponseMate::Manifest.new(options[:requests_manifest])
|
11
|
+
@inspector = ResponseMate::Inspector.new(@options)
|
12
|
+
@history = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
if options[:interactive]
|
17
|
+
interactive
|
18
|
+
else
|
19
|
+
args.each do |key|
|
20
|
+
inspector.inspect_key(key)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def interactive
|
26
|
+
loop do
|
27
|
+
input = ask('response_mate> ')
|
28
|
+
case input
|
29
|
+
when 'history'
|
30
|
+
input = choose { |menu|
|
31
|
+
menu.prompt = 'Replay any?'
|
32
|
+
menu.choices(*([:no] + history))
|
33
|
+
}.to_s
|
34
|
+
else
|
35
|
+
history << input
|
36
|
+
end
|
37
|
+
inspector.inspect_interactive(input)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module ResponseMate
|
2
|
+
module Commands
|
3
|
+
class ResponseMate::Commands::List < Base
|
4
|
+
attr_accessor :type
|
5
|
+
|
6
|
+
def initialize(args, options)
|
7
|
+
super(args, options)
|
8
|
+
@type = args.first || 'requests'
|
9
|
+
|
10
|
+
@options[:manifest] = ResponseMate::Manifest.new(options[:requests_manifest])
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
if type == 'requests'
|
15
|
+
choices = options[:manifest].requests.map { |r| r.key.to_sym }
|
16
|
+
elsif type == 'recordings'
|
17
|
+
choices = Dir.glob('output/responses/*.yml').map do |f|
|
18
|
+
File.basename(f).gsub(/\.yml/, '').to_sym
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
puts choices.join "\n"
|
23
|
+
puts "\n\n"
|
24
|
+
action = choose { |menu|
|
25
|
+
menu.prompt = 'Want to perform any of the actions above?'
|
26
|
+
menu.choices(:record, :inspect, :no)
|
27
|
+
}
|
28
|
+
|
29
|
+
unless action == :no
|
30
|
+
key = choose { |menu|
|
31
|
+
menu.prompt = 'Which one?'
|
32
|
+
menu.choices(*choices)
|
33
|
+
}.to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
if key
|
37
|
+
case action
|
38
|
+
when :record
|
39
|
+
ResponseMate::Recorder.new({ manifest: options[:manifest], keys: [key] }).
|
40
|
+
record
|
41
|
+
when :inspect
|
42
|
+
ResponseMate::Inspector.new(manifest: options[:manifest]).inspect_key(key)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ResponseMate
|
2
|
+
module Commands
|
3
|
+
# Handles the invocation of the record command
|
4
|
+
class ResponseMate::Commands::Record < Base
|
5
|
+
def initialize(args, options)
|
6
|
+
super(args, options)
|
7
|
+
@options = options.dup
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
environment = ResponseMate::Environment.new(options[:environment])
|
12
|
+
|
13
|
+
manifest = ResponseMate::Manifest.
|
14
|
+
new(options[:requests_manifest], environment)
|
15
|
+
|
16
|
+
options[:manifest] = manifest
|
17
|
+
recorder = ResponseMate::Recorder.new(options)
|
18
|
+
|
19
|
+
recorder.record
|
20
|
+
|
21
|
+
File.open(ResponseMate.configuration.output_dir + '.last_recording', 'w') do |f|
|
22
|
+
f << Time.current
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ResponseMate
|
2
|
+
module Commands
|
3
|
+
# Handles the invocation of the setup command
|
4
|
+
class ResponseMate::Commands::Setup < Base
|
5
|
+
attr_accessor :output_dir
|
6
|
+
|
7
|
+
def initialize(args, options)
|
8
|
+
super(args, options)
|
9
|
+
@options = options.dup
|
10
|
+
|
11
|
+
@output_dir = if args.present?
|
12
|
+
args.first
|
13
|
+
else
|
14
|
+
ResponseMate.configuration.output_dir
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
FileUtils.mkdir_p(output_dir)
|
20
|
+
puts "[Setup] Initialized empty directory #{output_dir}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class ResponseMate::Connection
|
2
|
+
delegate :params, to: :client
|
3
|
+
delegate(*(ResponseMate::ManifestParser::HTTP_VERBS), to: :client)
|
4
|
+
|
5
|
+
attr_accessor :client, :base_url
|
6
|
+
|
7
|
+
def initialize(base_url = nil)
|
8
|
+
@base_url = base_url
|
9
|
+
@client = Faraday.new do |c|
|
10
|
+
c.use FaradayMiddleware::FollowRedirects, limit: 5
|
11
|
+
c.adapter Faraday.default_adapter
|
12
|
+
end
|
13
|
+
|
14
|
+
client.headers.merge(ResponseMate::DEFAULT_HEADERS)
|
15
|
+
client.url_prefix = base_url if base_url
|
16
|
+
end
|
17
|
+
|
18
|
+
def fetch(request)
|
19
|
+
client.params = request[:params] if !request[:params].nil?
|
20
|
+
unless base_url || request[:path] =~ /^http:\/\//
|
21
|
+
request[:path] = 'http://' + request[:path]
|
22
|
+
end
|
23
|
+
client.send request[:verb].downcase.to_sym, "#{base_url}#{request[:path]}"
|
24
|
+
rescue Faraday::Error::ConnectionFailed
|
25
|
+
puts "Is a server up and running at #{request[:path]}?".red
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_headers_from_manifest(manifest)
|
30
|
+
if manifest.try(:[], 'default_headers')
|
31
|
+
manifest['default_headers'].each_pair do |k, v|
|
32
|
+
client.headers[ResponseMate::Helpers.headerize(k)] = v
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# The main module of the tool contains configuration code and some constats
|
3
|
+
module ResponseMate
|
4
|
+
class OutputDirError < StandardError; end
|
5
|
+
|
6
|
+
DEFAULT_HEADERS = {
|
7
|
+
'User-Agent' => 'Response-Mate'
|
8
|
+
}
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :configuration
|
12
|
+
end
|
13
|
+
|
14
|
+
class Configuration
|
15
|
+
attr_accessor :output_dir, :requests_manifest, :oauth_manifest, :environment
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@output_dir = './output/responses/'
|
19
|
+
@requests_manifest = './requests.yml'
|
20
|
+
@oauth_manifest = './oauth.yml'
|
21
|
+
@environment = './environment.yml'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.setup
|
26
|
+
self.configuration ||= Configuration.new
|
27
|
+
yield(configuration) if block_given?
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class ResponseMate::Environment
|
2
|
+
attr_accessor :filename, :env, :environment_text
|
3
|
+
|
4
|
+
delegate :[], to: :env
|
5
|
+
|
6
|
+
def initialize(filename)
|
7
|
+
@filename = filename || ResponseMate.configuration.environment
|
8
|
+
@env = {}
|
9
|
+
parse
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse
|
13
|
+
begin
|
14
|
+
@environment_text = File.read filename
|
15
|
+
rescue Errno::ENOENT
|
16
|
+
puts filename.red << ' does not seem to exist'
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
@env = YAML.load(environment_text)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module ResponseMate
|
4
|
+
class Exporter
|
5
|
+
attr_accessor :format, :handler, :manifest
|
6
|
+
|
7
|
+
def initialize(args = {})
|
8
|
+
@format = args[:format]
|
9
|
+
@manifest = args[:manifest]
|
10
|
+
end
|
11
|
+
|
12
|
+
def export
|
13
|
+
@handler = "ResponseMate::Exporters::#{format.capitalize}".safe_constantize.
|
14
|
+
new manifest
|
15
|
+
handler.export
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ResponseMate::Exporters
|
2
|
+
# Handles exporting to postman format
|
3
|
+
# Example output
|
4
|
+
# https://www.getpostman.com/collections/dbc0521911e45471ff4a
|
5
|
+
class Postman
|
6
|
+
include ResponseMate::ManifestParser
|
7
|
+
|
8
|
+
attr_accessor :manifest, :out
|
9
|
+
|
10
|
+
def initialize(manifest)
|
11
|
+
@manifest = manifest
|
12
|
+
@out = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def export
|
16
|
+
build_structure
|
17
|
+
build_requests
|
18
|
+
out
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def build_structure
|
24
|
+
out.merge!(
|
25
|
+
id: SecureRandom.uuid,
|
26
|
+
name: 'latest_export',
|
27
|
+
requests: [],
|
28
|
+
order: [],
|
29
|
+
timestamp: Time.now.to_i
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_requests
|
34
|
+
manifest.requests.each do |request|
|
35
|
+
req = ResponseMate::Manifest.parse_request(request.request)
|
36
|
+
url = if req['params'].present?
|
37
|
+
"#{req['path']}?#{req['params'].to_query}"
|
38
|
+
else
|
39
|
+
req['path']
|
40
|
+
end
|
41
|
+
|
42
|
+
out_req = {
|
43
|
+
id: SecureRandom.uuid,
|
44
|
+
collectionId: out[:id],
|
45
|
+
data: [],
|
46
|
+
description: '',
|
47
|
+
method: req['verb'],
|
48
|
+
name: request['key'],
|
49
|
+
url: url,
|
50
|
+
version: 2,
|
51
|
+
responses: [],
|
52
|
+
dataMode: 'params',
|
53
|
+
headers: request['headers'] || manifest.default_headers
|
54
|
+
}
|
55
|
+
|
56
|
+
out[:order] << out_req[:id]
|
57
|
+
out[:requests] << out_req
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ResponseMate::Http
|
2
|
+
STATUS_CODES = {
|
3
|
+
100 => 'Continue',
|
4
|
+
101 => 'Switching Protocols',
|
5
|
+
200 => 'OK',
|
6
|
+
201 => 'Created',
|
7
|
+
202 => 'Accepted',
|
8
|
+
203 => 'Non-Authoritative Information',
|
9
|
+
204 => 'No Content',
|
10
|
+
205 => 'Reset Content',
|
11
|
+
206 => 'Partial Content',
|
12
|
+
300 => 'Multiple Choices',
|
13
|
+
301 => 'Moved Permanently',
|
14
|
+
302 => 'Moved Temporarily',
|
15
|
+
303 => 'See Other',
|
16
|
+
304 => 'Not Modified',
|
17
|
+
305 => 'Use Proxy',
|
18
|
+
400 => 'Bad Request',
|
19
|
+
401 => 'Unauthorized',
|
20
|
+
402 => 'Payment Required',
|
21
|
+
403 => 'Forbidden',
|
22
|
+
404 => 'Not Found',
|
23
|
+
405 => 'Method Not Allowed',
|
24
|
+
406 => 'Not Acceptable',
|
25
|
+
407 => 'Proxy Authentication Required',
|
26
|
+
408 => 'Request Time-out',
|
27
|
+
409 => 'Conflict',
|
28
|
+
410 => 'Gone',
|
29
|
+
411 => 'Length Required',
|
30
|
+
412 => 'Precondition Failed',
|
31
|
+
413 => 'Request Entity Too Large',
|
32
|
+
414 => 'Request-URI Too Large',
|
33
|
+
415 => 'Unsupported Media Type',
|
34
|
+
500 => 'Internal Server Error',
|
35
|
+
501 => 'Not Implemented',
|
36
|
+
502 => 'Bad Gateway',
|
37
|
+
503 => 'Service Unavailable',
|
38
|
+
504 => 'Gateway Time-out',
|
39
|
+
505 => 'HTTP Version not supported'
|
40
|
+
}
|
41
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class ResponseMate::Inspector
|
2
|
+
attr_accessor :conn, :base_url, :oauth, :manifest, :print_type
|
3
|
+
|
4
|
+
def initialize(args = {})
|
5
|
+
@manifest = args[:manifest]
|
6
|
+
@oauth = ResponseMate::Oauth.new
|
7
|
+
@base_url = args[:base_url] || manifest.base_url
|
8
|
+
@print_type = args[:print] || 'raw'
|
9
|
+
|
10
|
+
if !args[:interactive]
|
11
|
+
@conn = ResponseMate::Connection.new(base_url)
|
12
|
+
@conn.set_headers_from_manifest(manifest)
|
13
|
+
else
|
14
|
+
@conn = ResponseMate::Connection.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect_key(key, options = {})
|
19
|
+
request = manifest.requests.find { |r| r.key == key }
|
20
|
+
request = ResponseMate::Manifest.parse_request(request.request)
|
21
|
+
|
22
|
+
puts "[#{key}] #{request[:verb]}".cyan_on_black.bold << " #{request[:path]}"
|
23
|
+
puts "\tparams #{request[:params]}" if request[:params].present?
|
24
|
+
|
25
|
+
print(conn.fetch(request))
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect_interactive(input)
|
29
|
+
request = ResponseMate::Manifest.parse_request(input)
|
30
|
+
print(conn.fetch(request))
|
31
|
+
end
|
32
|
+
|
33
|
+
def print(response)
|
34
|
+
__send__ "print_#{print_type}", response
|
35
|
+
end
|
36
|
+
|
37
|
+
def print_raw(response)
|
38
|
+
puts "\n"
|
39
|
+
status = "#{response.status} #{ResponseMate::Http::STATUS_CODES[response.status]}"
|
40
|
+
puts "HTTP/1.1 #{status}"
|
41
|
+
response.headers.each_pair do |k, v|
|
42
|
+
puts "#{ResponseMate::Helpers.headerize(k)}: #{v}"
|
43
|
+
end
|
44
|
+
puts response.body
|
45
|
+
end
|
46
|
+
|
47
|
+
def print_pretty(response)
|
48
|
+
ap(
|
49
|
+
status: response.status,
|
50
|
+
headers: response.headers,
|
51
|
+
body: response.body
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|