td-client 1.0.3 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f8123cb6d1ba413d6e482a5396d2675340256e1b
4
- data.tar.gz: 3e8541a2b1070b50dc270c54bb5f76a24e29cd24
2
+ SHA256:
3
+ metadata.gz: 8650b4fe6267d1edb91dad045ac0b31f77d0d0e37d776ea5433ec88debfe4b81
4
+ data.tar.gz: baf35bbd09b187c8f76ee48f68328f23ac09721ddb31641154a97900dc4ac93b
5
5
  SHA512:
6
- metadata.gz: 7bdbacf95cd257c635dd6d72486b32974408d9f09e4f81e1ec1233c4db0b709bbb0b48991c2820c80d1a94c305295b7809771dbc34ca5e8b0e885a1146053277
7
- data.tar.gz: 901e31f20e95e0aa83fabaa4daf18470376aee5396cbe09cad474e5680ab1e56fee43ceae3e466b91f92d1b60b7de1a4b2ad777e86e309807bf3d5a733007fcd
6
+ metadata.gz: 643dc4c3d7ec6e5e73b506f3b18750a7aeaf73e915890c68ccd7b590f8d539a1fb047f5adbb4652bb0a732f2e8466658be112384d9d57aae1cb72b902b8a4b46
7
+ data.tar.gz: fbf3228a704dd954299becd06462920f72934674d8cae49ba1db91d169441fe4a079348e33ec8f2e3b08c986b5383fbf476f7161de8711a3677583da1b4b3169
data/lib/td/client.rb CHANGED
@@ -88,9 +88,14 @@ class Client
88
88
  raise NotFoundError, "Database '#{db_name}' does not exist"
89
89
  end
90
90
 
91
+ # @param [String] db
92
+ # @param [String] table
93
+ # @option params [Fixnum] :expire_days days to expire table
94
+ # @option params [Boolean] :include_v (true) include v column on Hive
95
+ # @option params [Boolean] :detect_schema (true) detect schema on import
91
96
  # @return [true]
92
- def create_log_table(db_name, table_name)
93
- @api.create_log_table(db_name, table_name)
97
+ def create_log_table(db_name, table_name, params={})
98
+ @api.create_log_table(db_name, table_name, params)
94
99
  end
95
100
 
96
101
  # Swap table names
@@ -111,6 +116,16 @@ class Client
111
116
  @api.update_schema(db_name, table_name, schema.to_json)
112
117
  end
113
118
 
119
+ # @param [String] db
120
+ # @param [String] table
121
+ # @option params [Fixnum] :expire_days days to expire table
122
+ # @option params [Boolean] :include_v (true) include v column on Hive
123
+ # @option params [Boolean] :detect_schema (true) detect schema on import
124
+ # @return [true]
125
+ def update_table(db_name, table_name, params={})
126
+ @api.update_table(db_name, table_name, params)
127
+ end
128
+
114
129
  # @param [String] db_name
115
130
  # @param [String] table_name
116
131
  # @param [Fixnum] expire_days
@@ -130,10 +145,10 @@ class Client
130
145
  # @return [Array] Tables
131
146
  def tables(db_name)
132
147
  m = @api.list_tables(db_name)
133
- m.map {|table_name, (type, schema, count, created_at, updated_at, estimated_storage_size, last_import, last_log_timestamp, expire_days)|
148
+ m.map {|table_name, (type, schema, count, created_at, updated_at, estimated_storage_size, last_import, last_log_timestamp, expire_days, include_v)|
134
149
  schema = Schema.new.from_json(schema)
135
150
  Table.new(self, db_name, table_name, type, schema, count, created_at, updated_at,
136
- estimated_storage_size, last_import, last_log_timestamp, expire_days)
151
+ estimated_storage_size, last_import, last_log_timestamp, expire_days, include_v)
137
152
  }
138
153
  end
139
154
 
@@ -158,6 +173,14 @@ class Client
158
173
  @api.tail(db_name, table_name, count, to, from, &block)
159
174
  end
160
175
 
176
+ # @param [String] db_name
177
+ # @param [String] table_name
178
+ # @param [String] new_db_name
179
+ # @return [true]
180
+ def change_database(db_name, table_name, new_db_name)
181
+ @api.change_database(db_name, table_name, new_db_name)
182
+ end
183
+
161
184
  # @param [String] db_name
162
185
  # @param [String] q
163
186
  # @param [String] result_url
@@ -519,39 +542,6 @@ class Client
519
542
  @api.change_my_password(old_password, password)
520
543
  end
521
544
 
522
- # @return [Array<AccessControl>]
523
- def access_controls
524
- list = @api.list_access_controls
525
- list.map {|subject,action,scope,grant_option|
526
- AccessControl.new(self, subject, action, scope, grant_option)
527
- }
528
- end
529
-
530
- # @param [String] subject
531
- # @param [String] action
532
- # @param [String] scope
533
- # @param [Array] grant_option
534
- # @return [true]
535
- def grant_access_control(subject, action, scope, grant_option)
536
- @api.grant_access_control(subject, action, scope, grant_option)
537
- end
538
-
539
- # @param [String] subject
540
- # @param [String] action
541
- # @param [String] scope
542
- # @return [true]
543
- def revoke_access_control(subject, action, scope)
544
- @api.revoke_access_control(subject, action, scope)
545
- end
546
-
547
- # @param [String] user
548
- # @param [String] action
549
- # @param [String] scope
550
- # @return [Array]
551
- def test_access_control(user, action, scope)
552
- @api.test_access_control(user, action, scope)
553
- end
554
-
555
545
  # => BulkLoad::Job
556
546
  def bulk_load_guess(job)
557
547
  @api.bulk_load_guess(job)
data/lib/td/client/api.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'td/client/api_error'
2
2
  require 'td/client/version'
3
- require 'td/client/api/access_control'
4
3
  require 'td/client/api/account'
5
4
  require 'td/client/api/bulk_import'
6
5
  require 'td/client/api/bulk_load'
