songkick-transport 1.2.0 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,21 +1,21 @@
1
1
  module Songkick
2
2
  module Transport
3
-
3
+
4
4
  class Headers
5
5
  include Enumerable
6
-
6
+
7
7
  def self.new(hash)
8
8
  return hash if self === hash
9
9
  super
10
10
  end
11
-
11
+
12
12
  def self.normalize(header_name)
13
13
  header_name.
14
14
  gsub(/^HTTP_/, '').gsub('_', '-').
15
15
  downcase.
16
16
  gsub(/(^|-)([a-z])/) { $1 + $2.upcase }
17
17
  end
18
-
18
+
19
19
  def initialize(hash = {})
20
20
  @hash = {}
21
21
  hash.each do |key, value|
@@ -23,30 +23,35 @@ module Songkick
23
23
  @hash[self.class.normalize(key)] = value
24
24
  end
25
25
  end
26
-
26
+
27
27
  def each(&block)
28
28
  @hash.each(&block)
29
29
  end
30
-
30
+
31
31
  def [](header_name)
32
32
  @hash[self.class.normalize(header_name)]
33
33
  end
34
-
34
+
35
35
  def []=(header_name, value)
36
36
  @hash[self.class.normalize(header_name)] = value
37
37
  end
38
-
38
+
39
39
  def merge(hash)
40
40
  headers = self.class.new(to_hash)
41
41
  hash.each { |k,v| headers[k] = v }
42
42
  headers
43
43
  end
44
-
44
+
45
45
  def to_hash
46
46
  @hash.dup
47
47
  end
48
+
49
+ def ==(other)
50
+ return false unless other.is_a?(self.class)
51
+ to_hash == other.to_hash
52
+ end
48
53
  end
49
-
54
+
50
55
  end
51
56
  end
52
57
 
@@ -55,6 +55,7 @@
55
55
 
56
56
  #transport-report td.data-footer {
57
57
  font-weight: bold;
58
+ padding-bottom: 10px;
58
59
  }
59
60
 
60
61
  #transport-report td.data-ms {
@@ -109,7 +110,7 @@
109
110
  <tr>
110
111
  <td class="data-verb"><%= request.verb.upcase %></td>
111
112
  <td class="data-path">
