sumo-search 2.0.3 → 2.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 27b7af6b07a96c23c0583a4dea06fcfb5bf118d2
4
- data.tar.gz: 9e5b066988e0bd14f73fb82b261ca7930a92a2e8
3
+ metadata.gz: 34911b9efb6c328c089cda2f45d3b6ddbe941c98
4
+ data.tar.gz: 9ff1594f807e696be3b703609f322289cff41c52
5
5
  SHA512:
6
- metadata.gz: 16a15d1e68e1ac6601bc7bd8471092bee2c744c9ffcb32c63b17e942162cad3cd6828d665d10b69ffa0b991153e9b43319b4bdd1f8a7307d23b714291c3156f2
7
- data.tar.gz: 05fb4b9cd0ffcf642160fcdcbef91c55d88ebd7714a8b260d80c9f01b044e9e21da449fc1addb27053474798872fe7c0ef33ab0a992803170078042da91e64b6
6
+ metadata.gz: 367766f8515371162e4be5bdffffc4d2f805e363fae0265640e0e7f655ce96531e1e4312d46d3944e0683cf6f6cd90484474faa20a29d5913294f544695621d9
7
+ data.tar.gz: 6c812fc6cc43eb95156b825a51e4ea9a1f54c609a98ffa66c684d4c9e86af289973b2da77da52ddef3ac08366193dc1ee8973db912b3441e066ca55c12bf5d61
@@ -4,6 +4,8 @@ class Sumo::Client
4
4
 
5
5
  attr_reader :email, :password, :cookie
6
6
 
7
+ REDIRECT_STATUSES = [301, 302, 303, 307, 308]
8
+
7
9
  # The error message raised when the result can be parsed from Sumo.
8
10
  DEFAULT_ERROR_MESSAGE = 'Error sending API request'
9
11
 
@@ -13,13 +15,25 @@ class Sumo::Client
13
15
  @password = credentials['password'].freeze
14
16
  end
15
17
 
16
- # Send a HTTP request to the server, handling any errors that may occur.
18
+ # Send a request to the API and retrieve processed data.
17
19
  def request(hash, &block)
18
- response = connection.request(add_defaults(hash), &block)
20
+ handle_request(hash, &block).body
21
+ end
22
+
23
+ # Send a HTTP request to the server, handling any errors that may occur.
24
+ def handle_request(hash, endpoint = nil, depth = 0, &block)
25
+ response = connection(endpoint).request(add_defaults(hash), &block)
26
+
27
+ if REDIRECT_STATUSES.include?(response.status) &&
28
+ response.headers['Location']
29
+ response = handle_redirect(response, hash, depth, &block)
30
+ end
31
+
19
32
  handle_errors!(response)
20
33
  set_cookie!(response)
21
- response.body
34
+ response
22
35
  end
36
+ private :handle_request
23
37
 
24
38
  # Define methods for the HTTP methods used by the API (#get, #post, and
25
39
  # #delete).
@@ -32,13 +46,27 @@ class Sumo::Client
32
46
  # Private functions that operate on the request and response.
33
47
 
34
48
  def add_defaults(hash)
35
- hash.merge(
36
- :headers => default_headers.merge(hash[:headers] || {}),
37
- :path => "/api/v#{Sumo::API_VERSION}#{hash[:path]}"
38
- )
49
+ hash[:headers] = default_headers.merge(hash[:headers] || {})
50
+ hash[:path] = "/api/v#{Sumo::API_VERSION}#{hash[:path]}" unless
51
+ hash[:path].index("/api/v#{Sumo::API_VERSION}") == 0
52
+ hash
39
53
  end
40
54
  private :add_defaults
41
55
 