@@ -21,7 +20,6 @@ require 'td/core_ext/openssl/ssl/sslcontext/set_params'
21
20
  module TreasureData
22
21
 
23
22
  class API
24
- include API::AccessControl
25
23
  include API::Account
26
24
  include API::BulkImport
27
25
  include API::BulkLoad
@@ -638,15 +636,27 @@ private
638
636
  klass
639
637
  else
640
638
  case status_code
639
+ when "400"
640
+ BadRequestError
641
+ when "401"
642
+ AuthError
643
+ when "403"
644
+ ForbiddenError
641
645
  when "404"
642
646
  NotFoundError
647
+ when "405"
648
+ MethodNotAllowedError
643
649
  when "409"
644
650
  message = "#{message}: conflicts_with job:#{error["details"]["conflicts_with"]}" if error["details"] && error["details"]["conflicts_with"]
645
651
  AlreadyExistsError
646
- when "401"
647
- AuthError
648
- when "403"
649
- ForbiddenError
652
+ when "415"
653
+ UnsupportedMediaTypeError
654
+ when "422"
655
+ UnprocessableEntityError
656
+ when "429"
657
+ TooManyRequestsError
658
+ when /\A4\d\d\z/
659
+ ClientError
650
660
  else
651
661
  message = "#{status_code}: #{message}"
652
662
  APIError
@@ -28,7 +28,7 @@ module Export
28
28
  # @return [String] job_id
29
29
  def result_export(target_job_id, opts={})
30
30
  code, body, res = post("/v3/job/result_export/#{target_job_id}", opts)
31
- if code != "200"
31
+ if code[0] != ?2
32
32
  raise_error("Result Export failed", res)
33
33
  end
34
34
  js = checked_json(body, %w[job_id])
@@ -29,9 +29,7 @@ module Import
29
29
  if code[0] != ?2
30
30
  raise_error("Import failed", res)
31
31
  end
32
- js = checked_json(body, %w[])
33
- time = js['elapsed_time'].to_f
34
- return time
32
+ return true
35
33
  end
36
34
 
37
35
  end
@@ -6,7 +6,7 @@ module Job
6
6
  ##
7
7
 
8
8
  # @param [Fixnum] from
9
- # @param [Fixnum] to
9
+ # @param [Fixnum] to (to is inclusive)
10
10
  # @param [String] status
11
11
  # @param [Hash] conditions
12
12
  # @return [Array]
@@ -67,6 +67,8 @@ module Job
67
67
  num_records = js['num_records']
68
68
  duration = js['duration']
69
69
  result = js['result'] # result target URL
70
+ linked_result_export_job_id = js['linked_result_export_job_id']
71
+ result_export_target_job_id = js['result_export_target_job_id']
70
72
  hive_result_schema = (js['hive_result_schema'] || '')
71
73
  if hive_result_schema.empty?
72
74
  hive_result_schema = nil
@@ -100,7 +102,8 @@ module Job
100
102
  priority = js['priority']
101
103
  retry_limit = js['retry_limit']
102
104
  return [type, query, status, url, debug, start_at, end_at, cpu_time,
103
- result_size, result, hive_result_schema, priority, retry_limit, nil, database, duration, num_records]
105
+ result_size, result, hive_result_schema, priority, retry_limit, nil, database, duration, num_records,
106
+ linked_result_export_job_id, result_export_target_job_id]
104
107
  end
105
108
 
106
109
  # @param [String] job_id
@@ -313,8 +316,12 @@ module Job
313
316
  current_total_chunk_size = 0
314
317
  infl = nil
315
318
  begin # LOOP of Network/Server errors
319
+ first_chunk_p = true
316
320
  response = client.get(url, params, header) do |res, chunk|
317
- validate_response_status(res, current_total_chunk_size)
321
+ # Validate only on first chunk
322
+ validate_response_status(res, current_total_chunk_size) if first_chunk_p
323
+ first_chunk_p = false
324
+
318
325
  if infl.nil? && autodecode
319
326
  case res.header['Content-Encoding'][0].to_s.downcase
320
327
  when 'gzip'
@@ -66,7 +66,7 @@ module Schedule
66
66
 
67
67
  # @param [String] name
68
68
  # @param [Fixnum] from
69
- # @param [Fixnum] to
69
+ # @param [Fixnum] to (to is exclusive)
70
70
  # @return [Array]
71
71
  def history(name, from=0, to=nil)
72
72
  params = {}
@@ -25,16 +25,18 @@ module Table
25
25
  estimated_storage_size = m['estimated_storage_size'].to_i
26
26
  schema = JSON.parse(m['schema'] || '[]')
27
27
  expire_days = m['expire_days']
28
- result[name] = [type, schema, count, created_at, updated_at, estimated_storage_size, last_import, last_log_timestamp, expire_days]
28
+ include_v = m['include_v']
29
+ result[name] = [type, schema, count, created_at, updated_at, estimated_storage_size, last_import, last_log_timestamp, expire_days, include_v]
29
30
  }
30
31
  return result
31
32
  end
32
33
 
33
34
  # @param [String] db
34
35
  # @param [String] table
36
+ # @param [Hash] params
35
37
  # @return [true]
36
- def create_log_table(db, table)
37
- create_table(db, table, :log)
38
+ def create_log_table(db, table, params={})
39
+ create_table(db, table, :log, params)
38
40
  end
39
41
 
40
42
  # @param [String] db
@@ -81,9 +83,19 @@ module Table
81
83
  # @param [Fixnum] expire_days
82
84
  # @return [true]
83
85
  def update_expire(db, table, expire_days)
84
- code, body, res = post("/v3/table/update/#{e db}/#{e table}", {'expire_days'=>expire_days})
86
+ update_table(db, table, {:expire_days=>expire_days})
87
+ end
88
+
89
+ # @param [String] db
90
+ # @param [String] table
91
+ # @option params [Fixnum] :expire_days days to expire table
92
+ # @option params [Boolean] :include_v (true) include v column on Hive
93
+ # @option params [Boolean] :detect_schema (true) detect schema on import
94
+ # @return [true]
95
+ def update_table(db, table, params={})
96
+ code, body, res = post("/v3/table/update/#{e db}/#{e table}", params)
85
97
  if code != "200"
