td-client 0.9.0dev2 → 1.0.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 +4 -4
- data/lib/td/client.rb +16 -8
- data/lib/td/client/api.rb +66 -47
- data/lib/td/client/api/bulk_import.rb +1 -2
- data/lib/td/client/api/bulk_load.rb +3 -3
- data/lib/td/client/api/export.rb +12 -0
- data/lib/td/client/api/import.rb +3 -2
- data/lib/td/client/api/job.rb +146 -71
- data/lib/td/client/api/schedule.rb +1 -1
- data/lib/td/client/api_error.rb +5 -0
- data/lib/td/client/model.rb +92 -28
- data/lib/td/client/version.rb +1 -1
- data/spec/spec_helper.rb +5 -5
- data/spec/td/client/account_api_spec.rb +5 -5
- data/spec/td/client/api_error_spec.rb +77 -0
- data/spec/td/client/api_spec.rb +76 -52
- data/spec/td/client/api_ssl_connection_spec.rb +1 -1
- data/spec/td/client/bulk_import_spec.rb +28 -29
- data/spec/td/client/bulk_load_spec.rb +60 -35
- data/spec/td/client/db_api_spec.rb +1 -1
- data/spec/td/client/export_api_spec.rb +11 -1
- data/spec/td/client/import_api_spec.rb +85 -10
- data/spec/td/client/job_api_spec.rb +568 -61
- data/spec/td/client/model_job_spec.rb +27 -10
- data/spec/td/client/model_schedule_spec.rb +2 -2
- data/spec/td/client/model_schema_spec.rb +134 -0
- data/spec/td/client/partial_delete_api_spec.rb +1 -1
- data/spec/td/client/result_api_spec.rb +3 -3
- data/spec/td/client/sched_api_spec.rb +12 -4
- data/spec/td/client/server_status_api_spec.rb +2 -2
- data/spec/td/client/spec_resources.rb +1 -0
- data/spec/td/client/table_api_spec.rb +14 -14
- data/spec/td/client/user_api_spec.rb +12 -12
- data/spec/td/client_sched_spec.rb +31 -6
- data/spec/td/client_spec.rb +1 -0
- metadata +42 -81
data/lib/td/client/api_error.rb
CHANGED
@@ -23,6 +23,11 @@ end
|
|
23
23
|
|
24
24
|
# 409 API errors
|
25
25
|
class AlreadyExistsError < APIError
|
26
|
+
attr_reader :conflicts_with
|
27
|
+
def initialize(error_message = nil, api_backtrace = nil, conflicts_with=nil)
|
28
|
+
super(error_message, api_backtrace)
|
29
|
+
@conflicts_with = conflicts_with
|
30
|
+
end
|
26
31
|
end
|
27
32
|
|
28
33
|
# 404 API errors
|
data/lib/td/client/model.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
+
require 'timeout'
|
1
2
|
|
2
3
|
module TreasureData
|
3
4
|
|
4
|
-
|
5
5
|
class Model
|
6
6
|
# @param [TreasureData::Client] client
|
7
7
|
def initialize(client)
|
@@ -269,36 +269,62 @@ class Table < Model
|
|
269
269
|
def update_database!
|
270
270
|
@database = @client.database(@db_name)
|
271
271
|
end
|
272
|
+
|
273
|
+
# @return [String]
|
274
|
+
def inspect
|
275
|
+
%[#<%s:%#0#{1.size*2}x @db_name="%s" @table_name="%s">] %
|
276
|
+
[self.class.name, self.__id__*2, @db_name, @table_name]
|
277
|
+
end
|
272
278
|
end
|
273
279
|
|
274
280
|
class Schema
|
275
281
|
class Field
|
276
282
|
# @param [String] name
|
277
283
|
# @param [String] type
|
278
|
-
|
284
|
+
# @param [String] sql_alias
|
285
|
+
def initialize(name, type, sql_alias=nil)
|
286
|
+
if name == 'v' || name == 'time'
|
287
|
+
raise ParameterValidationError, "Column name '#{name}' is reserved."
|
288
|
+
end
|
289
|
+
API.validate_column_name(name)
|
290
|
+
API.validate_sql_alias_name(sql_alias) if sql_alias
|
279
291
|
@name = name
|
280
292
|
@type = type
|
293
|
+
@sql_alias = sql_alias
|
281
294
|
end
|
282
295
|
|
283
296
|
# @!attribute [r] name
|
284
297
|
# @!attribute [r] type
|
285
298
|
attr_reader :name
|
286
299
|
attr_reader :type
|
300
|
+
attr_reader :sql_alias
|
287
301
|
end
|
288
302
|
|
289
|
-
# @param [String]
|
303
|
+
# @param [String] columns
|
290
304
|
# @return [Schema]
|
291
|
-
def self.parse(
|
292
|
-
|
293
|
-
|
294
|
-
|
305
|
+
def self.parse(columns)
|
306
|
+
schema = Schema.new
|
307
|
+
columns.each {|column|
|
308
|
+
unless /\A(?<name>.*)(?::(?<type>[^:@]+))(?:@(?<sql_alias>[^:@]+))?\z/ =~ column
|
309
|
+
raise ParameterValidationError, "type must be specified"
|
310
|
+
end
|
311
|
+
schema.add_field(name, type, sql_alias)
|
295
312
|
}
|
296
|
-
|
313
|
+
schema
|
297
314
|
end
|
298
315
|
|
299
316
|
# @param [Array] fields
|
300
317
|
def initialize(fields=[])
|
301
318
|
@fields = fields
|
319
|
+
@names = {}
|
320
|
+
@fields.each do |f|
|
321
|
+
raise ArgumentError, "Column name '#{f.name}' is duplicated." if @names.key?(f.name)
|
322
|
+
@names[f.name] = true
|
323
|
+
if f.sql_alias
|
324
|
+
raise ArgumentError, "SQL Column alias '#{f.sql_alias}' is duplicated." if @names.key?(f.sql_alias)
|
325
|
+
@names[f.sql_alias] = true
|
326
|
+
end
|
327
|
+
end
|
302
328
|
end
|
303
329
|
|
304
330
|
# @!attribute [r] fields
|
@@ -307,8 +333,16 @@ class Schema
|
|
307
333
|
# @param [String] name
|
308
334
|
# @param [String] type
|
309
335
|
# @return [Array]
|
310
|
-
def add_field(name, type)
|
311
|
-
@
|
336
|
+
def add_field(name, type, sql_alias=nil)
|
337
|
+
if @names.key?(name)
|
338
|
+
raise ParameterValidationError, "Column name '#{name}' is duplicated."
|
339
|
+
end
|
340
|
+
@names[name] = true
|
341
|
+
if sql_alias && @names.key?(sql_alias)
|
342
|
+
raise ParameterValidationError, "SQL Column alias '#{sql_alias}' is duplicated."
|
343
|
+
end
|
344
|
+
@names[sql_alias] = true
|
345
|
+
@fields << Field.new(name, type, sql_alias)
|
312
346
|
end
|
313
347
|
|
314
348
|
# @param [Schema] schema
|
@@ -327,14 +361,14 @@ class Schema
|
|
327
361
|
|
328
362
|
# @return [Array<Field>]
|
329
363
|
def to_json(*args)
|
330
|
-
@fields.map {|f| [f.name, f.type] }.to_json(*args)
|
364
|
+
@fields.map {|f| f.sql_alias ? [f.name, f.type, f.sql_alias] : [f.name, f.type] }.to_json(*args)
|
331
365
|
end
|
332
366
|
|
333
367
|
# @param [Object] obj
|
334
368
|
# @return [self]
|
335
369
|
def from_json(obj)
|
336
370
|
@fields = obj.map {|f|
|
337
|
-
Field.new(f
|
371
|
+
Field.new(*f)
|
338
372
|
}
|
339
373
|
self
|
340
374
|
end
|
@@ -349,8 +383,6 @@ class Job < Model
|
|
349
383
|
STATUS_KILLED = "killed"
|
350
384
|
FINISHED_STATUS = [STATUS_SUCCESS, STATUS_ERROR, STATUS_KILLED]
|
351
385
|
|
352
|
-
class TimeoutError < StandardError; end
|
353
|
-
|
354
386
|
# @param [TreasureData::Client] client
|
355
387
|
# @param [String] job_id
|
356
388
|
# @param [String] type
|
@@ -370,9 +402,10 @@ class Job < Model
|
|
370
402
|
# @param [String] org_name
|
371
403
|
# @param [String] db_name
|
372
404
|
# @param [Fixnum] duration
|
405
|
+
# @param [Fixnum] num_records
|
373
406
|
def initialize(client, job_id, type, query, status=nil, url=nil, debug=nil, start_at=nil, end_at=nil, cpu_time=nil,
|
374
407
|
result_size=nil, result=nil, result_url=nil, hive_result_schema=nil, priority=nil, retry_limit=nil,
|
375
|
-
org_name=nil, db_name=nil, duration=nil)
|
408
|
+
org_name=nil, db_name=nil, duration=nil, num_records=nil)
|
376
409
|
super(client)
|
377
410
|
@job_id = job_id
|
378
411
|
@type = type
|
@@ -391,6 +424,7 @@ class Job < Model
|
|
391
424
|
@retry_limit = retry_limit
|
392
425
|
@db_name = db_name
|
393
426
|
@duration = duration
|
427
|
+
@num_records = num_records
|
394
428
|
end
|
395
429
|
|
396
430
|
# @!attribute [r] job_id
|
@@ -401,21 +435,40 @@ class Job < Model
|
|
401
435
|
# @!attribute [r] org_name
|
402
436
|
# @!attribute [r] db_name
|
403
437
|
# @!attribute [r] duration
|
438
|
+
# @!attribute [r] num_records
|
404
439
|
attr_reader :job_id, :type, :result_url
|
405
440
|
attr_reader :priority, :retry_limit, :org_name, :db_name
|
406
|
-
attr_reader :duration
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
441
|
+
attr_reader :duration, :num_records
|
442
|
+
|
443
|
+
# @option timeout [Integer,nil] timeout in second
|
444
|
+
# @option wait_interval [Integer,nil] interval in second of polling the job status
|
445
|
+
# @param detail [Boolean] update job detail or not
|
446
|
+
# @param verbose [Boolean] out retry log to stderr or not
|
447
|
+
def wait(*args)
|
448
|
+
opthash = Hash.try_convert(args.last)
|
449
|
+
if opthash
|
450
|
+
args.pop
|
451
|
+
detail = opthash.fetch(:detail, false)
|
452
|
+
verbose = opthash.fetch(:verbose, ENV['TD_CLIENT_DEBUG'])
|
418
453
|
end
|
454
|
+
timeout = args[0]
|
455
|
+
wait_interval = args[1] || 2
|
456
|
+
deadline = monotonic_clock + timeout if timeout
|
457
|
+
timeout_klass = Class.new(Exception)
|
458
|
+
begin
|
459
|
+
if timeout
|
460
|
+
if deadline <= monotonic_clock
|
461
|
+
raise timeout_klass, "timeout (#{timeout}) exceeded wait_interval=#{wait_interval}"
|
462
|
+
end
|
463
|
+
end
|
464
|
+
sleep wait_interval
|
465
|
+
detail ? update_status! : update_progress!
|
466
|
+
yield self if block_given?
|
467
|
+
rescue timeout_klass
|
468
|
+
raise Timeout::Error, $!.message
|
469
|
+
rescue Timeout::Error, SystemCallError, EOFError, SocketError, HTTPClient::ConnectTimeoutError
|
470
|
+
$stderr.puts "ignore network error (#{$!}); retry..." if verbose
|
471
|
+
end until finished?
|
419
472
|
end
|
420
473
|
|
421
474
|
def kill!
|
@@ -564,7 +617,7 @@ class Job < Model
|
|
564
617
|
def update_status!
|
565
618
|
type, query, status, url, debug, start_at, end_at, cpu_time,
|
566
619
|
result_size, result_url, hive_result_schema, priority, retry_limit,
|
567
|
-
org_name, db_name = @client.api.show_job(@job_id)
|
620
|
+
org_name, db_name , duration, num_records = @client.api.show_job(@job_id)
|
568
621
|
@query = query
|
569
622
|
@status = status
|
570
623
|
@url = url
|
@@ -578,8 +631,19 @@ class Job < Model
|
|
578
631
|
@priority = priority
|
579
632
|
@retry_limit = retry_limit
|
580
633
|
@db_name = db_name
|
634
|
+
@duration = duration
|
635
|
+
@num_records = num_records
|
581
636
|
self
|
582
637
|
end
|
638
|
+
|
639
|
+
private
|
640
|
+
def monotonic_clock
|
641
|
+
if defined?(Process.clock_gettime)
|
642
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
643
|
+
else
|
644
|
+
Time.now.to_i
|
645
|
+
end
|
646
|
+
end
|
583
647
|
end
|
584
648
|
|
585
649
|
|
data/lib/td/client/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -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
|
@@ -48,13 +48,13 @@ shared_context 'common helper' do
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def stub_api_request(method, path, opts = nil)
|
51
|
-
scheme = '
|
51
|
+
scheme = 'https'
|
52
52
|
with_opts = {:headers => headers}
|
53
53
|
if opts
|
54
|
-
scheme = '
|
54
|
+
scheme = 'http' if opts[:ssl] == false
|
55
55
|
with_opts[:query] = opts[:query] if opts[:query]
|
56
56
|
end
|
57
|
-
stub_request(method, "#{scheme}://api.
|
57
|
+
stub_request(method, "#{scheme}://api.treasuredata.com#{path}").with(with_opts)
|
58
58
|
end
|
59
59
|
|
60
60
|
def e(s)
|
@@ -14,7 +14,7 @@ describe 'Account API' do
|
|
14
14
|
it 'returns account properties' do
|
15
15
|
stub_api_request(:get, "/v3/account/show").
|
16
16
|
to_return(:body => {'account' => {'id' => 1, 'plan' => 0, 'storage_size' => 2, 'guaranteed_cores' => 3, 'maximum_cores' => 4, 'created_at' => '2014-12-14T17:24:00+0900'}}.to_json)
|
17
|
-
api.show_account.
|
17
|
+
expect(api.show_account).to eq([1, 0, 2, 3, 4, "2014-12-14T17:24:00+0900"])
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -25,10 +25,10 @@ describe 'Account API' do
|
|
25
25
|
stub_api_request(:get, "/v3/account/core_utilization", :query => {'from' => from, 'to' => to}).
|
26
26
|
to_return(:body => {'from' => from, 'to' => to, 'interval' => 1, 'history' => ['dummy']}.to_json)
|
27
27
|
r = api.account_core_utilization(from, to)
|
28
|
-
r[0].
|
29
|
-
r[1].
|
30
|
-
r[2].
|
31
|
-
r[3].
|
28
|
+
expect(r[0]).to eq(Time.parse(from))
|
29
|
+
expect(r[1]).to eq(Time.parse(to))
|
30
|
+
expect(r[2]).to eq(1)
|
31
|
+
expect(r[3]).to eq(['dummy'])
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe APIError do
|
4
|
+
let (:message){ 'message' }
|
5
|
+
let (:api_backtrace){ double('api_backtrace') }
|
6
|
+
describe 'new' do
|
7
|
+
context '' do
|
8
|
+
it do
|
9
|
+
exc = APIError.new(message, api_backtrace)
|
10
|
+
expect(exc).to be_an(APIError)
|
11
|
+
expect(exc.message).to eq message
|
12
|
+
expect(exc.api_backtrace).to eq api_backtrace
|
13
|
+
end
|
14
|
+
end
|
15
|
+
context 'api_backtrace is ""' do
|
16
|
+
let (:api_backtrace){ '' }
|
17
|
+
it do
|
18
|
+
exc = APIError.new(message, api_backtrace)
|
19
|
+
expect(exc).to be_an(APIError)
|
20
|
+
expect(exc.message).to eq message
|
21
|
+
expect(exc.api_backtrace).to be_nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
context 'api_backtrace is nil' do
|
25
|
+
let (:api_backtrace){ nil }
|
26
|
+
it do
|
27
|
+
exc = APIError.new(message, api_backtrace)
|
28
|
+
expect(exc).to be_an(APIError)
|
29
|
+
expect(exc.message).to eq message
|
30
|
+
expect(exc.api_backtrace).to be_nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe AlreadyExistsError do
|
37
|
+
let (:message){ 'message' }
|
38
|
+
let (:api_backtrace){ double('api_backtrace') }
|
39
|
+
let (:conflicts_with){ '12345' }
|
40
|
+
describe 'new' do
|
41
|
+
context '' do
|
42
|
+
it do
|
43
|
+
exc = AlreadyExistsError.new(message, api_backtrace)
|
44
|
+
expect(exc).to be_an(AlreadyExistsError)
|
45
|
+
expect(exc.message).to eq message
|
46
|
+
expect(exc.api_backtrace).to eq api_backtrace
|
47
|
+
end
|
48
|
+
end
|
49
|
+
context 'api_backtrace is ""' do
|
50
|
+
let (:api_backtrace){ '' }
|
51
|
+
it do
|
52
|
+
exc = AlreadyExistsError.new(message, api_backtrace)
|
53
|
+
expect(exc).to be_an(AlreadyExistsError)
|
54
|
+
expect(exc.message).to eq message
|
55
|
+
expect(exc.api_backtrace).to be_nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
context 'api_backtrace is nil' do
|
59
|
+
let (:api_backtrace){ nil }
|
60
|
+
it do
|
61
|
+
exc = AlreadyExistsError.new(message, api_backtrace)
|
62
|
+
expect(exc).to be_an(AlreadyExistsError)
|
63
|
+
expect(exc.message).to eq message
|
64
|
+
expect(exc.api_backtrace).to be_nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
context 'conflict' do
|
68
|
+
it do
|
69
|
+
exc = AlreadyExistsError.new(message, api_backtrace, conflicts_with)
|
70
|
+
expect(exc).to be_an(AlreadyExistsError)
|
71
|
+
expect(exc.message).to eq message
|
72
|
+
expect(exc.api_backtrace).to eq api_backtrace
|
73
|
+
expect(exc.conflicts_with).to eq conflicts_with
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/spec/td/client/api_spec.rb
CHANGED
@@ -28,38 +28,44 @@ describe API do
|
|
28
28
|
h = {'key' => 1111111111111111111111111111111111}
|
29
29
|
unpacked = MessagePack.unpack(API.normalized_msgpack(h))
|
30
30
|
expect(unpacked['key']).to eq(h['key'].to_s)
|
31
|
+
|
32
|
+
h = {'key' => -1111111111111111111111111111111111}
|
33
|
+
unpacked = MessagePack.unpack(API.normalized_msgpack(h))
|
34
|
+
expect(unpacked['key']).to eq(h['key'].to_s)
|
31
35
|
end
|
32
36
|
|
33
37
|
it 'normalized_msgpack with out argument should convert Bignum into String' do
|
34
|
-
h = {'key' => 1111111111111111111111111111111111}
|
38
|
+
h = {'key' => 1111111111111111111111111111111111, 'key2' => -1111111111111111111111111111111111, 'key3' => 0}
|
35
39
|
out = ''
|
36
40
|
API.normalized_msgpack(h, out)
|
37
41
|
unpacked = MessagePack.unpack(out)
|
38
42
|
expect(unpacked['key']).to eq(h['key'].to_s)
|
43
|
+
expect(unpacked['key2']).to eq(h['key2'].to_s)
|
44
|
+
expect(unpacked['key3']).to eq(h['key3']) # don't touch non-too-big integer
|
39
45
|
end
|
40
46
|
|
41
47
|
it 'normalize_database_name should return normalized data' do
|
42
48
|
INVALID_NAMES.each_pair {|ng,ok|
|
43
|
-
API.normalize_database_name(ng).
|
49
|
+
expect(API.normalize_database_name(ng)).to eq(ok)
|
44
50
|
}
|
45
|
-
|
51
|
+
expect {
|
46
52
|
API.normalize_database_name('')
|
47
|
-
}.
|
53
|
+
}.to raise_error(RuntimeError)
|
48
54
|
end
|
49
55
|
|
50
56
|
it 'normalize_table_name should return normalized data' do
|
51
57
|
INVALID_NAMES.each_pair {|ng,ok|
|
52
|
-
API.normalize_table_name(ng).
|
58
|
+
expect(API.normalize_table_name(ng)).to eq(ok)
|
53
59
|
}
|
54
60
|
# empty
|
55
|
-
|
61
|
+
expect {
|
56
62
|
API.normalize_table_name('')
|
57
|
-
}.
|
63
|
+
}.to raise_error(RuntimeError)
|
58
64
|
end
|
59
65
|
|
60
66
|
it 'normalize_database_name should return valid data' do
|
61
67
|
VALID_NAMES.each {|ok|
|
62
|
-
API.normalize_database_name(ok).
|
68
|
+
expect(API.normalize_database_name(ok)).to eq(ok)
|
63
69
|
}
|
64
70
|
end
|
65
71
|
end
|
@@ -68,19 +74,19 @@ describe API do
|
|
68
74
|
describe "'validate_database_name'" do
|
69
75
|
it 'should raise a ParameterValidationError exceptions' do
|
70
76
|
INVALID_NAMES.each_pair {|ng,ok|
|
71
|
-
|
77
|
+
expect {
|
72
78
|
API.validate_database_name(ng)
|
73
|
-
}.
|
79
|
+
}.to raise_error(ParameterValidationError)
|
74
80
|
}
|
75
81
|
# empty
|
76
|
-
|
82
|
+
expect {
|
77
83
|
API.validate_database_name('')
|
78
|
-
}.
|
84
|
+
}.to raise_error(ParameterValidationError)
|
79
85
|
end
|
80
86
|
|
81
87
|
it 'should return valid data' do
|
82
88
|
VALID_NAMES.each {|ok|
|
83
|
-
API.validate_database_name(ok).
|
89
|
+
expect(API.validate_database_name(ok)).to eq(ok)
|
84
90
|
}
|
85
91
|
end
|
86
92
|
end
|
@@ -88,18 +94,18 @@ describe API do
|
|
88
94
|
describe "'validate_table_name'" do
|
89
95
|
it 'should raise a ParameterValidationError exception' do
|
90
96
|
INVALID_NAMES.each_pair {|ng,ok|
|
91
|
-
|
97
|
+
expect {
|
92
98
|
API.validate_table_name(ng)
|
93
|
-
}.
|
99
|
+
}.to raise_error(ParameterValidationError)
|
94
100
|
}
|
95
|
-
|
101
|
+
expect {
|
96
102
|
API.validate_table_name('')
|
97
|
-
}.
|
103
|
+
}.to raise_error(ParameterValidationError)
|
98
104
|
end
|
99
105
|
|
100
106
|
it 'should return valid data' do
|
101
107
|
VALID_NAMES.each {|ok|
|
102
|
-
API.validate_database_name(ok).
|
108
|
+
expect(API.validate_database_name(ok)).to eq(ok)
|
103
109
|
}
|
104
110
|
end
|
105
111
|
end
|
@@ -107,69 +113,95 @@ describe API do
|
|
107
113
|
describe "'validate_result_set_name'" do
|
108
114
|
it 'should raise a ParameterValidationError exception' do
|
109
115
|
INVALID_NAMES.each_pair {|ng,ok|
|
110
|
-
|
116
|
+
expect {
|
111
117
|
API.validate_result_set_name(ng)
|
112
|
-
}.
|
118
|
+
}.to raise_error(ParameterValidationError)
|
113
119
|
}
|
114
120
|
# empty
|
115
|
-
|
121
|
+
expect {
|
116
122
|
API.validate_result_set_name('')
|
117
|
-
}.
|
123
|
+
}.to raise_error(ParameterValidationError)
|
118
124
|
end
|
119
125
|
|
120
126
|
it 'should return valid data' do
|
121
127
|
VALID_NAMES.each {|ok|
|
122
|
-
API.validate_result_set_name(ok).
|
128
|
+
expect(API.validate_result_set_name(ok)).to eq(ok)
|
123
129
|
}
|
124
130
|
end
|
125
131
|
end
|
126
132
|
|
127
133
|
describe "'validate_column_name'" do
|
128
134
|
it 'should raise a ParameterValidationError exception' do
|
129
|
-
['
|
130
|
-
|
135
|
+
[''].each { |ng|
|
136
|
+
expect {
|
131
137
|
API.validate_column_name(ng)
|
132
|
-
}.
|
138
|
+
}.to raise_error(ParameterValidationError)
|
133
139
|
}
|
134
140
|
end
|
135
141
|
|
136
142
|
it 'should return valid data' do
|
137
143
|
VALID_NAMES.each {|ok|
|
138
|
-
API.validate_column_name(ok).
|
144
|
+
expect(API.validate_column_name(ok)).to eq(ok)
|
145
|
+
}
|
146
|
+
['a', 'a'*255].each {|ok|
|
147
|
+
expect(API.validate_column_name(ok)).to eq(ok)
|
139
148
|
}
|
140
|
-
# columns can be as short as 2 characters
|
141
|
-
API.validate_column_name('ab').should == 'ab'
|
142
149
|
end
|
143
150
|
end
|
144
151
|
|
152
|
+
describe "'validate_sql_alias_name'" do
|
153
|
+
it 'should raise a ParameterValidationError exception' do
|
154
|
+
[''].each { |ng|
|
155
|
+
expect{API.validate_sql_alias_name(ng)}.to raise_error(ParameterValidationError)
|
156
|
+
}
|
157
|
+
valid = ("a".."z").to_a.join<<("0".."9").to_a.join<<"_"
|
158
|
+
("\x00".."\x7F").each { |ng|
|
159
|
+
next if valid.include?(ng)
|
160
|
+
expect{API.validate_sql_alias_name(ng)}.to raise_error(ParameterValidationError)
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should return valid data' do
|
165
|
+
VALID_NAMES.each {|ok|
|
166
|
+
expect(API.validate_sql_alias_name(ok)).to eq(ok)
|
167
|
+
}
|
168
|
+
['a', '_a', 'a_', 'a'*512].each {|ok|
|
169
|
+
expect(API.validate_sql_alias_name(ok)).to eq(ok)
|
170
|
+
}
|
171
|
+
end
|
172
|
+
end
|
145
173
|
|
146
174
|
describe "'generic validate_name'" do
|
147
175
|
it 'should raise a ParameterValidationError exception' do
|
176
|
+
# wrong target
|
177
|
+
expect {
|
178
|
+
API.validate_name("", 3, 256, '')
|
179
|
+
}.to raise_error(ParameterValidationError)
|
148
180
|
INVALID_NAMES.each_pair {|ng,ok|
|
149
|
-
|
181
|
+
expect {
|
150
182
|
API.validate_name("generic", 3, 256, ng)
|
151
|
-
}.
|
183
|
+
}.to raise_error(ParameterValidationError)
|
152
184
|
}
|
153
185
|
# empty
|
154
|
-
|
186
|
+
expect {
|
155
187
|
API.validate_name("generic", 3, 256, '')
|
156
|
-
}.
|
188
|
+
}.to raise_error(ParameterValidationError)
|
157
189
|
# too short - one less than left limit
|
158
|
-
|
190
|
+
expect {
|
159
191
|
API.validate_name("generic", 3, 256, 'ab')
|
160
|
-
}.
|
192
|
+
}.to raise_error(ParameterValidationError)
|
161
193
|
end
|
162
194
|
|
163
195
|
it 'should return valid data' do
|
164
196
|
VALID_NAMES.each {|ok|
|
165
|
-
API.validate_name("generic", 3, 256, ok).
|
197
|
+
expect(API.validate_name("generic", 3, 256, ok)).to eq(ok)
|
166
198
|
}
|
167
199
|
# esplore left boundary
|
168
|
-
API.validate_name("generic", 2, 256, 'ab').
|
169
|
-
API.validate_name("generic", 1, 256, 'a').
|
200
|
+
expect(API.validate_name("generic", 2, 256, 'ab')).to eq('ab')
|
201
|
+
expect(API.validate_name("generic", 1, 256, 'a')).to eq('a')
|
170
202
|
# explore right boundary
|
171
|
-
API.validate_name("generic", 3, 256, 'a' * 256).
|
172
|
-
API.validate_name("generic", 3, 128, 'a' * 128).
|
203
|
+
expect(API.validate_name("generic", 3, 256, 'a' * 256)).to eq('a' * 256)
|
204
|
+
expect(API.validate_name("generic", 3, 128, 'a' * 128)).to eq('a' * 128)
|
173
205
|
end
|
174
206
|
end
|
175
207
|
|
@@ -178,7 +210,7 @@ describe API do
|
|
178
210
|
|
179
211
|
let(:api) { API.new(nil, endpoint: endpoint) }
|
180
212
|
let :packed do
|
181
|
-
s = StringIO.new
|
213
|
+
s = StringIO.new(String.new)
|
182
214
|
Zlib::GzipWriter.wrap(s) do |f|
|
183
215
|
f << ['hello', 'world'].to_json
|
184
216
|
end
|
@@ -195,7 +227,7 @@ describe API do
|
|
195
227
|
end
|
196
228
|
|
197
229
|
subject (:get_api_call) {
|
198
|
-
api.job_result_format(12345, 'json', StringIO.new)
|
230
|
+
api.job_result_format(12345, 'json', StringIO.new(String.new))
|
199
231
|
}
|
200
232
|
|
201
233
|
context 'without ssl' do
|
@@ -204,7 +236,7 @@ describe API do
|
|
204
236
|
let(:content_length) { {'Content-Length' => packed.size} }
|
205
237
|
|
206
238
|
it 'not called #completed_body?' do
|
207
|
-
api.
|
239
|
+
expect(api).not_to receive(:completed_body?)
|
208
240
|
|
209
241
|
get_api_call
|
210
242
|
end
|
@@ -223,21 +255,13 @@ describe API do
|
|
223
255
|
end
|
224
256
|
|
225
257
|
context 'with Content-Length' do
|
226
|
-
context '
|
258
|
+
context 'match Content-Length and body.size' do
|
227
259
|
let(:content_length) { {'Content-Length' => packed.size} }
|
228
260
|
|
229
261
|
it 'api accuess succeded' do
|
230
262
|
expect { get_api_call }.not_to raise_error
|
231
263
|
end
|
232
264
|
end
|
233
|
-
|
234
|
-
context 'not macth Content-Length and body.size' do
|
235
|
-
let(:content_length) { {'Content-Length' => packed.size + 1} }
|
236
|
-
|
237
|
-
it 'api accuess succeded' do
|
238
|
-
expect { get_api_call }.to raise_error(TreasureData::API::IncompleteError)
|
239
|
-
end
|
240
|
-
end
|
241
265
|
end
|
242
266
|
end
|
243
267
|
end
|