td-client 0.8.78 → 0.8.79

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: b7b08bc82aed5007cc2f9b0500847ca78520cc78
4
- data.tar.gz: 1fa61539241cce5a9152f23629f46c5c95a7ec01
3
+ metadata.gz: 361771ba9d0d5fd00765c9ce956c2853ae4b3333
4
+ data.tar.gz: b6863fc4474b7598417d3e1fc776f585577b0b8b
5
5
  SHA512:
6
- metadata.gz: cd680197fab594cf321145804b06b99d9070cecb97115625b7a489d94b4baae685f47744a866172f20baf6d50b7e9c9eca0287b20ab0d4692cfeec38aa5e9448
7
- data.tar.gz: 64fc10d47a765efc4bab85fb24fa12b0cbc459336467f21765007057807117c6177f936f642c84da608d15fa09ddb9dacf5e08a3b50fcb96f900860f78c9c02e
6
+ metadata.gz: 9a36906b042f9fa5f41288a34ae679e54a80af6709f03ca41f94935728ca0450bf2f45adb5c63c348546448d5824c13db3351d6e1f3521963cd453d2f400ca8e
7
+ data.tar.gz: 3e564144569d8b2dd1b3ee1895a3a8117846802565d7581395deb5246ebc2588237c64868308e609e75dc9a8160c9a12f9d20687dfc254eb43a52623c51c87ac
@@ -356,7 +356,7 @@ class Client
356
356
  raise ArgumentError, "'cron' option is required" unless opts[:cron] || opts['cron']
357
357
  raise ArgumentError, "'query' option is required" unless opts[:query] || opts['query']
358
358
  start = @api.create_schedule(name, opts)
359
- return Time.parse(start)
359
+ return start && Time.parse(start)
360
360
  end
361
361
 
362
362
  # @param [String] name
@@ -235,16 +235,19 @@ private
235
235
 
236
236
  # @param [String] url
237
237
  # @param [Hash] params
238
+ # @param [Hash] opt
238
239
  # @yield [response]
239
- def get(url, params=nil, &block)
240
+ def get(url, params=nil, opt={}, &block)
240
241
  guard_no_sslv3 do
241
- do_get(url, params, &block)
242
+ do_get(url, params, opt, &block)
242
243
  end
243
244
  end
245
+
244
246
  # @param [String] url
245
247
  # @param [Hash] params
248
+ # @param [Hash] opt
246
249
  # @yield [response]
247
- def do_get(url, params=nil, &block)
250
+ def do_get(url, params=nil, opt={}, &block)
248
251
  client, header = new_client
249
252
  client.send_timeout = @send_timeout
250
253
  client.receive_timeout = @read_timeout
@@ -267,24 +270,29 @@ private
267
270
  # for both exceptions and 500+ errors retrying is enabled by default.
268
271
  # The total number of retries cumulatively should not exceed 10 minutes / 600 seconds
269
272
  response = nil
273
+ etag = nil
274
+ current_total_chunk_size = 0
270
275
  begin # this block is to allow retry (redo) in the begin part of the begin-rescue block
271
276
  begin
272
- if block
277
+ if etag
278
+ header['If-Range'] = etag
279
+ header['Range'] = "bytes=#{current_total_chunk_size}-"
280
+ else
273
281
  current_total_chunk_size = 0
282
+ end
283
+ if block
274
284
  response = client.get(target, params, header) {|res, chunk|
275
285
  current_total_chunk_size += chunk.bytesize
276
286
  block.call(res, chunk, current_total_chunk_size)
277
287
  }
278
-
279
- # XXX ext/openssl raises EOFError in case where underlying connection causes an error,
280
- # and msgpack-ruby that used in block handles it as an end of stream == no exception.
281
- # Therefor, check content size.
282
- validate_content_length!(response, current_total_chunk_size) if @ssl
283
288
  else
284
289
  response = client.get(target, params, header)
285
-
286
- validate_content_length!(response, response.body.bytesize) if @ssl
290
+ current_total_chunk_size += response.body.bytesize
287
291
  end
292
+ # XXX ext/openssl raises EOFError in case where underlying connection causes an error,
293
+ # but httpclient ignores it. Therefor, check content size.
294
+ # https://github.com/nahi/httpclient/issues/296
295
+ validate_content_length!(response, current_total_chunk_size)
288
296
 
289
297
  status = response.code
290
298
  # retry if the HTTP error code is 500 or higher and we did not run out of retrying attempts
@@ -296,7 +304,9 @@ private
296
304
  redo # restart from beginning of do-while loop
297
305
  end
298
306
  rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Timeout::Error, EOFError, OpenSSL::SSL::SSLError, SocketError, IncompleteError => e
299
- if block_given?
307
+ if opt[:resume]
308
+ etag = response.header['ETag'].first
309
+ elsif block_given?
300
310
  raise e
301
311
  end
302
312
  $stderr.print "#{e.class}: #{e.message}. "
@@ -328,8 +338,14 @@ private
328
338
  end
329
339
 
330
340
  def validate_content_length!(response, body_size)
331
- content_length = response.header['Content-Length'].first
332
- raise IncompleteError if @ssl && content_length && content_length.to_i != body_size
341
+ if content_length = response.header['Content-Range'].first
342
+ content_length = content_length[/\d+$/]
343
+ else
344
+ content_length = response.header['Content-Length'].first
345
+ end
346
+ if content_length && content_length.to_i != body_size
347
+ raise IncompleteError, "#{content_length} bytes expected, but got #{body_size} bytes"
348
+ end
333
349
  end
334
350
 
335
351
  def inflate_body(response)
@@ -214,8 +214,8 @@ module Job
214
214
  def job_result_raw(job_id, format, io = nil, &block)
215
215
  body = nil