86
- raise_error("Update table expiration failed", res)
98
+ raise_error("Update table failed", res)
87
99
  end
88
100
  return true
89
101
  end
@@ -128,5 +140,14 @@ module Table
128
140
  end
129
141
  end
130
142
 
143
+ def change_database(db, table, dest_db)
144
+ params = { 'dest_database_name' => dest_db }
145
+ code, body, res = post("/v3/table/change_database/#{e db}/#{e table}", params)
146
+ if code != "200"
147
+ raise_error("Change database failed", res)
148
+ end
149
+ return true
150
+ end
151
+
131
152
  end
132
153
  end
@@ -13,16 +13,32 @@ class APIError < StandardError
13
13
  end
14
14
  end
15
15
 
16
- # 401 API errors
17
- class AuthError < APIError
16
+ # 4xx Client Errors
17
+ class ClientError < APIError
18
18
  end
19
19
 
20
- # 403 API errors, used for database permissions
21
- class ForbiddenError < APIError
20
+ # 400 Bad Request
21
+ class BadRequestError < ClientError
22
22
  end
23
23
 
24
- # 409 API errors
25
- class AlreadyExistsError < APIError
24
+ # 401 Unauthorized
25
+ class AuthError < ClientError
26
+ end
27
+
28
+ # 403 Forbidden, used for database permissions
29
+ class ForbiddenError < ClientError
30
+ end
31
+
32
+ # 404 Not Found
33
+ class NotFoundError < ClientError
34
+ end
35
+
36
+ # 405 Method Not Allowed
37
+ class MethodNotAllowedError < ClientError
38
+ end
39
+
40
+ # 409 Conflict
41
+ class AlreadyExistsError < ClientError
26
42
  attr_reader :conflicts_with
27
43
  def initialize(error_message = nil, api_backtrace = nil, conflicts_with=nil)
28
44
  super(error_message, api_backtrace)
@@ -30,8 +46,16 @@ class AlreadyExistsError < APIError
30
46
  end
31
47
  end
32
48
 
33
- # 404 API errors
34
- class NotFoundError < APIError
49
+ # 415 Unsupported Media Type
50
+ class UnsupportedMediaTypeError < ClientError
51
+ end
52
+
53
+ # 422 Unprocessable Entity
54
+ class UnprocessableEntityError < ClientError
55
+ end
56
+
57
+ # 429 Too Many Requests
58
+ class TooManyRequestsError < ClientError
35
59
  end
36
60
 
37
61
  end
@@ -153,7 +153,7 @@ class Table < Model
153
153
  # @param [String] last_import
154
154
  # @param [String] last_log_timestamp
155
155
  # @param [Fixnum, String] expire_days
156
- def initialize(client, db_name, table_name, type, schema, count, created_at=nil, updated_at=nil, estimated_storage_size=nil, last_import=nil, last_log_timestamp=nil, expire_days=nil)
156
+ def initialize(client, db_name, table_name, type, schema, count, created_at=nil, updated_at=nil, estimated_storage_size=nil, last_import=nil, last_log_timestamp=nil, expire_days=nil, include_v=false)
157
157
  super(client)
158
158
  @database = nil
159
159
  @db_name = db_name
@@ -167,6 +167,7 @@ class Table < Model
167
167
  @last_import = last_import
168
168
  @last_log_timestamp = last_log_timestamp
169
169
  @expire_days = expire_days
170
+ @include_v = include_v
170
171
  end
171
172
 
172
173
  # @!attribute [r] type
@@ -175,7 +176,7 @@ class Table < Model
175
176
  # @!attribute [r] schema
176
177
  # @!attribute [r] count
177
178
  # @!attribute [r] estimated_storage_size
178
- attr_reader :type, :db_name, :table_name, :schema, :count, :estimated_storage_size
179
+ attr_reader :type, :db_name, :table_name, :schema, :count, :estimated_storage_size, :include_v
179
180
 
180
181
  alias database_name db_name
181
182
  alias name table_name
@@ -425,6 +426,7 @@ class Job < Model
425
426
  @db_name = db_name
426
427
  @duration = duration
427
428
  @num_records = num_records
429
+ @auto_update_status = true
428
430
  end
429
431
 
430
432
  # @!attribute [r] job_id
@@ -440,6 +442,16 @@ class Job < Model
440
442
  attr_reader :priority, :retry_limit, :org_name, :db_name
441
443
  attr_reader :duration, :num_records
442
444
 
445
+ # whether it update status if the job is not finished yet or not
446
+ def auto_update_status?
447
+ @auto_update_status
448
+ end
449
+
450
+ # set whether it update status if the job is not finished yet or not
451
+ def auto_update_status=(bool)
452
+ @auto_update_status = bool ? true : false
453
+ end
454
+
443
455
  # @option timeout [Integer,nil] timeout in second
444
456
  # @option wait_interval [Integer,nil] interval in second of polling the job status
445
457
  # @param detail [Boolean] update job detail or not
@@ -478,55 +490,55 @@ class Job < Model
478
490
 
479
491
  # @return [String]
480
492
  def query
481
- update_status! unless @query || finished?
493
+ update_status! unless @query || !@auto_update_status || finished?
482
494
  @query
483
495
  end
484
496
 
485
497
  # @return [String]
486
498
  def status
487
- update_status! unless @status || finished?
499
+ update_status! unless @status || !@auto_update_status || finished?
488
500
  @status
489
501
  end
490
502
 
491
503
  # @return [String]
492
504
  def url
493
- update_status! unless @url || finished?
505
+ update_status! unless @url || !@auto_update_status || finished?
494
506
  @url
495
507
  end
496
508
 
497
509
  # @return [Boolean]
498
510
  def debug
499
- update_status! unless @debug || finished?
511
+ update_status! unless @debug || !@auto_update_status || finished?
500
512
  @debug
