response_mate 0.1.1

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.
Files changed (46) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +15 -0
  3. data/.travis.yml +10 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile.lock +107 -0
  6. data/LICENSE +20 -0
  7. data/README.md +107 -0
  8. data/Rakefile +12 -0
  9. data/bin/response_mate +5 -0
  10. data/environment.yml.sample +4 -0
  11. data/lib/response_mate.rb +43 -0
  12. data/lib/response_mate/cli.rb +58 -0
  13. data/lib/response_mate/commands.rb +1 -0
  14. data/lib/response_mate/commands/base.rb +12 -0
  15. data/lib/response_mate/commands/clear.rb +24 -0
  16. data/lib/response_mate/commands/export.rb +29 -0
  17. data/lib/response_mate/commands/inspect.rb +42 -0
  18. data/lib/response_mate/commands/list.rb +48 -0
  19. data/lib/response_mate/commands/record.rb +27 -0
  20. data/lib/response_mate/commands/setup.rb +24 -0
  21. data/lib/response_mate/connection.rb +36 -0
  22. data/lib/response_mate/core.rb +29 -0
  23. data/lib/response_mate/environment.rb +22 -0
  24. data/lib/response_mate/exporter.rb +18 -0
  25. data/lib/response_mate/exporters/postman.rb +61 -0
  26. data/lib/response_mate/helpers/application.rb +3 -0
  27. data/lib/response_mate/http.rb +41 -0
  28. data/lib/response_mate/inspector.rb +54 -0
  29. data/lib/response_mate/manifest.rb +68 -0
  30. data/lib/response_mate/manifest_parser.rb +7 -0
  31. data/lib/response_mate/oauth.rb +17 -0
  32. data/lib/response_mate/recorder.rb +39 -0
  33. data/lib/response_mate/request.rb +2 -0
  34. data/lib/response_mate/tape.rb +18 -0
  35. data/lib/response_mate/version.rb +3 -0
  36. data/oauth.yml.sample +12 -0
  37. data/requests.yml.sample +16 -0
  38. data/response_mate.gemspec +41 -0
  39. data/roadmap.md +24 -0
  40. data/spec/fixtures/two_keys.yml.erb +8 -0
  41. data/spec/lib/response_mate/cli_spec.rb +108 -0
  42. data/spec/lib/response_mate/core_spec.rb +65 -0
  43. data/spec/lib/response_mate/recorder_spec.rb +103 -0
  44. data/spec/lib/response_mate_spec.rb +7 -0
  45. data/spec/spec_helper.rb +32 -0
  46. 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,3 @@
1
+ module ResponseMate::Helpers
2
+ def self.headerize(string); string.split('_').map(&:capitalize).join('-') end
3
+ 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