216
216
 
217
- get("/v3/job/result/#{e job_id}", {'format'=>format}) {|res, chunk, current_total_chunk_size|
218
- if res.code != 200
217
+ get("/v3/job/result/#{e job_id}", {'format'=>format}, {:resume => true}) {|res, chunk, current_total_chunk_size|
218
+ unless res.ok?
219
219
  raise_error("Get job result failed", res)
220
220
  end
221
221
 
@@ -14,7 +14,7 @@ module Schedule
14
14
  if code != "200"
15
15
  raise_error("Create schedule failed", res)
16
16
  end
17
- js = checked_json(body, %w[start])
17
+ js = checked_json(body)
18
18
  return js['start']
19
19
  end
20
20
 
@@ -1,5 +1,5 @@
1
1
  module TreasureData
2
2
  class Client
3
- VERSION = '0.8.78'
3
+ VERSION = '0.8.79'
4
4
  end
5
5
  end
@@ -10,10 +10,10 @@ unless ENV['APPVEYOR']
10
10
  require 'simplecov'
11
11
  require 'coveralls'
12
12
 
13
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
13
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
14
14
  SimpleCov::Formatter::HTMLFormatter,
15
15
  Coveralls::SimpleCov::Formatter
16
- ]
16
+ ])
17
17
  SimpleCov.start("test_frameworks")
18
18
  end
19
19
  rescue NameError
@@ -311,6 +311,53 @@ describe 'Job API' do
311
311
  api.job_result_raw(12345, 'json').should == 'raw binary'
312
312
  end
313
313
  end
314
+
315
+ let :packed do
316
+ # Hard code fixture data to make the size stable
317
+ # s = StringIO.new
318
+ # Zlib::GzipWriter.wrap(s) do |f|
319
+ # pk = MessagePack::Packer.new(f)
320
+ # pk.write('hello')
321
+ # pk.write('world')
322
+ # pk.flush
323
+ # end
324
+ # s.string
325
+ "\x1F\x8B\b\x00#\xA1\x93T\x00\x03[\x9A\x91\x9A\x93\x93\xBF\xB4<\xBF('\x05\x00e 0\xB3\f\x00\x00\x00".force_encoding(Encoding::ASCII_8BIT)
326
+ end
327
+
328
+ it 'can resume' do
329
+ stub_api_request(:get, '/v3/job/result/12345').
330
+ with(:query => {'format' => 'msgpack.gz'}).
331
+ to_return(
332
+ :headers => {
333
+ 'Content-Length' => 32,
334
+ 'Etag' => '"abcdefghijklmn"',
335
+ },
336
+ :body => packed[0, 20]
337
+ )
338
+ stub_api_request(:get, '/v3/job/result/12345').
339
+ with(
340
+ :headers => {
341
+ 'If-Range' => '"abcdefghijklmn"',
342
+ 'Range' => 'bytes=20-',
343
+ },
344
+ :query => {'format' => 'msgpack.gz'}
345
+ ).
346
+ to_return(
347
+ :headers => {
348
+ 'Content-Length' => 12,
349
+ 'Content-Range' => 'bytes 20-31/32',
350
+ 'Etag' => '"abcdefghijklmn"',
351
+ },
352
+ :body => packed[20, 12]
353
+ )
354
+ expect(api).to receive(:sleep).once
355
+ expect($stderr).to receive(:print)
356
+ expect($stderr).to receive(:puts)
357
+ sio = StringIO.new(''.force_encoding(Encoding::ASCII_8BIT))
358
+ api.job_result_raw(12345, 'msgpack.gz', sio)
359
+ sio.string.should == packed
360
+ end
314
361
  end
315
362
 
316
363
  describe 'kill' do
@@ -23,6 +23,14 @@ describe 'Schedule API' do
23
23
  api.create_schedule(sched_name, opts.merge('type' => 'hive')).should == start.to_s
24
24
  end
25
25
 
26
+ it 'should create a dummy schedule' do
27
+ stub_api_request(:post, "/v3/schedule/create/#{e(sched_name)}").
28
+ with(:body => opts.merge('type' => 'hive')).
29
+ to_return(:body => {'name' => sched_name, 'start' => nil}.to_json)
30
+
31
+ api.create_schedule(sched_name, opts.merge('type' => 'hive')).should be_nil
32
+ end
33
+
26
34
  it 'should return 422 error with invalid name' do
27
35
  name = '1'
28
36
  err_msg = "Validation failed: Name is too short" # " (minimum is 3 characters)"
@@ -15,6 +15,31 @@ describe 'Schedule Command' do
15
15
  client
16
16
  end
17
17
 
18
+ describe 'create' do
19
+ let :opts do
20
+ {:database => db_name, :cron => '', :type => 'hive', :query => 'select 1;'}
21
+ end
22
+
23
+ before do
24
+ stub_api_request(:post, "/v3/schedule/create/#{e(sched_name)}").
25
+ with(:body => opts).
26
+ to_return(:body => {'name' => sched_name, 'start' => start}.to_json)
27
+ end
28
+ context 'start is now' do
29
+ let (:start){ Time.now.round }
30
+ it 'returns Time object' do
31
+ client.create_schedule(sched_name, opts).should == start
32
+ end
33
+ end
34
+
35
+ context 'start is nil' do
36
+ let (:start){ nil }
37
+ it do
38
+ client.create_schedule(sched_name, opts).should == start
39
+ end
40
+ end
41
+ end
42
+
18
43
  describe 'history' do
19
44
  let :opts do
20
45
  {'database' => db_name}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: td-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.78
4
+ version: 0.8.79
5
5
  platform: ruby
6
6
  authors:
7
7
  - Treasure Data, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-26 00:00:00.000000000 Z
11
+ date: 2016-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack