response_mate 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -0
- data/CHANGELOG.md +41 -0
- data/CONTRIBUTING.md +42 -0
- data/Gemfile +1 -2
- data/Guardfile +4 -4
- data/README.md +126 -37
- data/Rakefile +1 -1
- data/bin/response_mate +18 -1
- data/lib/response_mate.rb +1 -1
- data/lib/response_mate/cli.rb +8 -4
- data/lib/response_mate/commands/inspect.rb +5 -6
- data/lib/response_mate/commands/list.rb +10 -7
- data/lib/response_mate/commands/record.rb +2 -3
- data/lib/response_mate/connection.rb +2 -4
- data/lib/response_mate/core.rb +3 -2
- data/lib/response_mate/environment.rb +10 -10
- data/lib/response_mate/exporter.rb +3 -2
- data/lib/response_mate/exporters/postman/environment.rb +1 -1
- data/lib/response_mate/manifest.rb +58 -19
- data/lib/response_mate/request.rb +7 -5
- data/lib/response_mate/tape.rb +11 -10
- data/lib/response_mate/version.rb +1 -1
- data/response_mate.gemspec +1 -1
- data/spec/lib/response_mate/commands/list_spec.rb +44 -1
- data/spec/lib/response_mate/commands/record_spec.rb +13 -14
- data/spec/lib/response_mate/core_spec.rb +7 -10
- data/spec/lib/response_mate/environment_spec.rb +67 -0
- data/spec/lib/response_mate/manifest_spec.rb +175 -0
- data/spec/lib/response_mate/recorder_spec.rb +1 -1
- data/spec/spec_helper.rb +34 -1
- metadata +13 -8
- data/roadmap.md +0 -24
@@ -2,15 +2,14 @@ class ResponseMate::Commands::Inspect < ResponseMate::Commands::Base
|
|
2
2
|
attr_reader :inspector
|
3
3
|
attr_accessor :history
|
4
4
|
|
5
|
-
def
|
6
|
-
|
5
|
+
def run # rubocop:disable Metrics/AbcSize
|
6
|
+
environment = ResponseMate::Environment.new(options[:environment])
|
7
|
+
options[:manifest] = ResponseMate::Manifest.new(options[:requests_manifest],
|
8
|
+
environment)
|
7
9
|
|
8
|
-
@
|
9
|
-
@inspector = ResponseMate::Inspector.new(@options)
|
10
|
+
@inspector = ResponseMate::Inspector.new(options)
|
10
11
|
@history = []
|
11
|
-
end
|
12
12
|
|
13
|
-
def run
|
14
13
|
if args.empty?
|
15
14
|
return $stderr.puts 'At least one key has to be specified'.red
|
16
15
|
end
|
@@ -1,15 +1,14 @@
|
|
1
1
|
# Command which performs the operations required by `response_mate list`
|
2
2
|
class ResponseMate::Commands::List < ResponseMate::Commands::Base
|
3
3
|
# Run the command based on args, options provided
|
4
|
-
def run
|
4
|
+
def run # rubocop:disable Metrics/AbcSize
|
5
5
|
environment = ResponseMate::Environment.new(options[:environment])
|
6
|
-
|
7
|
-
|
8
|
-
available_keys = @manifest.requests.map { |r| r.key.to_sym }
|
6
|
+
options[:manifest] = ResponseMate::Manifest.new(options[:requests_manifest],
|
7
|
+
environment)
|
9
8
|
|
10
9
|
puts available_keys.join("\n") << "\n\n"
|
11
|
-
|
12
10
|
action = ask_action
|
11
|
+
|
13
12
|
return if action == :no
|
14
13
|
|
15
14
|
perform_action(action, ask_key(available_keys))
|
@@ -17,6 +16,10 @@ class ResponseMate::Commands::List < ResponseMate::Commands::Base
|
|
17
16
|
|
18
17
|
private
|
19
18
|
|
19
|
+
def available_keys
|
20
|
+
options[:manifest].requests.map { |r| r.key.to_sym }
|
21
|
+
end
|
22
|
+
|
20
23
|
def ask_action
|
21
24
|
choose do |menu|
|
22
25
|
menu.prompt = 'Want to perform any of the actions above?'
|
@@ -34,9 +37,9 @@ class ResponseMate::Commands::List < ResponseMate::Commands::Base
|
|
34
37
|
def perform_action(action, key)
|
35
38
|
case action
|
36
39
|
when :record
|
37
|
-
ResponseMate::Recorder.new(
|
40
|
+
ResponseMate::Recorder.new(options).record([key])
|
38
41
|
when :inspect
|
39
|
-
ResponseMate::Inspector.new(
|
42
|
+
ResponseMate::Inspector.new(options).inspect_key(key)
|
40
43
|
end
|
41
44
|
end
|
42
45
|
end
|
@@ -3,9 +3,8 @@ class ResponseMate::Commands::Record < ResponseMate::Commands::Base
|
|
3
3
|
# Run the command based on args, options provided
|
4
4
|
def run
|
5
5
|
environment = ResponseMate::Environment.new(options[:environment])
|
6
|
-
manifest = ResponseMate::Manifest.new(options[:requests_manifest],
|
7
|
-
|
8
|
-
options[:manifest] = manifest
|
6
|
+
options[:manifest] = ResponseMate::Manifest.new(options[:requests_manifest],
|
7
|
+
environment)
|
9
8
|
|
10
9
|
recorder = ResponseMate::Recorder.new(options)
|
11
10
|
|
@@ -14,7 +14,7 @@ class ResponseMate::Connection
|
|
14
14
|
|
15
15
|
# Performs the supplied request
|
16
16
|
# @param {ResponseMate::Request} The request to be performed
|
17
|
-
def fetch(request)
|
17
|
+
def fetch(request) # rubocop:disable Metrics/AbcSize
|
18
18
|
uri = URI.parse(request[:url])
|
19
19
|
|
20
20
|
if request[:params]
|
@@ -23,10 +23,8 @@ class ResponseMate::Connection
|
|
23
23
|
uri.query = query
|
24
24
|
end
|
25
25
|
|
26
|
-
verb = request[:verb] || 'GET'
|
27
|
-
|
28
26
|
client.headers = request[:headers] if request[:headers]
|
29
|
-
client.send verb.downcase.to_sym, uri.to_s
|
27
|
+
client.send request[:verb].downcase.to_sym, uri.to_s
|
30
28
|
rescue Faraday::Error::ConnectionFailed
|
31
29
|
puts "Is a server up and running at #{request[:path]}?".red
|
32
30
|
exit 1
|
data/lib/response_mate/core.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
# The main module of the tool contains configuration code and some constants
|
2
2
|
module ResponseMate
|
3
3
|
class OutputDirError < StandardError; end
|
4
|
+
class ManifestMissing < StandardError; end
|
4
5
|
class KeysNotFound < StandardError; end
|
5
6
|
|
6
7
|
# Methods handled by response_mate
|
7
|
-
HTTP_METHODS =
|
8
|
+
HTTP_METHODS = Faraday::Connection::METHODS
|
8
9
|
|
9
10
|
# Responsible for keeping initialization configuration values
|
10
11
|
class Configuration
|
11
12
|
attr_accessor :output_dir, :requests_manifest, :environment
|
12
13
|
|
13
14
|
def initialize
|
14
|
-
@output_dir = './
|
15
|
+
@output_dir = './'
|
15
16
|
@requests_manifest = './requests.yml'
|
16
17
|
@environment = './environment.yml'
|
17
18
|
end
|
@@ -3,26 +3,26 @@
|
|
3
3
|
# is a Hash which will be used for the rendering of the requests manifest as a
|
4
4
|
# [Mustace template](http://mustache.github.io/mustache.5.html).
|
5
5
|
class ResponseMate::Environment
|
6
|
-
attr_accessor :filename, :env
|
6
|
+
attr_accessor :filename, :env
|
7
7
|
|
8
8
|
delegate :[], to: :env
|
9
9
|
|
10
10
|
def initialize(filename)
|
11
11
|
@filename = filename || ResponseMate.configuration.environment
|
12
|
-
@env = {}
|
13
12
|
parse
|
14
13
|
end
|
15
14
|
|
15
|
+
# Check for environment file existence
|
16
|
+
# @return [TrueClass|FalseClass]
|
17
|
+
def exists?
|
18
|
+
File.exist? filename
|
19
|
+
end
|
20
|
+
|
16
21
|
private
|
17
22
|
|
23
|
+
# Set the env to the parsed YAML environment file
|
18
24
|
def parse
|
19
|
-
|
20
|
-
|
21
|
-
rescue Errno::ENOENT
|
22
|
-
puts filename.red << ' does not seem to exist'
|
23
|
-
exit 1
|
24
|
-
end
|
25
|
-
|
26
|
-
@env = ::YAML.load(environment_text)
|
25
|
+
return @env = {} unless exists?
|
26
|
+
@env = ::YAML.load_file(filename)
|
27
27
|
end
|
28
28
|
end
|
@@ -13,8 +13,9 @@ module ResponseMate
|
|
13
13
|
|
14
14
|
# Returns the compatible transformed resource
|
15
15
|
def export
|
16
|
-
@handler = "ResponseMate::Exporters::#{format.capitalize}".safe_constantize.
|
17
|
-
|
16
|
+
@handler = "ResponseMate::Exporters::#{format.capitalize}".safe_constantize.new manifest,
|
17
|
+
environment,
|
18
|
+
resource
|
18
19
|
handler.export
|
19
20
|
end
|
20
21
|
end
|
@@ -10,29 +10,17 @@ class ResponseMate::Manifest
|
|
10
10
|
parse
|
11
11
|
end
|
12
12
|
|
13
|
-
# Parse the requests manifest
|
14
|
-
# @return [Array] of requests
|
15
|
-
def parse
|
16
|
-
preprocess_manifest
|
17
|
-
@request_hashes = YAML.load(requests_text).deep_symbolize_keys
|
18
|
-
@name = @request_hashes[:name] || filename
|
19
|
-
@description = @request_hashes[:description] || ''
|
20
|
-
@requests = @request_hashes[:requests].
|
21
|
-
map(&:deep_symbolize_keys!).
|
22
|
-
map { |rh| ResponseMate::Request.new(rh).normalize! }
|
23
|
-
end
|
24
|
-
|
25
13
|
# Filters requests based on the supplied Array of keys
|
26
14
|
# @param [Array] keys The keys to lookup for matching requests
|
27
15
|
# @return [Array] of matching requests
|
28
16
|
def requests_for_keys(keys)
|
29
|
-
return [] if keys.
|
17
|
+
return [] if keys.blank?
|
30
18
|
|
31
19
|
existing_keys = requests.map(&:key)
|
32
20
|
missing_keys = keys - existing_keys
|
33
21
|
|
34
22
|
if missing_keys.present?
|
35
|
-
fail ResponseMate::KeysNotFound.new(missing_keys.join(','))
|
23
|
+
fail ResponseMate::KeysNotFound.new(missing_keys.join(', '))
|
36
24
|
end
|
37
25
|
|
38
26
|
requests.select! do |r|
|
@@ -42,18 +30,69 @@ class ResponseMate::Manifest
|
|
42
30
|
|
43
31
|
private
|
44
32
|
|
33
|
+
# Parse the requests manifest
|
34
|
+
# @return [Array] of requests
|
35
|
+
def parse
|
36
|
+
preprocess_manifest
|
37
|
+
parsed_manifest = YAML.load(@requests_text).deep_symbolize_keys
|
38
|
+
@name = parsed_manifest.fetch(:name, filename)
|
39
|
+
@description = parsed_manifest.fetch(:description, '')
|
40
|
+
check_requests(parsed_manifest)
|
41
|
+
@requests = parsed_manifest.
|
42
|
+
fetch(:requests, []). # rubocop:disable Style/MultilineOperationIndentation
|
43
|
+
map(&:deep_symbolize_keys!). # rubocop:disable Style/MultilineOperationIndentation
|
44
|
+
map { |rh| ResponseMate::Request.new(rh).normalize! } # rubocop:disable Style/MultilineOperationIndentation
|
45
|
+
end
|
46
|
+
|
45
47
|
# Parse the manifest file as a template
|
46
48
|
# @return [String] The manifest text parsed as a template
|
47
49
|
def preprocess_manifest
|
48
50
|
begin
|
49
51
|
@requests_text = File.read filename
|
50
52
|
rescue Errno::ENOENT
|
51
|
-
|
52
|
-
exit 1
|
53
|
+
raise ResponseMate::ManifestMissing.new(filename)
|
53
54
|
end
|
54
55
|
|
55
|
-
if environment.present?
|
56
|
-
|
57
|
-
|
56
|
+
process_mustache! if environment.present? && mustache?
|
57
|
+
end
|
58
|
+
|
59
|
+
# Check if the manifest file is a Mustache template
|
60
|
+
def mustache?
|
61
|
+
requests_text =~ /{{.*}}/
|
62
|
+
end
|
63
|
+
|
64
|
+
# Evaluate Mustache template
|
65
|
+
def process_mustache!
|
66
|
+
check_environment
|
67
|
+
|
68
|
+
@requests_text = Mustache.render(requests_text, environment.env)
|
69
|
+
end
|
70
|
+
|
71
|
+
def check_environment
|
72
|
+
return if environment.exists?
|
73
|
+
|
74
|
+
warning = 'The specified requests file is a template, but no environment file is found.'
|
75
|
+
guide = <<-GUIDE
|
76
|
+
The environment file holds any variables you wish to be interpolated.
|
77
|
+
You may specify a different environment file using the -e [file.yml] option.
|
78
|
+
Default: environment.yml
|
79
|
+
GUIDE
|
80
|
+
|
81
|
+
STDERR.puts warning.yellow, guide
|
82
|
+
end
|
83
|
+
|
84
|
+
def check_requests(parsed_manifest)
|
85
|
+
return if parsed_manifest.key? :requests
|
86
|
+
|
87
|
+
gemspec = Gem::Specification.find_by_name('response_mate')
|
88
|
+
example_manifest = "#{gemspec.homepage}/blob/master/requests.yml.sample"
|
89
|
+
|
90
|
+
warning = 'The specified requests file contains no requests.'
|
91
|
+
guide = <<-GUIDE
|
92
|
+
To declare requests place them under the :requests key.
|
93
|
+
Example manifest: #{example_manifest}
|
94
|
+
GUIDE
|
95
|
+
|
96
|
+
STDERR.puts warning.yellow, guide
|
58
97
|
end
|
59
98
|
end
|
@@ -5,9 +5,10 @@ class ResponseMate::Request < OpenStruct
|
|
5
5
|
|
6
6
|
# Make sure all defined requests in the manifest have complete
|
7
7
|
# information for {ResponseMate::Connection#fetch}
|
8
|
-
def normalize!
|
9
|
-
|
10
|
-
request
|
8
|
+
def normalize! # rubocop:disable Metrics/AbcSize
|
9
|
+
request[:verb] = begin
|
10
|
+
(ResponseMate::HTTP_METHODS & [request.fetch(:verb, 'GET').downcase.to_sym]).first ||
|
11
|
+
'GET'
|
11
12
|
end
|
12
13
|
|
13
14
|
if request[:url] !~ /{{.*?}}/ # Avoid encoding unprocessed mustache tags
|
@@ -18,8 +19,9 @@ class ResponseMate::Request < OpenStruct
|
|
18
19
|
end
|
19
20
|
|
20
21
|
# @return [String] Output string suitable for a terminal
|
21
|
-
def to_cli_format
|
22
|
-
out = "[#{key}]
|
22
|
+
def to_cli_format # rubocop:disable Metrics/AbcSize
|
23
|
+
out = ["[#{key.yellow}] ", request[:verb].to_s.upcase.colorize(:cyan).on_black.bold,
|
24
|
+
" #{request[:url]}"].join
|
23
25
|
out << "\tparams #{request[:params]}" if request[:params].present?
|
24
26
|
out
|
25
27
|
end
|
data/lib/response_mate/tape.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Responsible for the recorded responses as files
|
2
2
|
class ResponseMate::Tape
|
3
3
|
# Writes the tape as a file
|
4
|
-
def write(key, request, response, meta = {}, output_dir = nil)
|
4
|
+
def write(key, request, response, meta = {}, output_dir = nil) # rubocop:disable Metrics/AbcSize
|
5
5
|
output_dir ||= ResponseMate.configuration.output_dir
|
6
6
|
|
7
7
|
output_path = File.join output_dir, "#{key}.yml"
|
@@ -22,21 +22,22 @@ class ResponseMate::Tape
|
|
22
22
|
f << file_content.to_yaml
|
23
23
|
end
|
24
24
|
rescue Errno::ENOENT
|
25
|
-
raise ResponseMate::OutputDirError
|
25
|
+
raise ResponseMate::OutputDirError.new(output_dir)
|
26
26
|
end
|
27
27
|
|
28
28
|
private
|
29
29
|
|
30
30
|
def _utf8_encode(object)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
31
|
+
if object.respond_to? :force_encoding
|
32
|
+
object.force_encoding('UTF-8').encode!
|
33
|
+
elsif object.is_a? Hash
|
34
|
+
object.reduce({}) do |h, (k, v)|
|
35
|
+
key = k.dup unless k.is_a? Symbol
|
36
|
+
|
37
|
+
h[_utf8_encode(key)] = _utf8_encode(v)
|
38
|
+
h
|
38
39
|
end
|
39
|
-
|
40
|
+
elsif object.respond_to? :each
|
40
41
|
object.each { |v| _utf8_encode(v) }
|
41
42
|
end
|
42
43
|
end
|
data/response_mate.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_dependency 'thor', '~> 0.19'
|
27
27
|
spec.add_dependency 'awesome_print'
|
28
28
|
spec.add_dependency 'activesupport'
|
29
|
-
spec.add_dependency '
|
29
|
+
spec.add_dependency 'colorize', '> 0.7.5'
|
30
30
|
spec.add_dependency 'faraday'
|
31
31
|
spec.add_dependency 'faraday_middleware'
|
32
32
|
spec.add_dependency 'addressable'
|
@@ -3,9 +3,11 @@ require 'spec_helper'
|
|
3
3
|
describe ResponseMate::Commands::List do
|
4
4
|
include_context 'stubbed_requests'
|
5
5
|
|
6
|
-
|
6
|
+
let(:options) { {} }
|
7
7
|
let(:cmd_output) { capture(:stdout) { subject.run } }
|
8
8
|
|
9
|
+
subject(:command) { ResponseMate::Commands::List.new([], options) }
|
10
|
+
|
9
11
|
describe '#run' do
|
10
12
|
before { subject.stub(:ask_action).and_return(:no) }
|
11
13
|
|
@@ -23,6 +25,47 @@ describe ResponseMate::Commands::List do
|
|
23
25
|
quietly { cmd_output }
|
24
26
|
expect(File.basename(output_files.call.last)).to eq('user_friends.yml')
|
25
27
|
end
|
28
|
+
|
29
|
+
describe 'output directory' do
|
30
|
+
context 'when the output_dir option is specified' do
|
31
|
+
let(:custom_output_dir) do
|
32
|
+
File.expand_path('spec/source/other_output_dir')
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:options) { { output_dir: custom_output_dir } }
|
36
|
+
|
37
|
+
context 'and it exists' do
|
38
|
+
let(:output_files) { -> { Dir[custom_output_dir + '/*'] } }
|
39
|
+
|
40
|
+
after { output_files.call.each { |file| File.delete(file) } }
|
41
|
+
|
42
|
+
it 'places the tapes in the specified directory' do
|
43
|
+
quietly { command.run }
|
44
|
+
|
45
|
+
expect(output_files.call.size).to eq(1)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'and it does not exist' do
|
50
|
+
let(:custom_output_dir) do
|
51
|
+
File.expand_path('spec/source/i_do_not_exist')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'raises ResponeMate::OutputDirError' do
|
55
|
+
expect { quietly { command.run } }.to raise_error(ResponseMate::OutputDirError,
|
56
|
+
/#{custom_output_dir}/)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when the output_dir options is not specified' do
|
62
|
+
it 'places the tapes in the default output directory' do
|
63
|
+
quietly { command.run }
|
64
|
+
|
65
|
+
expect(output_files.call.size).to eq(1)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
26
69
|
end
|
27
70
|
end
|
28
71
|
|
@@ -10,8 +10,10 @@ describe ResponseMate::Commands::Record do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
describe 'output files' do
|
13
|
+
subject { output_files.call }
|
14
|
+
|
13
15
|
it 'creates on for each request' do
|
14
|
-
expect(
|
16
|
+
expect(subject.size).to eq(2)
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
@@ -24,7 +26,7 @@ describe ResponseMate::Commands::Record do
|
|
24
26
|
let(:cmd_with_output_dir) do
|
25
27
|
quietly do
|
26
28
|
ResponseMate::Commands::Record.new([], keys: [],
|
27
|
-
output_dir:
|
29
|
+
output_dir: other_output_dir).run
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
@@ -34,8 +36,8 @@ describe ResponseMate::Commands::Record do
|
|
34
36
|
before { cmd_with_output_dir }
|
35
37
|
after { output_files.call.each { |file| File.delete(file) } }
|
36
38
|
|
37
|
-
it '
|
38
|
-
expect(output_files.call).to
|
39
|
+
it 'creates the tapes in the specified directory' do
|
40
|
+
expect(output_files.call.size).to eq(2)
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
@@ -45,7 +47,8 @@ describe ResponseMate::Commands::Record do
|
|
45
47
|
end
|
46
48
|
|
47
49
|
it 'raises ResponeMate::OutputDirError' do
|
48
|
-
expect { cmd_with_output_dir }.to raise_error(ResponseMate::OutputDirError
|
50
|
+
expect { cmd_with_output_dir }.to raise_error(ResponseMate::OutputDirError,
|
51
|
+
/#{other_output_dir}/)
|
49
52
|
end
|
50
53
|
end
|
51
54
|
end
|
@@ -54,7 +57,7 @@ describe ResponseMate::Commands::Record do
|
|
54
57
|
it 'creates the tapes in the default output directory' do
|
55
58
|
quietly { ResponseMate::Commands::Record.new([], keys: []).run }
|
56
59
|
|
57
|
-
expect(output_files.call).to
|
60
|
+
expect(output_files.call.size).to eq(2)
|
58
61
|
end
|
59
62
|
end
|
60
63
|
|
@@ -67,7 +70,7 @@ describe ResponseMate::Commands::Record do
|
|
67
70
|
end
|
68
71
|
|
69
72
|
it 'creates an output response file' do
|
70
|
-
expect(output_files.call).to
|
73
|
+
expect(output_files.call.size).to eq(1)
|
71
74
|
end
|
72
75
|
|
73
76
|
describe 'output response file' do
|
@@ -76,7 +79,7 @@ describe ResponseMate::Commands::Record do
|
|
76
79
|
end
|
77
80
|
|
78
81
|
it 'has the right file extension' do
|
79
|
-
expect(File.exist?(output_filename)).to
|
82
|
+
expect(File.exist?(output_filename)).to be(true)
|
80
83
|
end
|
81
84
|
|
82
85
|
describe 'YAML content' do
|
@@ -91,13 +94,9 @@ describe ResponseMate::Commands::Record do
|
|
91
94
|
expect(subject[:request][:verb]).to be_present
|
92
95
|
end
|
93
96
|
|
94
|
-
|
95
|
-
pending
|
96
|
-
end
|
97
|
+
xit 'contains the original request path'
|
97
98
|
|
98
|
-
|
99
|
-
pending
|
100
|
-
end
|
99
|
+
xit 'contains the original request params'
|
101
100
|
|
102
101
|
it 'contains the response status' do
|
103
102
|
expect(subject[:response][:status]).
|