501
513
  end
502
514
 
503
515
  # @return [Time, nil]
504
516
  def start_at
505
- update_status! unless @start_at || finished?
517
+ update_status! unless @start_at || !@auto_update_status || finished?
506
518
  @start_at && !@start_at.empty? ? Time.parse(@start_at) : nil
507
519
  end
508
520
 
509
521
  # @return [Time, nil]
510
522
  def end_at
511
- update_status! unless @end_at || finished?
523
+ update_status! unless @end_at || !@auto_update_status || finished?
512
524
  @end_at && !@end_at.empty? ? Time.parse(@end_at) : nil
513
525
  end
514
526
 
515
527
  # @return [String]
516
528
  def cpu_time
517
- update_status! unless @cpu_time || finished?
529
+ update_status! unless @cpu_time || !@auto_update_status || finished?
518
530
  @cpu_time
519
531
  end
520
532
 
521
533
  # @return [Array]
522
534
  def hive_result_schema
523
- update_status! unless @hive_result_schema.instance_of? Array || finished?
535
+ update_status! unless @hive_result_schema.instance_of?(Array) || !@auto_update_status || finished?
524
536
  @hive_result_schema
525
537
  end
526
538
 
527
539
  # @return [String]
528
540
  def result_size
529
- update_status! unless @result_size || finished?
541
+ update_status! unless @result_size || !@auto_update_status || finished?
530
542
  @result_size
531
543
  end
532
544
 
@@ -1,5 +1,5 @@
1
1
  module TreasureData
2
2
  class Client
3
- VERSION = '1.0.3'
3
+ VERSION = '1.0.8'
4
4
  end
5
5
  end
data/spec/spec_helper.rb CHANGED
@@ -49,11 +49,7 @@ shared_context 'common helper' do
49
49
  end
50
50
 
51
51
  let :headers do
52
- if RUBY_VERSION >= "2.0.0"
53
- {'Accept' => '*/*', 'Accept-Encoding' => /gzip/, 'Date' => /.*/, 'User-Agent' => /Ruby/}
54
- else
55
- {'Accept' => '*/*', 'Date' => /.*/, 'User-Agent' => /Ruby/}
56
- end
52
+ {'Accept' => '*/*', 'Accept-Encoding' => /gzip/, 'Date' => /.*/, 'User-Agent' => /Ruby/}
57
53
  end
58
54
 
59
55
  def stub_api_request(method, path, opts = nil)
@@ -39,29 +39,30 @@ describe 'API SSL connection' do
39
39
  api = API.new(nil, :endpoint => "https://localhost:#{@serverport}", :retry_post_requests => false)
40
40
  api.ssl_ca_file = File.join(DIR, 'ca-all.cert')
41
41
  expect {
42
- api.delete_database('no_such_database')
42
+ begin
43
+ api.delete_database('no_such_database')
44
+ rescue Errno::ECONNRESET
45
+ raise OpenSSL::SSL::SSLError # When openssl does not support SSLv3, httpclient server will not start. For context: https://github.com/nahi/httpclient/pull/424#issuecomment-731714786
46
+ end
43
47
  }.to raise_error OpenSSL::SSL::SSLError
44
48
  end
45
49
 
46
- # 1.8.7's net/http does not call SSLContext#set_params. Use 1.8.7 where you don't care security.
47
- if RUBY_VERSION >= '1.9.0'
48
- it 'should success to connect TLSv1 only server' do
49
- @server = setup_server(:TLSv1)
50
- api = API.new(nil, :endpoint => "https://localhost:#{@serverport}", :retry_post_requests => false)
51
- api.ssl_ca_file = File.join(DIR, 'ca-all.cert')
52
- expect {
53
- api.delete_database('no_such_database')
54
- }.to raise_error TreasureData::NotFoundError
55
- end
50
+ it 'should success to connect TLSv1 only server' do
51
+ @server = setup_server(:TLSv1)
52
+ api = API.new(nil, :endpoint => "https://localhost:#{@serverport}", :retry_post_requests => false)
53
+ api.ssl_ca_file = File.join(DIR, 'ca-all.cert')
54
+ expect {
55
+ api.delete_database('no_such_database')
56
+ }.to raise_error TreasureData::NotFoundError
56
57
  end
57
58
 
58
- def setup_server(ssl_version)
59
+ def setup_server(ssl_version, port = 1000 + rand(1000))
59
60
  logger = Logger.new(STDERR)
60
61
  logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)