112
- <% if request.verb == "GET" %>
113
+ <% if request.verb.upcase == "GET" %>
113
114
  <% nice_params = (request.params.any? ? "?#{request.params.to_a.sort_by {|x,_| x.to_s}.map {|k,v| "#{k}=#{v}"}.join("&") }": "no params") %>
114
115
  <% path = request.path + nice_params %>
115
116
  <% uri = request.endpoint + path %>
@@ -127,7 +128,10 @@
127
128
  </tr>
128
129
  <% already[uri] = true %>
129
130
  <% end %>
130
- <tr><td></td><td></td><td class="data-footer data-ms"><%= hash[:total_duration] %> ms</td></tr>
131
+ <tr>
132
+ <td colspan="2" class="data-footer"><%= hash[:requests].size %> request(s)</td>
133
+ <td class="data-footer data-ms"><%= hash[:total_duration] %> ms</td>
134
+ </tr>
131
135
  </table>
132
136
 
133
137
  <% end %>
@@ -2,50 +2,45 @@ require 'httparty'
2
2
 
3
3
  module Songkick
4
4
  module Transport
5
-
5
+
6
6
  class HttParty
7
7
  def self.new(host, options = {})
8
- klass = options[:adapter] || Class.new(Adapter)
9
- klass.base_uri(host)
10
- klass.default_timeout(options[:timeout] || DEFAULT_TIMEOUT)
11
- klass.format(options[:format] || DEFAULT_FORMAT)
12
-
13
- transport = klass.new
14
- transport.user_agent = options[:user_agent]
15
- transport.user_error_codes =
16
- options[:user_error_codes] || DEFAULT_USER_ERROR_CODES
17
- transport
8
+ adapter_class = options.delete(:adapter) || Adapter
9
+ adapter_class.base_uri(host)
10
+ adapter_class.format(options[:format] || DEFAULT_FORMAT)
11
+ adapter_class.default_timeout(options[:timeout] || DEFAULT_TIMEOUT)
12
+ adapter_class.new(host, options)
18
13
  end
19
-
14
+
20
15
  class Adapter < Base
21
16
  include HTTParty
22
-
17
+
23
18
  def endpoint
24
19
  self.class.base_uri
25
20
  end
26
-
21
+
27
22
  def execute_request(req)
28
23
  timeout = req.timeout || self.class.default_options[:timeout]
29
-
24
+
30
25
  response = if req.use_body?
31
26
  self.class.__send__(req.verb, req.path, :body => req.body, :headers => req.headers, :timeout => timeout)
32
27
  else
33
28
  self.class.__send__(req.verb, req.url, :headers => req.headers, :timeout => timeout)
34
29
  end
35
-
30
+
36
31
  process(req, response.code, response.headers, response.parsed_response)
37
-
32
+
38
33
  rescue SocketError => error
39
34
  logger.warn "Could not connect to host: #{self.class.base_uri}"
40
35
  raise ConnectionFailedError, req
41
-
36
+
42
37
  rescue Timeout::Error => error
43
38
  logger.warn "Request timed out: #{req}"
44
39
  raise Transport::TimeoutError, req
45
40
 
46
41
  rescue UpstreamError => error
47
42
  raise error
48
-
43
+
49
44
  rescue Object => error
50
45
  if error.class.name =~ /json/i or error.message =~ /json/i
51
46
  logger.warn("Request returned invalid JSON: #{req}")
@@ -57,7 +52,6 @@ module Songkick
57
52
  end
58
53
  end
59
54
  end
60
-
55
+
61
56
  end
62
57
  end
63
-
@@ -3,52 +3,37 @@ require 'timeout'
3
3
 
4
4
  module Songkick
5
5
  module Transport
6
-
6
+
7
7
  class RackTest < Base
8
8
  class Client
9
9
  attr_reader :app
10
10
  include Rack::Test::Methods
11
-
11
+
12
12
  def initialize(app)
13
13
  @app = app
14
14
  end
15
15
  end
16
-
16
+
17
17
  def initialize(app, options = {})
18
- @app = app
19
- @timeout = options[:timeout] || DEFAULT_TIMEOUT
20
- @user_error_codes = options[:user_error_codes] || DEFAULT_USER_ERROR_CODES
18
+ super(nil, options)
19
+ @app = app
21
20
  end
22
-
23
- HTTP_VERBS.each do |verb|
24
- class_eval %{
25
- def #{verb}(path, params = {}, head = {}, timeout = nil)
26
- client = Client.new(@app)
27
- start = Time.now
28
- request = Request.new(@app, '#{verb}', path, params, headers.merge(head), timeout)
29
- result = nil
30
-
31
- Timeout.timeout(timeout || @timeout) do
32
- request.headers.each { |key, value| client.header(key, value) }
33
- response = client.#{verb}(path, params)
34
- request.response = process("\#{path}, \#{params.inspect}", response.status, response.headers, response.body)
35
- Reporting.record(request)
36
- request.response
37
- end
38
-
39
- rescue UpstreamError => error
40
- request.error = error
41
- Reporting.record(request)
42
- raise error
43
-
44
- rescue Object => error
45
- logger.warn(error.message)
46
- raise UpstreamError, request
47
- end
48
- }
21
+
22
+ def endpoint
23
+ @app
49
24
  end
25
+
26
+ def execute_request(req)
27
+ client = Client.new(@app)
28
+
29
+ Timeout.timeout(req.timeout || @timeout) do
30
+ req.headers.each { |key, value| client.header(key, value) }
31
+ response = client.__send__(req.verb, req.path, req.params)
32
+ process(req, response.status, response.headers, response.body)
33
+ end
34
+ end
35
+
50
36
  end
51
-
37
+
52
38
  end
53
39
  end
54
-
@@ -27,7 +27,7 @@ module Songkick
27
27
  response = request.response
28
28
  duration = (Time.now.to_f - request.start_time.to_f) * 1000
29
29
  logger.info "Response status: #{response.status}, duration: #{duration.ceil}ms"
30
- logger.debug "Response data: #{response.data.inspect}"
30
+ logger.debug { "Response data: #{response.data.inspect}" }
31
31
  end
32
32
 
33
33
  def self.logger
@@ -67,4 +67,3 @@ module Songkick
67
67
 
68
68
  end
69
69
  end
70
-
@@ -1,6 +1,6 @@
1
1
  module Songkick
2
2
  module Transport
3
-
3
+
4
4
  class Request
5
5
  attr_reader :endpoint,
6
6
  :verb,
@@ -10,9 +10,9 @@ module Songkick
10
10
  :start_time,
11
11
  :response,
12
12
  :error
13
-
13
+
14
14
  alias :http_method :verb
15
-
15
+
16
16
  def initialize(endpoint, verb, path, params, headers = {}, timeout = DEFAULT_TIMEOUT)
17
17
  @endpoint = endpoint
18
18
  @verb = verb.to_s.downcase
@@ -22,39 +22,39 @@ module Songkick
22
22
  @timeout = timeout
23
23
  @start_time = start_time || Time.now
24
24
  @multipart = Serialization.multipart?(params)
25
-
25
+
26
26
  if use_body?
27
27
  @headers['Content-Type'] ||= @multipart ? multipart_request[:content_type] : FORM_ENCODING
28
28
  end
29
29
  end
30
-
30
+
31
31
  def response=(response)
32
32
  @response = response
33
33
  @end_time = Time.now
34
34
  end
35
-
35
+
36
36
  def error=(error)
37
37
  @error = error
38
38
  @end_time = Time.now
39
39
  end
40
-
40
+
41
41
  def duration
42
42
  return nil unless @end_time
43
43
  (@end_time.to_f - @start_time.to_f) * 1000
44
44
  end
45
-
45
+
46
46
  def headers
47
47
  @headers.to_hash
48
48
  end
49
-
49
+
50
50
  def use_body?
51
51
  USE_BODY.include?(@verb)
52
52
  end
53
-
53
+
54
54
  def multipart?
55
55
  @multipart
56
56
  end
57
-
57
+
58
58
  def body
59
59
  return nil unless use_body?
60
60
  if @multipart
@@ -63,16 +63,16 @@ module Songkick
63
63
  Serialization.build_query_string(params)
64
64
  end
65
65
  end
66
-
66
+
67
67
  def url
68
68
  Serialization.build_url(@verb, @endpoint, @path, @params)
69
69
  end
70
-
70
+
71
71
  def to_s
72
72
  url = String === @endpoint ?
73
73
  Serialization.build_url(@verb, @endpoint, @path, @params, true) :
74
74
  @endpoint.to_s
75
-
75
+
76
76
  command = "#{@verb.upcase} '#{url}'"
77
77
  @headers.each do |key, value|
78
78
  value = Serialization::SANITIZED_VALUE if Serialization.sanitize?(key)
@@ -83,15 +83,14 @@ module Songkick
83
83
  command << " -d '#{query}'"
84
84
  command
85
85
  end
86
-
86
+
87
87
  private
88
-
88
+
89
89
  def multipart_request
90
90
  return nil unless @multipart
91
91
  @multipart_request ||= Serialization.serialize_multipart(params)
92
92
  end
93
93
  end
94
-
94
+
95
95
  end
96
96
  end
97
-
@@ -1,6 +1,6 @@
1
1
  module Songkick
2
2
  module Transport
3
-
3
+
4
4
  class Response
5
5
  def self.process(request, status, headers, body, user_error_codes=409)
6
6
  case status.to_i
@@ -23,26 +23,25 @@ module Songkick
23
23
  content_type = (content_type || '').split(/\s*;\s*/).first
24
24
  Transport.parser_for(content_type).parse(body)
25
25
  end
26
-
26
+
27
27
  attr_reader :body, :data, :headers, :status
28
-
28
+
29
29
  def initialize(status, headers, body)
30
30
  @body = body
31
31
  @data = Response.parse(body, headers['Content-Type'])
32
32
  @headers = Headers.new(headers)
33
33
  @status = status.to_i
34
34
  end
35
-
35
+
36
36
  def errors
37
37
  data && data['errors']
38
38
  end
39
-
39
+
40
40
  class OK < Response ; end
41
41
  class Created < Response ; end
42
42
  class NoContent < Response ; end
43
43
  class UserError < Response ; end
44
44
  end
45
-
45
+
46
46
  end
47
47
  end
48
-
@@ -1,6 +1,34 @@
1
1
  module Songkick
2
2
  module Transport
3
3
  class Service
4
+ DEFAULT_TIMEOUT = 10
5
+ DEFAULT_TRANSPORT = Songkick::Transport::Curb
6
+
7
+ def self.ancestor
8
+ warn "DEPRECATED: calling ancestor on #{self}"
9
+ self.ancestors.select { |a| a.respond_to?(:get_user_agent) }[1]
10
+ end
11
+
12
+ def self.stub_transport(stub)
13
+ warn "DEPRECATED: calling stub_transport on #{self}"
14
+ @stub_transport = stub
15
+ end
16
+
17
+ def self.extra_headers
18
+ warn "DEPRECATED: calling extra_headers on #{self}"
19
+ get_with_headers
20
+ end
21
+
22
+ def self.this_extra_headers
23
+ warn "DEPRECATED: calling this_extra_headers on #{self}"
24
+ @with_headers || {}
25
+ end
26
+
27
+ def self.parent_service
28
+ superclass if superclass <= Songkick::Transport::Service
29
+ end
30
+ private_class_method :parent_service
31
+
4
32
  def self.endpoint(name)
5
33
  @endpoint_name = name.to_s
6
34
  end
@@ -17,8 +45,8 @@ module Songkick
17
45
  @transport_layer = value
18
46
  end
19
47
 
20
- def self.stub_transport(stub)
21
- @stub_transport = stub
48
+ def self.transport_layer_options(value)
49
+ @transport_layer_options = value
22
50
  end
23
51
 
24
52
  def self.set_endpoints(hash)
@@ -28,20 +56,16 @@ module Songkick
28
56
  @endpoints = hash
29
57
  end
30
58
 
31
- def self.ancestor
32
- self.ancestors.select {|a| a.respond_to?(:get_user_agent)}[1]
33
- end
34
-
35
59
  def self.get_endpoint_name
36
- @endpoint_name || (ancestor && ancestor.get_endpoint_name)
60
+ @endpoint_name || (parent_service && parent_service.get_endpoint_name)
37
61
  end
38
62
 
39
63
  def self.get_timeout
40
- @timeout || (ancestor && ancestor.get_timeout) || 10
64
+ @timeout || (parent_service && parent_service.get_timeout) || DEFAULT_TIMEOUT
41
65
  end
42
66
 
43
67
  def self.get_user_agent
44
- @user_agent || (ancestor && ancestor.get_user_agent)
68
+ @user_agent || (parent_service && parent_service.get_user_agent)
45
69
  end
46
70
 
47
71
  def self.get_endpoints
@@ -49,11 +73,19 @@ module Songkick
49
73
  end
50
74
 
51
75
  def self.get_transport_layer
52
- @transport_layer || (ancestor && ancestor.get_transport_layer) || Songkick::Transport::Curb
76
+ @transport_layer || (parent_service && parent_service.get_transport_layer) || DEFAULT_TRANSPORT
77
+ end
78
+
79
+ def self.get_transport_layer_options
80
+ ((parent_service && parent_service.get_transport_layer_options) || {}).merge(@transport_layer_options || {})
53
81
  end
54
82
 
55
83
  def self.get_stub_transport
56
- @stub_transport || (ancestor && ancestor.get_stub_transport) || nil
84
+ @stub_transport || (parent_service && parent_service.get_stub_transport) || nil
85
+ end
86
+
87
+ def self.get_with_headers
88
+ ((parent_service && parent_service.get_with_headers) || {}).merge(@with_headers || {})
57
89
  end
58
90
 
59
91
  def self.new_transport
@@ -66,12 +98,17 @@ module Songkick
66
98
  unless user_agent = get_user_agent
67
99
  raise "no user agent specified for #{self}, call user_agent 'foo' inside #{self} or on Songkick::Transport::Service"
68
100
  end
69
- get_stub_transport || get_transport_layer.new(endpoint, :user_agent => user_agent,
70
- :timeout => get_timeout)
101
+ get_stub_transport || get_transport_layer.new(endpoint, { :user_agent => user_agent, :timeout => get_timeout }.merge(get_transport_layer_options))
102
+ end
103
+
104
+ def self.with_headers(headers)
105
+ @with_headers = headers
71
106
  end
72
107
 
73
108
  def http
74
- @http ||= self.class.new_transport
109
+ r = (@http ||= self.class.new_transport)
110
+ r.with_headers(self.class.get_with_headers) if !self.class.get_with_headers.empty?
111
+ r
75
112
  end
76
113
 
77
114
  def stub_transport(http)