56
+ # Recursively handle redirection up to 10 level depth
57
+ def handle_redirect(response, hash, depth, &block)
58
+ fail 'Too many redirections.' if depth > 9
59
+
60
+ endpoint = response.headers['Location']
61
+ .match(%r{^(https://.+\.[a-z]+)/}).to_a[1]
62
+
63
+ depth += 1
64
+ # I tried to blindly follow redirection path, but it omits the job ID.
65
+ # hash[:path] = path
66
+ handle_request(hash, endpoint, depth, &block)
67
+ end
68
+ private :handle_redirect
69
+
42
70
  def handle_errors!(response)
43
71
  case response.status
44
72
  when 400..499 then raise ClientError, extract_error_message(response.body)
@@ -79,8 +107,13 @@ class Sumo::Client
79
107
  end
80
108
  private :creds
81
109
 
82
- def connection
83
- @connection ||= Excon.new('https://api.sumologic.com')
110
+ def connection(endpoint = nil)
111
+ @connections ||= {}
112
+ endpoint ||= 'https://api.sumologic.com'
113
+
114
+ fail 'Base url out of allowed domain.' unless
115
+ endpoint.match(%r{^https://.+\.sumologic\.com$})
116
+ @connections[endpoint] ||= Excon.new(endpoint)
84
117
  end
85
118
  private :connection
86
119
  end
@@ -1,8 +1,8 @@
1
1
  # This module holds versioning information for the gem.
2
2
  module Sumo
3
3
  MAJOR = 2
4
- MINOR = 0
5
- PATCH = 3
4
+ MINOR = 1
5
+ PATCH = 0
6
6
  RELEASE = nil
7
7
 
8
8
  VERSION = [MAJOR, MINOR, PATCH, RELEASE].compact.join('.')
@@ -46,6 +46,7 @@ describe Sumo::Client do
46
46
  subject.stub(:handle_errors!)
47
47
  response.stub(:body)
48
48
  response.stub(:headers).and_return({})
49
+ response.stub(:status).and_return(200)
49
50
  connection.should_receive(:request)
50
51
  .with(
51
52
  :method => :get,
@@ -134,6 +135,118 @@ describe Sumo::Client do
134
135
  end
135
136
  end
136
137
  end
138
+
139
+ context 'when a 3xx-level status code is returned by the API' do
140
+ let(:cookie) { 'oreo' }
141
+ let(:headers) { {'Set-Cookie' => cookie } }
142
+ let(:default_connection) { double(:default_connection) }
143
+ let(:redirect_connection) { double(:redirect_connection) }
144
+
145
+ before do
146
+ # Do not stub full connection method but only Excon.
147
+ subject.stub(:connection).and_call_original
148
+ default_connection.stub(:request).and_return(response)
149
+ allow(Excon).to receive(:new)
150
+ .with('https://api.sumologic.com').and_return default_connection
151
+ allow(Excon).to receive(:new)
152
+ .with('https://api.us2.sumologic.com').and_return redirect_connection
153
+ end
154
+
155
+ context 'and the redirection url is within sumologic domain' do
156
+ let(:response) do
157
+ double(:response, :status => 301, :body => '', :headers => {
158
+ 'Location' => 'https://api.us2.sumologic.com/api/v1/jobs'
159
+ })
160
+ end
161
+ let(:final_response) do
162
+ double(:response, :status => 200, :body => '', :headers => {})
163
+ end
164
+
165
+ it 'should follow it' do
166
+ expect(default_connection).to receive(:request)
167
+ .with(
168
+ :method => :get,
169
+ :path => '/api/v1/',
170
+ :headers => {
171
+ 'Content-Type' => 'application/json',
172
+ 'Accept' => 'application/json',
173
+ 'Authorization' => "Basic #{encoded}" })
174
+ .and_return(response)
175
+
176
+ expect(redirect_connection).to receive(:request)
177
+ .with(
178
+ :method => :get,
179
+ :path => '/api/v1/',
180
+ :headers => {
181
+ 'Content-Type' => 'application/json',
182
+ 'Accept' => 'application/json',
183
+ 'Authorization' => "Basic #{encoded}" })
184
+ .and_return(final_response)
185
+ subject.request(:method => :get, :path => '/')
186
+ end
187
+ end
188
+
189
+ context 'and the redirection url is outside of sumologic' do
190
+ let(:response) do
191
+ double(:response, :status => 301, :body => '', :headers => {
192
+ 'Location' => 'https://donotfollow.me/api/v1/jobs'
193
+ })
194
+ end
195
+
196
+ it 'should not follow it' do
197
+ expect(default_connection).to receive(:request)
198
+ .with(
199
+ :method => :get,
200
+ :path => '/api/v1/',
201
+ :headers => {
202
+ 'Content-Type' => 'application/json',
203
+ 'Accept' => 'application/json',
204
+ 'Authorization' => "Basic #{encoded}" })
205
+ .once
206
+ .and_return(response)
207
+ expect(Excon).not_to receive(:new)
208
+ .with('https://donotfollow.me')
209
+ expect do
210
+ subject.request(:method => :get, :path => '/')
211
+ end.to raise_error 'Base url out of allowed domain.'
212
+ end
213
+ end
214
+
215
+ context 'and there is a redirection loop' do
216
+ let(:response) do
217
+ double(:response, :status => 301, :body => '', :headers => {
218
+ 'Location' => 'https://api.us2.sumologic.com/api/v1/jobs'
219
+ })
220
+ end
221
+
222
+ it 'should throw a too many redirections error' do
223
+ expect(default_connection).to receive(:request)
224
+ .with(
225
+ :method => :get,
226
+ :path => '/api/v1/',
227
+ :headers => {
228
+ 'Content-Type' => 'application/json',
229
+ 'Accept' => 'application/json',
230
+ 'Authorization' => "Basic #{encoded}" })
231
+ .and_return(response)
232
+
233
+ expect(redirect_connection).to receive(:request)
234
+ .with(
235
+ :method => :get,
236
+ :path => '/api/v1/',
237
+ :headers => {
238
+ 'Content-Type' => 'application/json',
239
+ 'Accept' => 'application/json',
240
+ 'Authorization' => "Basic #{encoded}" })
241
+ .exactly(10).times
242
+ .and_return(response)
243
+
244
+ expect do
245
+ subject.request(:method => :get, :path => '/')
246
+ end.to raise_error 'Too many redirections.'
247
+ end
248
+ end
249
+ end
137
250
  end
138
251
 
139
252
  [:get, :post, :delete].each do |http_method|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sumo-search
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Swipely, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-10 00:00:00.000000000 Z
11
+ date: 2015-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: excon