61
62
  @server = WEBrick::HTTPServer.new(
62
63
  :BindAddress => "localhost",
63
64
  :Logger => logger,
64
- :Port => 0,
65
+ :Port => port,
65
66
  :AccessLog => [],
66
67
  :DocumentRoot => '.',
67
68
  :SSLEnable => true,
@@ -46,9 +46,9 @@ describe 'Import API' do
46
46
  end
47
47
  stub_request(:put, "https://#{endpoint_import}/v3/table/import_with_id/db/table/unique_id/format").
48
48
  with(:body => '12345').
49
- to_return(:body => '{"elapsed_time":"1.23"}')
49
+ to_return(:status => 200)
50
50
  File.open(t.path) do |f|
51
- expect(api.import('db', 'table', 'format', f, 5, 'unique_id')).to eq(1.23)
51
+ expect(api.import('db', 'table', 'format', f, 5, 'unique_id')).to eq(true)
52
52
  end
53
53
  end
54
54
 
@@ -59,9 +59,9 @@ describe 'Import API' do
59
59
  end
60
60
  stub_request(:put, "https://#{endpoint_import}/v3/table/import/db/table/format").
61
61
  with(:body => '12345').
62
- to_return(:body => '{"elapsed_time":"1.23"}')
62
+ to_return(:status => 200)
63
63
  File.open(t.path) do |f|
64
- expect(api.import('db', 'table', 'format', f, 5)).to eq(1.23)
64
+ expect(api.import('db', 'table', 'format', f, 5)).to eq(true)
65
65
  end
66
66
  end
67
67
 
@@ -72,9 +72,9 @@ describe 'Import API' do
72
72
  end
73
73
  stub_request(:put, "http://#{endpoint_import_old}/v3/table/import/db/table/format").
74
74
  with(:body => '12345').
75
- to_return(:body => '{"elapsed_time":"1.23"}')
75
+ to_return(:status => 200)
76
76
  File.open(t.path) do |f|
77
- expect(api_old.import('db', 'table', 'format', f, 5)).to eq(1.23)
77
+ expect(api_old.import('db', 'table', 'format', f, 5)).to eq(true)
78
78
  end
79
79
  end
80
80
 
@@ -85,9 +85,9 @@ describe 'Import API' do
85
85
  end
86
86
  stub_request(:put, "https://#{endpoint_import}/v3/table/import/db/table/format").
87
87
  with(:body => '12345').
88
- to_return(:body => '{"elapsed_time":"1.23"}')
88
+ to_return(:status => 200)
89
89
  File.open(t.path) do |f|
90
- expect(api_default.import('db', 'table', 'format', f, 5)).to eq 1.23
90
+ expect(api_default.import('db', 'table', 'format', f, 5)).to eq true
91
91
  end
92
92
  end
93
93
 
@@ -98,9 +98,9 @@ describe 'Import API' do
98
98
  end
99
99
  stub_request(:put, "http://#{endpoint_import}/v3/table/import/db/table/format").
100
100
  with(:body => '12345').
101
- to_return(:body => '{"elapsed_time":"1.23"}')
101
+ to_return(:status => 200)
102
102
  File.open(t.path) do |f|
103
- expect(api_default_http.import('db', 'table', 'format', f, 5)).to eq 1.23
103
+ expect(api_default_http.import('db', 'table', 'format', f, 5)).to eq true
104
104
  end
105
105
  end
106
106
 
@@ -111,9 +111,9 @@ describe 'Import API' do
111
111
  end
112
112
  stub_request(:put, "https://#{endpoint_unknown}/v3/table/import/db/table/format").
113
113
  with(:body => '12345').
114
- to_return(:body => '{"elapsed_time":"1.23"}')
114
+ to_return(:status => 200)
115
115
  File.open(t.path) do |f|
116
- expect(api_unknown_host.import('db', 'table', 'format', f, 5)).to eq 1.23
116
+ expect(api_unknown_host.import('db', 'table', 'format', f, 5)).to eq true
117
117
  end
118
118
  end
119
119
 
@@ -124,9 +124,9 @@ describe 'Import API' do
124
124
  end
125
125
  stub_request(:put, "http://#{endpoint_unknown}/v3/table/import/db/table/format").
126
126
  with(:body => '12345').
127
- to_return(:body => '{"elapsed_time":"1.23"}')
127
+ to_return(:status => 200)
128
128
  File.open(t.path) do |f|
129
- expect(api_unknown_host_http.import('db', 'table', 'format', f, 5)).to eq 1.23
129
+ expect(api_unknown_host_http.import('db', 'table', 'format', f, 5)).to eq true
130
130
  end
131
131
  end
132
132
 
@@ -31,7 +31,7 @@ describe 'Job API' do
31
31
  jobs = api.list_jobs
32
32
  jobs[i..i].map {|job_id, type, status, query, start_at, end_at, cpu_time,
33
33
  result_size, result_url, priority, retry_limit, org, db,
34
- duration, num_records|
34
+ duration, num_records, linked_result_export_job_id, result_export_target_job_id|
35
35
  expect(job_id).to eq(job['job_id'])
36
36
  expect(type).to eq(job['type'])
37
37
  expect(status).to eq(job['status'])
@@ -47,6 +47,8 @@ describe 'Job API' do
47
47
  expect(db).to eq(job['database'])
48
48
  expect(duration).to eq(job['duration'])
49
49
  expect(num_records).to eq(job['num_records'])
50
+ expect(linked_result_export_job_id).to eq(job['linked_result_export_job_id'])
51
+ expect(result_export_target_job_id).to eq(job['result_export_target_job_id'])
50
52
  }
51
53
  end
52
54
  }
@@ -341,6 +343,21 @@ describe 'Job API' do
341
343
  expect(api.job_result(12345)).to eq ['hello', 'world']
342
344
  end
343
345
 
346
+ it 'has multiple chunks' do
347
+ client = double('client')
348
+ allow(api).to receive(:new_client).and_return([client, {}])
349
+ allow(client).to receive(:send_timeout=)
350
+ allow(client).to receive(:receive_timeout=)
351
+ res = HTTP::Message.new_response(nil, HTTP::Message::Headers.new)
352
+ sz = 3
353
+ chunk1 = packed[0, sz]
354
+ chunk2 = packed[sz, packed.bytesize-sz]
355
+ expect(client).to receive(:get).
356
+ and_yield(res, chunk1).
357
+ and_yield(res, chunk2).
358
+ and_return(res)
359
+ expect(api.job_result(12345)).to eq ['hello', 'world']
360
+ end
344
361
  end
345
362
 
346
363
  describe 'job_result_format' do
@@ -35,6 +35,56 @@ describe 'Job Model' do
35
35
  end
36
36
  end
37
37
 
38
+ describe '#auto_update_status' do
39
+ let(:client) { Client.authenticate('user', 'password') }
40
+ let(:job_id) { 12345678 }
41
+ let(:job) { Job.new(client, job_id, nil, nil) }
42
+ let(:format) { 'json' }
43
+ let(:io) { StringIO.new }
44
+ before { allow(job).to receive(:finished?) { false } }
45
+
46
+ it 'can set' do
47
+ expect(job.auto_update_status?).to eq true
48
+ job.auto_update_status = false
49
+ expect(job.auto_update_status?).to eq false
50
+ job.auto_update_status = true
51
+ expect(job.auto_update_status?).to eq true
52
+ end
53
+
54
+ it 'calls API if auto_update_status=true' do
55
+ job.auto_update_status = true
56
+ result_job = {
57
+ 'job_id' => job_id,
58
+ 'status' => 'queued',
59
+ 'created_at' => Time.now,
60
+ }
61
+ stub_request(:get, "https://api.treasuredata.com/v3/job/show/#{job_id}").
62
+ to_return(:body => result_job.to_json)
63
+ expect(job.query).to be_nil
64
+ expect(job.status).to eq "queued"
65
+ expect(job.url).to be_nil
66
+ expect(job.debug).to be_nil
67
+ expect(job.start_at).to be_nil
68
+ expect(job.end_at).to be_nil
69
+ expect(job.cpu_time).to be_nil
70
+ expect(job.hive_result_schema).to be_nil
71
+ expect(job.result_size).to be_nil
72
+ end
73
+
74
+ it "doesn't call API if auto_update_status=false" do
75
+ job.auto_update_status = false
76
+ expect(job.query).to be_nil
77
+ expect(job.status).to be_nil
78
+ expect(job.url).to be_nil
79
+ expect(job.debug).to be_nil
80
+ expect(job.start_at).to be_nil
81
+ expect(job.end_at).to be_nil
82
+ expect(job.cpu_time).to be_nil
83
+ expect(job.hive_result_schema).to be_nil
84
+ expect(job.result_size).to be_nil
85
+ end
86
+ end
87
+
38
88
  describe '#result_raw' do
39
89
  let(:client) { Client.authenticate('user', 'password') }
40
90
  let(:job_id) { 12345678 }
@@ -90,15 +140,16 @@ describe 'Job Model' do
90
140
  end
91
141
 
92
142
  it 'calls a given block in every wait_interval second' do
93
- now = 1_400_000_000
94
- allow(self).to receive(:sleep){|arg| now += arg }
95
- allow(Process).to receive(:clock_gettime){ now }
143
+ # Let's try disable stubbing #sleep for now
144
+ # now = 1_400_000_000
145
+ # allow(self).to receive(:sleep){|arg| now += arg }
146
+ # allow(Process).to receive(:clock_gettime){ now }
96
147
  expect { |b|
97
148
  begin
98
149
  thread = Thread.start {
99
- job.wait(nil, 2, &b)
150
+ job.wait(nil, 0.1, &b)
100
151
  }
101
- sleep 6
152
+ sleep 0.3
102
153
  change_job_status(Job::STATUS_SUCCESS)
103
154
  thread.join(1)
104
155
  expect(thread).to be_stop
@@ -30,6 +30,13 @@ describe 'Table API' do
30
30
  expect(api.create_log_table(db_name, table_name)).to be true
31
31
  end
32
32
 
33
+ it 'should create a new table with params' do
34
+ stub_api_request(:post, "/v3/table/create/#{e db_name}/#{e(table_name)}/log").
35
+ with(:body => {'include_v' => 'false'}).
36
+ to_return(:body => {'database' => db_name, 'table' => table_name, 'type' => 'log', 'include_v' => 'false'}.to_json)
37
+ expect(api.create_log_table(db_name, table_name, include_v: false)).to be true
38
+ end
39
+
33
40
  it 'should return 400 error with invalid name' do
34
41
  invalid_name = 'a'
35
42
  err_msg = "Name must be 3 to 256 characters, got #{invalid_name.length} characters. name = '#{invalid_name}'"
@@ -52,6 +59,52 @@ describe 'Table API' do
52
59
  end
53
60
  end
54
61
 
62
+ describe "'create_log_table' client API" do
63
+ it 'should return 404 error if the database does not exist' do
64
+ err_msg = "Create log table failed: Couldn't find UserDatabase with name = #{db_name}"
65
+ stub_api_request(:post, "/v3/table/create/#{e db_name}/#{e(table_name)}/log").
66
+ to_return(:status => 404, :body => {'message' => err_msg}.to_json)
67
+
68
+ expect {
69
+ client.create_log_table(db_name, table_name)
70
+ }.to raise_error(TreasureData::NotFoundError, /#{err_msg}/)
71
+ end
72
+
73
+ it 'should create a new table if the database exists' do
74
+ stub_api_request(:post, "/v3/table/create/#{e db_name}/#{e(table_name)}/log").
75
+ to_return(:body => {'database' => db_name, 'table' => table_name, 'type' => 'log'}.to_json)
76
+ expect(client.create_log_table(db_name, table_name)).to be true
77
+ end
78
+
79
+ it 'should create a new table with params' do
80
+ stub_api_request(:post, "/v3/table/create/#{e db_name}/#{e(table_name)}/log").
81
+ with(:body => {'include_v' => 'false'}).
82
+ to_return(:body => {'database' => db_name, 'table' => table_name, 'type' => 'log', 'include_v' => 'false'}.to_json)
83
+ expect(client.create_log_table(db_name, table_name, include_v: false)).to be true
84
+ end
85
+
86
+ it 'should return 400 error with invalid name' do
87
+ invalid_name = 'a'
88
+ err_msg = "Name must be 3 to 256 characters, got #{invalid_name.length} characters. name = '#{invalid_name}'"
89
+ stub_api_request(:post, "/v3/table/create/#{e db_name}/#{e invalid_name}/log").
90
+ to_return(:status => 400, :body => {'message' => err_msg}.to_json)
91
+
92
+ expect {
93
+ client.create_log_table(db_name, invalid_name)
94
+ }.to raise_error(TreasureData::APIError, /#{err_msg}/)
95
+ end
96
+
97
+ it 'should return 409 error with duplicated name' do
98
+ err_msg = "Table #{table_name} already exists"
99
+ stub_api_request(:post, "/v3/table/create/#{e db_name}/#{e table_name}/log").
100
+ to_return(:status => 409, :body => {'message' => err_msg}.to_json)
101
+
102
+ expect {
103
+ client.create_log_table(db_name, table_name)
104
+ }.to raise_error(TreasureData::AlreadyExistsError, /#{err_msg}/)
105
+ end
106
+ end
107
+
55
108
  describe "'list_tables' API" do
56
109
  it 'should list the tables in a Hash whose values include type, count, created_at, updated_at, schema, ...' do
57
110
  tables = [
@@ -179,6 +232,31 @@ describe 'Table API' do
179
232
  end
180
233
  end
181
234
 
235
+ describe 'handle include_v' do
236
+ it 'should set/unset include_v flag' do
237
+ stub_api_request(:get, '/v3/table/list/db').
238
+ to_return(:body => {'tables' => [
239
+ {'name' => 'table', 'type' => 'log', 'include_v' => true},
240
+ ]}.to_json)
241
+
242
+ table = client.table('db', 'table')
243
+ expect(table.include_v).to eq true
244
+
245
+ stub_api_request(:get, '/v3/table/list/db').
246
+ to_return(:body => {'tables' => [
247
+ {'name' => 'table', 'type' => 'log', 'include_v' => false},
248
+ ]}.to_json)
249
+
250
+ stub_api_request(:post, '/v3/table/update/db/table').
251
+ with(:body => {'include_v' => "false"}).
252
+ to_return(:body => {"database"=>"db","table"=>"table","type"=>"log"}.to_json)
253
+ api.update_table('db', 'table', include_v: "false")
254
+
255
+ table = client.table('db', 'table')
256
+ expect(table.include_v).to eq false
257
+ end
258
+ end
259
+
182
260
  describe 'tail' do
183
261
  let :packed do
184
262
  s = StringIO.new
@@ -223,4 +301,43 @@ describe 'Table API' do
223
301
  expect(r.read).to eq(%Q(parameter "to" and "from" no longer work\n))
224
302
  end
225
303
  end
304
+
305
+ describe 'change_database' do
306
+ it 'should change the database belonging to' do
307
+ stub_api_request(:post, "/v3/table/change_database/src_db/table").
308
+ with(:body => {'dest_database_name' => 'dst_db'}).
309
+ to_return(:body => {'database' => 'dst_db', 'table' => 'table', 'type' => 'log'}.to_json)
310
+ expect(api.change_database('src_db', 'table', 'dst_db')).to be true
311
+ end
312
+
313
+ it 'should return 403 error if dest database is inaccessible' do
314
+ err_msg = 'Access denied for the destination'
315
+ stub_api_request(:post, "/v3/table/change_database/src_db/table").
316
+ with(:body => {'dest_database_name' => 'inaccessible_db'}).
317
+ to_return(:status => 403, :body => {'message' => err_msg}.to_json)
318
+ expect {
319
+ api.change_database('src_db', 'table', 'inaccessible_db')
320
+ }.to raise_error(TreasureData::ForbiddenError, /#{err_msg}/)
321
+ end
322
+
323
+ it 'should return 403 error if there is a same name table in the dest database' do
324
+ err_msg = 'Table table already exists in the destination'
325
+ stub_api_request(:post, "/v3/table/change_database/src_db/table").
326
+ with(:body => {'dest_database_name' => 'dst_db'}).
327
+ to_return(:status => 403, :body => {'message' => err_msg}.to_json)
328
+ expect {
329
+ api.change_database('src_db', 'table', 'dst_db')
330
+ }.to raise_error(TreasureData::ForbiddenError, /#{err_msg}/)
331
+ end
332
+
333
+ it 'should return 404 error if the dest database does not exist' do
334
+ err_msg = 'Destination database is not found'
335
+ stub_api_request(:post, "/v3/table/change_database/src_db/table").
336
+ with(:body => {'dest_database_name' => 'notexist_db'}).
337
+ to_return(:status => 404, :body => {'message' => err_msg}.to_json)
338
+ expect {
339
+ api.change_database('src_db', 'table', 'notexist_db')
340
+ }.to raise_error(TreasureData::NotFoundError, /#{err_msg}/)
341
+ end
342
+ end
226
343
  end
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: 1.0.3
4
+ version: 1.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Treasure Data, Inc.
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-15 00:00:00.000000000 Z
11
+ date: 2021-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -128,6 +128,20 @@ dependencies:
128
128
  - - ">="
129
129
  - !ruby/object:Gem::Version
130
130
  version: '0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: webrick
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
131
145
  description: Treasure Data API library for Ruby
132
146
  email: support@treasure-data.com
133
147
  executables: []
@@ -138,7 +152,6 @@ files:
138
152
  - lib/td-client.rb
139
153
  - lib/td/client.rb
140
154
  - lib/td/client/api.rb
141
- - lib/td/client/api/access_control.rb
142
155
  - lib/td/client/api/account.rb
143
156
  - lib/td/client/api/bulk_import.rb
144
157
  - lib/td/client/api/bulk_load.rb
@@ -158,7 +171,6 @@ files:
158
171
  - lib/td/client/version.rb
159
172
  - lib/td/core_ext/openssl/ssl/sslcontext/set_params.rb
160
173
  - spec/spec_helper.rb
161
- - spec/td/client/access_control_api_spec.rb
162
174
  - spec/td/client/account_api_spec.rb
163
175
  - spec/td/client/api_error_spec.rb
164
176
  - spec/td/client/api_spec.rb
@@ -182,9 +194,10 @@ files:
182
194
  - spec/td/client_sched_spec.rb
183
195
  - spec/td/client_spec.rb
184
196
  homepage: http://treasuredata.com/
185
- licenses: []
197
+ licenses:
198
+ - Apache-2.0
186
199
  metadata: {}
187
- post_install_message:
200
+ post_install_message:
188
201
  rdoc_options: []
189
202
  require_paths:
190
203
  - lib
@@ -199,31 +212,29 @@ required_rubygems_version: !ruby/object:Gem::Requirement
199
212
  - !ruby/object:Gem::Version
200
213
  version: '0'
201
214
  requirements: []
202
- rubyforge_project:
203
- rubygems_version: 2.6.13
204
- signing_key:
215
+ rubygems_version: 3.0.9
216
+ signing_key:
205
217
  specification_version: 4
206
218
  summary: Treasure Data API library for Ruby
207
219
  test_files:
208
- - spec/td/client/access_control_api_spec.rb
209
- - spec/td/client/account_api_spec.rb
210
- - spec/td/client/api_error_spec.rb
220
+ - spec/td/client_spec.rb
221
+ - spec/td/client_sched_spec.rb
222
+ - spec/td/client/partial_delete_api_spec.rb
211
223
  - spec/td/client/api_spec.rb
212
- - spec/td/client/api_ssl_connection_spec.rb
213
- - spec/td/client/bulk_import_spec.rb
214
- - spec/td/client/bulk_load_spec.rb
215
224
  - spec/td/client/db_api_spec.rb
225
+ - spec/td/client/result_api_spec.rb
216
226
  - spec/td/client/export_api_spec.rb
217
- - spec/td/client/import_api_spec.rb
218
- - spec/td/client/job_api_spec.rb
219
- - spec/td/client/model_job_spec.rb
220
- - spec/td/client/model_schedule_spec.rb
221
227
  - spec/td/client/model_schema_spec.rb
222
- - spec/td/client/partial_delete_api_spec.rb
223
- - spec/td/client/result_api_spec.rb
228
+ - spec/td/client/api_error_spec.rb
224
229
  - spec/td/client/sched_api_spec.rb
225
- - spec/td/client/server_status_api_spec.rb
226
230
  - spec/td/client/table_api_spec.rb
231
+ - spec/td/client/bulk_load_spec.rb
232
+ - spec/td/client/api_ssl_connection_spec.rb
233
+ - spec/td/client/job_api_spec.rb
234
+ - spec/td/client/account_api_spec.rb
227
235
  - spec/td/client/user_api_spec.rb
228
- - spec/td/client_sched_spec.rb
229
- - spec/td/client_spec.rb
236
+ - spec/td/client/bulk_import_spec.rb
237
+ - spec/td/client/model_schedule_spec.rb
238
+ - spec/td/client/import_api_spec.rb
239
+ - spec/td/client/server_status_api_spec.rb
240
+ - spec/td/client/model_job_spec.rb
@@ -1,74 +0,0 @@
1
- class TreasureData::API
2
- module AccessControl
3
-
4
- ####
5
- ## Access Control API
6
- ##
7
-
8
- # @param [String] subject
9
- # @param [String] action
10
- # @param [String] scope
11
- # @param [Array] grant_option
12
- # @return [true]
13
- def grant_access_control(subject, action, scope, grant_option)
14
- params = {'subject'=>subject, 'action'=>action, 'scope'=>scope, 'grant_option'=>grant_option.to_s}
15
- code, body, res = post("/v3/acl/grant", params)
16
- if code != "200"
17
- raise_error("Granting access control failed", res)
18
- end
19
- return true
20
- end
21
-
22
- # @param [String] subject
23
- # @param [String] action
24
- # @param [String] scope
25
- # @return [true]
26
- def revoke_access_control(subject, action, scope)
27
- params = {'subject'=>subject, 'action'=>action, 'scope'=>scope}
28
- code, body, res = post("/v3/acl/revoke", params)
29
- if code != "200"
30
- raise_error("Revoking access control failed", res)
31
- end
32
- return true
33
- end
34
-
35
- # @param [String] user
36
- # @param [String] action
37
- # @param [String] scope
38
- # @return [Array]
39
- def test_access_control(user, action, scope)
40
- params = {'user'=>user, 'action'=>action, 'scope'=>scope}
41
- code, body, res = get("/v3/acl/test", params)
42
- if code != "200"
43
- raise_error("Testing access control failed", res)
44
- end
45
- js = checked_json(body, %w[permission access_controls])
46
- perm = js["permission"]
47
- acl = js["access_controls"].map {|roleinfo|
48
- subject = roleinfo['subject']
49
- action = roleinfo['action']
50
- scope = roleinfo['scope']
51
- [name, action, scope]
52
- }
53
- return perm, acl
54
- end
55
-
56
- # @return [Array]
57
- def list_access_controls
58
- code, body, res = get("/v3/acl/list")
59
- if code != "200"
60
- raise_error("Listing access control failed", res)
61
- end
62
- js = checked_json(body, %w[access_controls])
63
- acl = js["access_controls"].map {|roleinfo|
64
- subject = roleinfo['subject']
65
- action = roleinfo['action']
66
- scope = roleinfo['scope']
67
- grant_option = roleinfo['grant_option']
68
- [subject, action, scope, grant_option]
69
- }
70
- return acl
71
- end
72
-
73
- end
74
- end
@@ -1,37 +0,0 @@
1
- require 'spec_helper'
2
- require 'td/client/spec_resources'
3
- require 'json'
4
-
5
- describe 'Access Control API' do
6
- include_context 'spec symbols'
7
- include_context 'common helper'
8
-
9
- let :api do
10
- # no retry for GET
11
- API.new(nil, {:max_cumul_retry_delay => -1})
12
- end
13
-
14
- describe 'all apis' do
15
- it 'is deprecated' do
16
- stub_api_request(:post, "/v3/acl/grant").to_return(:status => 500)
17
- expect {
18
- api.grant_access_control('subject', 'action', 'scope', [])
19
- }.to raise_error(TreasureData::APIError)
20
-
21
- stub_api_request(:post, "/v3/acl/revoke").to_return(:status => 500)
22
- expect {
23
- api.revoke_access_control('subject', 'action', 'scope')
24
- }.to raise_error(TreasureData::APIError)
25
-
26
- stub_api_request(:get, "/v3/acl/test", :query => {'user' => 'user', 'action' => 'action', 'scope' => 'scope'}).to_return(:status => 422)
27
- expect {
28
- api.test_access_control('user', 'action', 'scope')
29
- }.to raise_error(TreasureData::APIError)
30
-
31
- stub_api_request(:get, "/v3/acl/list").to_return(:status => 500)
32
- expect {
33
- api.list_access_controls
34
- }.to raise_error(TreasureData::APIError)
35
- end
36
- end
37
- end