td-client 0.8.67 → 0.8.68

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: 13c565f827556460635d600c91a409818904e256
4
- data.tar.gz: 2ff8ec8823177a04c69dd767040fc844dcf47e9c
3
+ metadata.gz: a6f4ce3a275f3c038d731feb43dda2ec4cc477e5
4
+ data.tar.gz: b6affcd7ad8e6430af814d9459cfbabde9ae50f9
5
5
  SHA512:
6
- metadata.gz: e45453b9ce95aa5054436ce1131c66c36b35b8a85e91c7b49df632b4cf940ce8969c8325f6d5bb1d6d686529e609f7cb28cd626d60de2ebb83d30074caad94b1
7
- data.tar.gz: c0c9b534f3d0e33947387d3a73394e281e61f74b2f896220b4e9dba2a87166059d40788c8a9cc7951e8cfb39d698d987d5bc2fb09875cc87cab073a84da36888
6
+ metadata.gz: 3127bd887d7fff303cde39f92932d8d9a263cfb37201e3c299a24e53a3767390a70ce4195fe34ce9003ecd815465a0776e3e094cc1c35d3da37f181bc4273c98
7
+ data.tar.gz: d002d279379a09a63c10882503cf09f6d25d7e3d12198281c55e82881d8f1ef5a7e2ca2af3cb66856b89d8056667da7003a5172f91d424e87fba8a1c625ddcdf
data/lib/td/client.rb CHANGED
@@ -287,8 +287,18 @@ class Client
287
287
  def history(name, from=nil, to=nil)
288
288
  result = @api.history(name, from, to)
289
289
  result.map {|scheduled_at,job_id,type,status,query,start_at,end_at,result_url,priority,database|
290
- # TODO org
291
- ScheduledJob.new(self, scheduled_at, job_id, type, query, status, nil, nil, start_at, end_at, nil, result_url, nil, priority, nil, nil, database)
290
+ job_param = [job_id, type, query, status,
291
+ nil, nil, # url, debug
292
+ start_at, end_at,
293
+ nil, # cpu_time
294
+ nil, nil, # result_size, result
295
+ result_url,
296
+ nil, # hive_result_schema
297
+ priority,
298
+ nil, # retry_limit
299
+ nil, # TODO org_name
300
+ database]
301
+ ScheduledJob.new(self, scheduled_at, *job_param)
292
302
  }
293
303
  end
294
304
 
data/lib/td/client/api.rb CHANGED
@@ -1,32 +1,39 @@
1
+ require 'td/client/api_error'
1
2
  require 'td/client/version'
3
+ require 'td/client/api/access_control'
4
+ require 'td/client/api/account'
5
+ require 'td/client/api/bulk_import'
6
+ require 'td/client/api/database'
7
+ require 'td/client/api/export'
8
+ require 'td/client/api/import'
9
+ require 'td/client/api/job'
10
+ require 'td/client/api/partial_delete'
11
+ require 'td/client/api/result'
12
+ require 'td/client/api/schedule'
13
+ require 'td/client/api/server_status'
14
+ require 'td/client/api/table'
15
+ require 'td/client/api/user'
16
+
17
+ # For disabling SSLv3 connection in favor of POODLE Attack protection
18
+ require 'td/core_ext/openssl/ssl/sslcontext/set_params'
2
19
 
3
20
  module TreasureData
4
21
 
5
- class ParameterValidationError < StandardError
6
- end
7
-
8
- # Generic API error
9
- class APIError < StandardError
10
- end
11
-
12
- # 401 API errors
13
- class AuthError < APIError
14
- end
15
-
16
- # 403 API errors, used for database permissions
17
- class ForbiddenError < APIError
18
- end
19
-
20
- # 409 API errors
21
- class AlreadyExistsError < APIError
22
- end
23
-
24
- # 404 API errors
25
- class NotFoundError < APIError
26
- end
27
-
28
-
29
22
  class API
23
+ include API::AccessControl
24
+ include API::Account
25
+ include API::BulkImport
26
+ include API::Database
27
+ include API::Export
28
+ include API::Import
29
+ include API::Job
30
+ include API::PartialDelete
31
+ include API::Result
32
+ include API::Schedule
33
+ include API::ServerStatus
34
+ include API::Table
35
+ include API::User
36
+
30
37
  DEFAULT_ENDPOINT = 'api.treasure-data.com'
31
38
  DEFAULT_IMPORT_ENDPOINT = 'api-import.treasure-data.com'
32
39
 
@@ -42,6 +49,10 @@ class API
42
49
  require 'time'
43
50
  #require 'faraday' # faraday doesn't support streaming upload with httpclient yet so now disabled
44
51
  require 'httpclient'
52
+ require 'zlib'
53
+ require 'stringio'
54
+ require 'cgi'
55
+ require 'msgpack'
45
56
 
46
57
  @apikey = apikey
47
58
  @user_agent = "TD-Client-Ruby: #{TreasureData::Client::VERSION}"
@@ -195,1006 +206,17 @@ class API
195
206
 
196
207
  # for fluent-plugin-td / td command to check table existence with import onlt user
197
208
  def self.create_empty_gz_data
198
- require 'zlib'
199
- require 'stringio'
200
-
201
209
  io = StringIO.new
202
210
  Zlib::GzipWriter.new(io).close
203
211
  io.string
204
212
  end
205
213
 
206
- ####
207
- ## Account API
208
- ##
209
-
210
- def show_account
211
- code, body, res = get("/v3/account/show")
212
- if code != "200"
213
- raise_error("Show account failed", res)
214
- end
215
- js = checked_json(body, %w[account])
216
- a = js["account"]
217
- account_id = a['id'].to_i
218
- plan = a['plan'].to_i
219
- storage_size = a['storage_size'].to_i
220
- guaranteed_cores = a['guaranteed_cores'].to_i
221
- maximum_cores = a['maximum_cores'].to_i
222
- created_at = a['created_at']
223
- return [account_id, plan, storage_size, guaranteed_cores, maximum_cores, created_at]
224
- end
225
-
226
- def account_core_utilization(from, to)
227
- params = { }
228
- params['from'] = from.to_s if from
229
- params['to'] = to.to_s if to
230
- code, body, res = get("/v3/account/core_utilization", params)
231
- if code != "200"
232
- raise_error("Show account failed", res)
233
- end
234
- js = checked_json(body, %w[from to interval history])
235
- from = Time.parse(js['from']).utc
236
- to = Time.parse(js['to']).utc
237
- interval = js['interval'].to_i
238
- history = js['history']
239
- return [from, to, interval, history]
240
- end
241
-
242
-
243
- ####
244
- ## Database API
245
- ##
246
-
247
- # => [name:String]
248
- def list_databases
249
- code, body, res = get("/v3/database/list")
250
- if code != "200"
251
- raise_error("List databases failed", res)
252
- end
253
- js = checked_json(body, %w[databases])
254
- result = {}
255
- js["databases"].each {|m|
256
- name = m['name']
257
- count = m['count']
258
- created_at = m['created_at']
259
- updated_at = m['updated_at']
260
- permission = m['permission']
261
- result[name] = [count, created_at, updated_at, nil, permission] # set nil to org for API compatibiilty
262
- }
263
- return result
264
- end
265
-
266
- # => true
267
- def delete_database(db)
268
- code, body, res = post("/v3/database/delete/#{e db}")
269
- if code != "200"
270
- raise_error("Delete database failed", res)
271
- end
272
- return true
273
- end
274
-
275
- # => true
276
- def create_database(db, opts={})
277
- params = opts.dup
278
- code, body, res = post("/v3/database/create/#{e db}", params)
279
- if code != "200"
280
- raise_error("Create database failed", res)
281
- end
282
- return true
283
- end
284
-
285
-
286
- ####
287
- ## Table API
288
- ##
289
-
290
- # => {name:String => [type:Symbol, count:Integer]}
291
- def list_tables(db)
292
- code, body, res = get("/v3/table/list/#{e db}")
293
- if code != "200"
294
- raise_error("List tables failed", res)
295
- end
296
- js = checked_json(body, %w[tables])
297
- result = {}
298
- js["tables"].map {|m|
299
- name = m['name']
300
- type = (m['type'] || '?').to_sym
301
- count = (m['count'] || 0).to_i # TODO?
302
- created_at = m['created_at']
303
- updated_at = m['updated_at']
304
- last_import = m['counter_updated_at']
305
- last_log_timestamp = m['last_log_timestamp']
306
- estimated_storage_size = m['estimated_storage_size'].to_i
307
- schema = JSON.parse(m['schema'] || '[]')
308
- expire_days = m['expire_days']
309
- primary_key = m['primary_key']
310
- primary_key_type = m['primary_key_type']
311
- result[name] = [type, schema, count, created_at, updated_at, estimated_storage_size, last_import, last_log_timestamp, expire_days, primary_key, primary_key_type]
312
- }
313
- return result
314
- end
315
-
316
- def create_log_or_item_table(db, table, type)
317
- code, body, res = post("/v3/table/create/#{e db}/#{e table}/#{type}")
318
- if code != "200"
319
- raise_error("Create #{type} table failed", res)
320
- end
321
- return true
322
- end
323
- private :create_log_or_item_table
324
-
325
- # => true
326
- def create_log_table(db, table)
327
- create_table(db, table, :log)
328
- end
329
-
330
- # => true
331
- def create_item_table(db, table, primary_key, primary_key_type)
332
- params = {'primary_key' => primary_key, 'primary_key_type' => primary_key_type}
333
- create_table(db, table, :item, params)
334
- end
335
-
336
- def create_table(db, table, type, params={})
337
- schema = schema.to_s
338
- code, body, res = post("/v3/table/create/#{e db}/#{e table}/#{type}", params)
339
- if code != "200"
340
- raise_error("Create #{type} table failed", res)
341
- end
342
- return true
343
- end
344
- private :create_table
345
-
346
- # => true
347
- def swap_table(db, table1, table2)
348
- code, body, res = post("/v3/table/swap/#{e db}/#{e table1}/#{e table2}")
349
- if code != "200"
350
- raise_error("Swap tables failed", res)
351
- end
352
- return true
353
- end
354
-
355
- # => true
356
- def update_schema(db, table, schema_json)
357
- code, body, res = post("/v3/table/update-schema/#{e db}/#{e table}", {'schema'=>schema_json})
358
- if code != "200"
359
- raise_error("Create schema table failed", res)
360
- end
361
- return true
362
- end
363
-
364
- def update_expire(db, table, expire_days)
365
- code, body, res = post("/v3/table/update/#{e db}/#{e table}", {'expire_days'=>expire_days})
366
- if code != "200"
367
- raise_error("Update table expiration failed", res)
368
- end
369
- return true
370
- end
371
-
372
- # => type:Symbol
373
- def delete_table(db, table)
374
- code, body, res = post("/v3/table/delete/#{e db}/#{e table}")
375
- if code != "200"
376
- raise_error("Delete table failed", res)
377
- end
378
- js = checked_json(body, %w[])
379
- type = (js['type'] || '?').to_sym
380
- return type
381
- end
382
-
383
- def tail(db, table, count, to, from, &block)
384
- params = {'format' => 'msgpack'}
385
- params['count'] = count.to_s if count
386
- params['to'] = to.to_s if to
387
- params['from'] = from.to_s if from
388
- code, body, res = get("/v3/table/tail/#{e db}/#{e table}", params)
389
- if code != "200"
390
- raise_error("Tail table failed", res)
391
- end
392
- require 'msgpack'
393
- if block
394
- MessagePack::Unpacker.new.feed_each(body, &block)
395
- nil
396
- else
397
- result = []
398
- MessagePack::Unpacker.new.feed_each(body) {|row|
399
- result << row
400
- }
401
- return result
402
- end
403
- end
404
-
405
-
406
- ####
407
- ## Job API
408
- ##
409
-
410
- # => [(jobId:String, type:Symbol, status:String, start_at:String, end_at:String, result_url:String)]
411
- def list_jobs(from=0, to=nil, status=nil, conditions=nil)
412
- params = {}
413
- params['from'] = from.to_s if from
414
- params['to'] = to.to_s if to
415
- params['status'] = status.to_s if status
416
- params.merge!(conditions) if conditions
417
- code, body, res = get("/v3/job/list", params)
418
- if code != "200"
419
- raise_error("List jobs failed", res)
420
- end
421
- js = checked_json(body, %w[jobs])
422
- result = []
423
- js['jobs'].each {|m|
424
- job_id = m['job_id']
425
- type = (m['type'] || '?').to_sym
426
- database = m['database']
427
- status = m['status']
428
- query = m['query']
429
- start_at = m['start_at']
430
- end_at = m['end_at']
431
- cpu_time = m['cpu_time']
432
- result_size = m['result_size'] # compressed result size in msgpack.gz format
433
- result_url = m['result']
434
- priority = m['priority']
435
- retry_limit = m['retry_limit']
436
- result << [job_id, type, status, query, start_at, end_at, cpu_time,
437
- result_size, result_url, priority, retry_limit, nil, database]
438
- }
439
- return result
440
- end
441
-
442
- # => (type:Symbol, status:String, result:String, url:String, result:String)
443
- def show_job(job_id)
444
- # use v3/job/status instead of v3/job/show to poll finish of a job
445
- code, body, res = get("/v3/job/show/#{e job_id}")
446
- if code != "200"
447
- raise_error("Show job failed", res)
448
- end
449
- js = checked_json(body, %w[status])
450
- # TODO debug
451
- type = (js['type'] || '?').to_sym # TODO
452
- database = js['database']
453
- query = js['query']
454
- status = js['status']
455
- debug = js['debug']
456
- url = js['url']
457
- start_at = js['start_at']
458
- end_at = js['end_at']
459
- cpu_time = js['cpu_time']
460
- result_size = js['result_size'] # compressed result size in msgpack.gz format
461
- result = js['result'] # result target URL
462
- hive_result_schema = (js['hive_result_schema'] || '')
463
- if hive_result_schema.empty?
464
- hive_result_schema = nil
465
- else
466
- begin
467
- hive_result_schema = JSON.parse(hive_result_schema)
468
- rescue JSON::ParserError => e
469
- # this is a workaround for a Known Limitation in the Pig Engine which does not set a default, auto-generated
470
- # column name for anonymous columns (such as the ones that are generated from UDF like COUNT or SUM).
471
- # The schema will contain 'nil' for the name of those columns and that breaks the JSON parser since it violates
472
- # the JSON syntax standard.
473
- if type == :pig and hive_result_schema !~ /[\{\}]/
474
- begin
475
- # NOTE: this works because a JSON 2 dimensional array is the same as a Ruby one.
476
- # Any change in the format for the hive_result_schema output may cause a syntax error, in which case
477
- # this lame attempt at fixing the problem will fail and we will be raising the original JSON exception
478
- hive_result_schema = eval(hive_result_schema)
479
- rescue SyntaxError => ignored_e
480
- raise e
481
- end
482
- hive_result_schema.each_with_index {|col_schema, idx|
483
- if col_schema[0].nil?
484
- col_schema[0] = "_col#{idx}"
485
- end
486
- }
487
- else
488
- raise e
489
- end
490
- end
491
- end
492
- priority = js['priority']
493
- retry_limit = js['retry_limit']
494
- return [type, query, status, url, debug, start_at, end_at, cpu_time,
495
- result_size, result, hive_result_schema, priority, retry_limit, nil, database]
496
- end
497
-
498
- def job_status(job_id)
499
- code, body, res = get("/v3/job/status/#{e job_id}")
500
- if code != "200"
501
- raise_error("Get job status failed", res)
502
- end
503
-
504
- js = checked_json(body, %w[status])
505
- return js['status']
506
- end
507
-
508
- def job_result(job_id)
509
- require 'msgpack'
510
- code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>'msgpack'})
511
- if code != "200"
512
- raise_error("Get job result failed", res)
513
- end
514
- result = []
515
- MessagePack::Unpacker.new.feed_each(body) {|row|
516
- result << row
517
- }
518
- return result
519
- end
520
-
521
- # block is optional and must accept 1 parameter
522
- def job_result_format(job_id, format, io=nil, &block)
523
- if io
524
- code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>format}) {|res|
525
- if res.code != "200"
526
- raise_error("Get job result failed", res)
527
- end
528
-
529
- if ce = res.header['Content-Encoding']
530
- require 'zlib'
531
- res.extend(DeflateReadBodyMixin)
532
- res.gzip = true if ce == 'gzip'
533
- else
534
- res.extend(DirectReadBodyMixin)
535
- end
536
-
537
- res.extend(DirectReadBodyMixin)
538
- if ce = res.header['Content-Encoding']
539
- if ce == 'gzip'
540
- infl = Zlib::Inflate.new(Zlib::MAX_WBITS + 16)
541
- else
542
- infl = Zlib::Inflate.new
543
- end
544
- end
545
-
546
- total_compr_size = 0
547
- res.each_fragment {|fragment|
548
- total_compr_size += fragment.size
549
- # uncompressed if the 'Content-Enconding' header is set in response
550
- fragment = infl.inflate(fragment) if ce
551
- io.write(fragment)
552
- block.call(total_compr_size) if block_given?
553
- }
554
- }
555
- nil
556
- else
557
- code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>format})
558
- if res.code != "200"
559
- raise_error("Get job result failed", res)
560
- end
561
- body
562
- end
563
- end
564
-
565
- # block is optional and must accept 1 argument
566
- def job_result_each(job_id, &block)
567
- require 'msgpack'
568
- get("/v3/job/result/#{e job_id}", {'format'=>'msgpack'}) {|res|
569
- if res.code != "200"
570
- raise_error("Get job result failed", res)
571
- end
572
-
573
- # default to decompressing the response since format is fixed to 'msgpack'
574
- res.extend(DeflateReadBodyMixin)
575
- res.gzip = (res.header['Content-Encoding'] == 'gzip')
576
- upkr = MessagePack::Unpacker.new
577
- res.each_fragment {|inflated_fragment|
578
- upkr.feed_each(inflated_fragment, &block)
579
- }
580
- }
581
- nil
582
- end
583
-
584
- # block is optional and must accept 1 argument
585
- def job_result_each_with_compr_size(job_id, &block)
586
- require 'zlib'
587
- require 'msgpack'
588
-
589
- get("/v3/job/result/#{e job_id}", {'format'=>'msgpack'}) {|res|
590
- if res.code != "200"
591
- raise_error("Get job result failed", res)
592
- end
593
-
594
- res.extend(DirectReadBodyMixin)
595
- if res.header['Content-Encoding'] == 'gzip'
596
- infl = Zlib::Inflate.new(Zlib::MAX_WBITS + 16)
597
- else
598
- infl = Zlib::Inflate.new
599
- end
600
- upkr = MessagePack::Unpacker.new
601
- begin
602
- total_compr_size = 0
603
- res.each_fragment {|fragment|
604
- total_compr_size += fragment.size
605
- upkr.feed_each(infl.inflate(fragment)) {|unpacked|
606
- block.call(unpacked, total_compr_size) if block_given?
607
- }
608
- }
609
- ensure
610
- infl.close
611
- end
612
- }
613
- nil
614
- end
615
-
616
- def job_result_raw(job_id, format)
617
- code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>format})
618
- if code != "200"
619
- raise_error("Get job result failed", res)
620
- end
621
- return body
622
- end
623
-
624
- def kill(job_id)
625
- code, body, res = post("/v3/job/kill/#{e job_id}")
626
- if code != "200"
627
- raise_error("Kill job failed", res)
628
- end
629
- js = checked_json(body, %w[])
630
- former_status = js['former_status']
631
- return former_status
632
- end
633
-
634
- # => jobId:String
635
- def hive_query(q, db=nil, result_url=nil, priority=nil, retry_limit=nil, opts={})
636
- query(q, :hive, db, result_url, priority, retry_limit, opts)
637
- end
638
-
639
- # => jobId:String
640
- def pig_query(q, db=nil, result_url=nil, priority=nil, retry_limit=nil, opts={})
641
- query(q, :pig, db, result_url, priority, retry_limit, opts)
642
- end
643
-
644
- # => jobId:String
645
- def query(q, type=:hive, db=nil, result_url=nil, priority=nil, retry_limit=nil, opts={})
646
- params = {'query' => q}.merge(opts)
647
- params['result'] = result_url if result_url
648
- params['priority'] = priority if priority
649
- params['retry_limit'] = retry_limit if retry_limit
650
- code, body, res = post("/v3/job/issue/#{type}/#{e db}", params)
651
- if code != "200"
652
- raise_error("Query failed", res)
653
- end
654
- js = checked_json(body, %w[job_id])
655
- return js['job_id'].to_s
656
- end
657
-
658
- ####
659
- ## Export API
660
- ##
661
-
662
- # => jobId:String
663
- def export(db, table, storage_type, opts={})
664
- params = opts.dup
665
- params['storage_type'] = storage_type
666
- code, body, res = post("/v3/export/run/#{e db}/#{e table}", params)
667
- if code != "200"
668
- raise_error("Export failed", res)
669
- end
670
- js = checked_json(body, %w[job_id])
671
- return js['job_id'].to_s
672
- end
673
-
674
-
675
- ####
676
- ## Partial delete API
677
- ##
678
-
679
- def partial_delete(db, table, to, from, opts={})
680
- params = opts.dup
681
- params['to'] = to.to_s
682
- params['from'] = from.to_s
683
- code, body, res = post("/v3/table/partialdelete/#{e db}/#{e table}", params)
684
- if code != "200"
685
- raise_error("Partial delete failed", res)
686
- end
687
- js = checked_json(body, %w[job_id])
688
- return js['job_id'].to_s
689
- end
690
-
691
- ####
692
- ## Bulk import API
693
- ##
694
-
695
- # => nil
696
- def create_bulk_import(name, db, table, opts={})
697
- params = opts.dup
698
- code, body, res = post("/v3/bulk_import/create/#{e name}/#{e db}/#{e table}", params)
699
- if code != "200"
700
- raise_error("Create bulk import failed", res)
701
- end
702
- return nil
703
- end
704
-
705
- # => nil
706
- def delete_bulk_import(name, opts={})
707
- params = opts.dup
708
- code, body, res = post("/v3/bulk_import/delete/#{e name}", params)
709
- if code != "200"
710
- raise_error("Delete bulk import failed", res)
711
- end
712
- return nil
713
- end
714
-
715
- # => data:Hash
716
- def show_bulk_import(name)
717
- code, body, res = get("/v3/bulk_import/show/#{name}")
718
- if code != "200"
719
- raise_error("Show bulk import failed", res)
720
- end
721
- js = checked_json(body, %w[status])
722
- return js
723
- end
724
-
725
- # => result:[data:Hash]
726
- def list_bulk_imports(opts={})
727
- params = opts.dup
728
- code, body, res = get("/v3/bulk_import/list", params)
729
- if code != "200"
730
- raise_error("List bulk imports failed", res)
731
- end
732
- js = checked_json(body, %w[bulk_imports])
733
- return js['bulk_imports']
734
- end
735
-
736
- def list_bulk_import_parts(name, opts={})
737
- params = opts.dup
738
- code, body, res = get("/v3/bulk_import/list_parts/#{e name}", params)
739
- if code != "200"
740
- raise_error("List bulk import parts failed", res)
741
- end
742
- js = checked_json(body, %w[parts])
743
- return js['parts']
744
- end
745
-
746
- # => nil
747
- def bulk_import_upload_part(name, part_name, stream, size, opts={})
748
- code, body, res = put("/v3/bulk_import/upload_part/#{e name}/#{e part_name}", stream, size)
749
- if code[0] != ?2
750
- raise_error("Upload a part failed", res)
751
- end
752
- return nil
753
- end
754
-
755
- # => nil
756
- def bulk_import_delete_part(name, part_name, opts={})
757
- params = opts.dup
758
- code, body, res = post("/v3/bulk_import/delete_part/#{e name}/#{e part_name}", params)
759
- if code[0] != ?2
760
- raise_error("Delete a part failed", res)
761
- end
762
- return nil
763
- end
764
-
765
- # => nil
766
- def freeze_bulk_import(name, opts={})
767
- params = opts.dup
768
- code, body, res = post("/v3/bulk_import/freeze/#{e name}", params)
769
- if code != "200"
770
- raise_error("Freeze bulk import failed", res)
771
- end
772
- return nil
773
- end
774
-
775
- # => nil
776
- def unfreeze_bulk_import(name, opts={})
777
- params = opts.dup
778
- code, body, res = post("/v3/bulk_import/unfreeze/#{e name}", params)
779
- if code != "200"
780
- raise_error("Unfreeze bulk import failed", res)
781
- end
782
- return nil
783
- end
784
-
785
- # => jobId:String
786
- def perform_bulk_import(name, opts={})
787
- params = opts.dup
788
- code, body, res = post("/v3/bulk_import/perform/#{e name}", params)
789
- if code != "200"
790
- raise_error("Perform bulk import failed", res)
791
- end
792
- js = checked_json(body, %w[job_id])
793
- return js['job_id'].to_s
794
- end
795
-
796
- # => nil
797
- def commit_bulk_import(name, opts={})
798
- params = opts.dup
799
- code, body, res = post("/v3/bulk_import/commit/#{e name}", params)
800
- if code != "200"
801
- raise_error("Commit bulk import failed", res)
802
- end
803
- return nil
804
- end
805
-
806
- # => data...
807
- def bulk_import_error_records(name, opts={}, &block)
808
- params = opts.dup
809
- code, body, res = get("/v3/bulk_import/error_records/#{e name}", params)
810
- if code != "200"
811
- raise_error("Failed to get bulk import error records", res)
812
- end
813
- if body.empty?
814
- if block
815
- return nil
816
- else
817
- return []
818
- end
819
- end
820
- require 'zlib'
821
- require 'stringio'
822
- require 'msgpack'
823
- require File.expand_path('compat_gzip_reader', File.dirname(__FILE__))
824
- u = MessagePack::Unpacker.new(Zlib::GzipReader.new(StringIO.new(body)))
825
- if block
826
- begin
827
- u.each(&block)
828
- rescue EOFError
829
- end
830
- nil
831
- else
832
- result = []
833
- begin
834
- u.each {|row|
835
- result << row
836
- }
837
- rescue EOFError
838
- end
839
- return result
840
- end
841
- end
842
-
843
- ####
844
- ## Schedule API
845
- ##
846
-
847
- # => start:String
848
- def create_schedule(name, opts)
849
- params = opts.update({:type=> opts[:type] || opts['type'] || 'hive'})
850
- code, body, res = post("/v3/schedule/create/#{e name}", params)
851
- if code != "200"
852
- raise_error("Create schedule failed", res)
853
- end
854
- js = checked_json(body, %w[start])
855
- return js['start']
856
- end
857
-
858
- # => cron:String, query:String
859
- def delete_schedule(name)
860
- code, body, res = post("/v3/schedule/delete/#{e name}")
861
- if code != "200"
862
- raise_error("Delete schedule failed", res)
863
- end
864
- js = checked_json(body, %w[])
865
- return js['cron'], js["query"]
866
- end
867
-
868
- # => [(name:String, cron:String, query:String, database:String, result_url:String)]
869
- def list_schedules
870
- code, body, res = get("/v3/schedule/list")
871
- if code != "200"
872
- raise_error("List schedules failed", res)
873
- end
874
- js = checked_json(body, %w[schedules])
875
- result = []
876
- js['schedules'].each {|m|
877
- name = m['name']
878
- cron = m['cron']
879
- query = m['query']
880
- database = m['database']
881
- result_url = m['result']
882
- timezone = m['timezone']
883
- delay = m['delay']
884
- next_time = m['next_time']
885
- priority = m['priority']
886
- retry_limit = m['retry_limit']
887
- result << [name, cron, query, database, result_url, timezone, delay, next_time, priority, retry_limit, nil] # same as database
888
- }
889
- return result
890
- end
891
-
892
- def update_schedule(name, params)
893
- code, body, res = post("/v3/schedule/update/#{e name}", params)
894
- if code != "200"
895
- raise_error("Update schedule failed", res)
896
- end
897
- return nil
898
- end
899
-
900
- def history(name, from=0, to=nil)
901
- params = {}
902
- params['from'] = from.to_s if from
903
- params['to'] = to.to_s if to
904
- code, body, res = get("/v3/schedule/history/#{e name}", params)
905
- if code != "200"
906
- raise_error("List history failed", res)
907
- end
908
- js = checked_json(body, %w[history])
909
- result = []
910
- js['history'].each {|m|
911
- job_id = m['job_id']
912
- type = (m['type'] || '?').to_sym
913
- database = m['database']
914
- status = m['status']
915
- query = m['query']
916
- start_at = m['start_at']
917
- end_at = m['end_at']
918
- scheduled_at = m['scheduled_at']
919
- result_url = m['result']
920
- priority = m['priority']
921
- result << [scheduled_at, job_id, type, status, query, start_at, end_at, result_url, priority, database]
922
- }
923
- return result
924
- end
925
-
926
- def run_schedule(name, time, num)
927
- params = {}
928
- params = {'num' => num} if num
929
- code, body, res = post("/v3/schedule/run/#{e name}/#{e time}", params)
930
- if code != "200"
931
- raise_error("Run schedule failed", res)
932
- end
933
- js = checked_json(body, %w[jobs])
934
- result = []
935
- js['jobs'].each {|m|
936
- job_id = m['job_id']
937
- scheduled_at = m['scheduled_at']
938
- type = (m['type'] || '?').to_sym
939
- result << [job_id, type, scheduled_at]
940
- }
941
- return result
942
- end
943
-
944
- ####
945
- ## Import API
946
- ##
947
-
948
- # => time:Float
949
- def import(db, table, format, stream, size, unique_id=nil)
950
- if unique_id
951
- path = "/v3/table/import_with_id/#{e db}/#{e table}/#{unique_id}/#{format}"
952
- else
953
- path = "/v3/table/import/#{e db}/#{e table}/#{format}"
954
- end
955
- opts = {}
956
- if @host == DEFAULT_ENDPOINT
957
- opts[:host] = DEFAULT_IMPORT_ENDPOINT
958
- elsif @host == NEW_DEFAULT_ENDPOINT
959
- opts[:host] = NEW_DEFAULT_IMPORT_ENDPOINT
960
- end
961
- code, body, res = put(path, stream, size, opts)
962
- if code[0] != ?2
963
- raise_error("Import failed", res)
964
- end
965
- js = checked_json(body, %w[])
966
- time = js['elapsed_time'].to_f
967
- return time
968
- end
969
-
970
-
971
- ####
972
- ## Result API
973
- ##
974
-
975
- def list_result
976
- code, body, res = get("/v3/result/list")
977
- if code != "200"
978
- raise_error("List result table failed", res)
979
- end
980
- js = checked_json(body, %w[results])
981
- result = []
982
- js['results'].map {|m|
983
- result << [m['name'], m['url'], nil] # same as database
984
- }
985
- return result
986
- end
987
-
988
- # => true
989
- def create_result(name, url, opts={})
990
- params = {'url'=>url}.merge(opts)
991
- code, body, res = post("/v3/result/create/#{e name}", params)
992
- if code != "200"
993
- raise_error("Create result table failed", res)
994
- end
995
- return true
996
- end
997
-
998
- # => true
999
- def delete_result(name)
1000
- code, body, res = post("/v3/result/delete/#{e name}")
1001
- if code != "200"
1002
- raise_error("Delete result table failed", res)
1003
- end
1004
- return true
1005
- end
1006
-
1007
-
1008
- ####
1009
- ## User API
1010
- ##
1011
-
1012
- # apikey:String
1013
- def authenticate(user, password)
1014
- code, body, res = post("/v3/user/authenticate", {'user'=>user, 'password'=>password})
1015
- if code != "200"
1016
- if code == "400"
1017
- raise_error("Authentication failed", res, AuthError)
1018
- else
1019
- raise_error("Authentication failed", res)
1020
- end
1021
- end
1022
- js = checked_json(body, %w[apikey])
1023
- apikey = js['apikey']
1024
- return apikey
1025
- end
1026
-
1027
- # => [[name:String,organization:String,[user:String]]
1028
- def list_users
1029
- code, body, res = get("/v3/user/list")
1030
- if code != "200"
1031
- raise_error("List users failed", res)
1032
- end
1033
- js = checked_json(body, %w[users])
1034
- result = js["users"].map {|roleinfo|
1035
- name = roleinfo['name']
1036
- email = roleinfo['email']
1037
- [name, nil, nil, email] # set nil to org and role for API compatibility
1038
- }
1039
- return result
1040
- end
1041
-
1042
- # => true
1043
- def add_user(name, org, email, password)
1044
- params = {'organization'=>org, :email=>email, :password=>password}
1045
- code, body, res = post("/v3/user/add/#{e name}", params)
1046
- if code != "200"
1047
- raise_error("Adding user failed", res)
1048
- end
1049
- return true
1050
- end
1051
-
1052
- # => true
1053
- def remove_user(user)
1054
- code, body, res = post("/v3/user/remove/#{e user}")
1055
- if code != "200"
1056
- raise_error("Removing user failed", res)
1057
- end
1058
- return true
1059
- end
1060
-
1061
- # => true
1062
- def change_email(user, email)
1063
- params = {'email' => email}
1064
- code, body, res = post("/v3/user/email/change/#{e user}", params)
1065
- if code != "200"
1066
- raise_error("Changing email failed", res)
1067
- end
1068
- return true
1069
- end
1070
-
1071
- # => [apikey:String]
1072
- def list_apikeys(user)
1073
- code, body, res = get("/v3/user/apikey/list/#{e user}")
1074
- if code != "200"
1075
- raise_error("List API keys failed", res)
1076
- end
1077
- js = checked_json(body, %w[apikeys])
1078
- return js['apikeys']
1079
- end
1080
-
1081
- # => true
1082
- def add_apikey(user)
1083
- code, body, res = post("/v3/user/apikey/add/#{e user}")
1084
- if code != "200"
1085
- raise_error("Adding API key failed", res)
1086
- end
1087
- return true
1088
- end
1089
-
1090
- # => true
1091
- def remove_apikey(user, apikey)
1092
- params = {'apikey' => apikey}
1093
- code, body, res = post("/v3/user/apikey/remove/#{e user}", params)
1094
- if code != "200"
1095
- raise_error("Removing API key failed", res)
1096
- end
1097
- return true
1098
- end
1099
-
1100
- # => true
1101
- def change_password(user, password)
1102
- params = {'password' => password}
1103
- code, body, res = post("/v3/user/password/change/#{e user}", params)
1104
- if code != "200"
1105
- raise_error("Changing password failed", res)
1106
- end
1107
- return true
1108
- end
1109
-
1110
- # => true
1111
- def change_my_password(old_password, password)
1112
- params = {'old_password' => old_password, 'password' => password}
1113
- code, body, res = post("/v3/user/password/change", params)
1114
- if code != "200"
1115
- raise_error("Changing password failed", res)
1116
- end
1117
- return true
1118
- end
1119
-
1120
-
1121
- ####
1122
- ## Access Control API
1123
- ##
1124
-
1125
- def grant_access_control(subject, action, scope, grant_option)
1126
- params = {'subject'=>subject, 'action'=>action, 'scope'=>scope, 'grant_option'=>grant_option.to_s}
1127
- code, body, res = post("/v3/acl/grant", params)
1128
- if code != "200"
1129
- raise_error("Granting access control failed", res)
1130
- end
1131
- return true
1132
- end
1133
-
1134
- def revoke_access_control(subject, action, scope)
1135
- params = {'subject'=>subject, 'action'=>action, 'scope'=>scope}
1136
- code, body, res = post("/v3/acl/revoke", params)
1137
- if code != "200"
1138
- raise_error("Revoking access control failed", res)
1139
- end
1140
- return true
1141
- end
1142
-
1143
- # [true, [{subject:String,action:String,scope:String}]]
1144
- def test_access_control(user, action, scope)
1145
- params = {'user'=>user, 'action'=>action, 'scope'=>scope}
1146
- code, body, res = get("/v3/acl/test", params)
1147
- if code != "200"
1148
- raise_error("Testing access control failed", res)
1149
- end
1150
- js = checked_json(body, %w[permission access_controls])
1151
- perm = js["permission"]
1152
- acl = js["access_controls"].map {|roleinfo|
1153
- subject = roleinfo['subject']
1154
- action = roleinfo['action']
1155
- scope = roleinfo['scope']
1156
- [name, action, scope]
1157
- }
1158
- return perm, acl
1159
- end
1160
-
1161
- # [{subject:String,action:String,scope:String}]
1162
- def list_access_controls
1163
- code, body, res = get("/v3/acl/list")
1164
- if code != "200"
1165
- raise_error("Listing access control failed", res)
1166
- end
1167
- js = checked_json(body, %w[access_controls])
1168
- acl = js["access_controls"].map {|roleinfo|
1169
- subject = roleinfo['subject']
1170
- action = roleinfo['action']
1171
- scope = roleinfo['scope']
1172
- grant_option = roleinfo['grant_option']
1173
- [subject, action, scope, grant_option]
1174
- }
1175
- return acl
1176
- end
1177
-
1178
- ####
1179
- ## Server Status API
1180
- ##
1181
-
1182
- # => status:String
1183
- def server_status
1184
- code, body, res = get('/v3/system/server_status')
1185
- if code != "200"
1186
- return "Server is down (#{code})"
1187
- end
1188
- js = checked_json(body, %w[status])
1189
- status = js['status']
1190
- return status
1191
- end
1192
-
1193
214
  def ssl_ca_file=(ssl_ca_file)
1194
215
  @ssl_ca_file = ssl_ca_file
1195
216
  end
1196
217
 
1197
- private
218
+ private
219
+
1198
220
  module DeflateReadBodyMixin
1199
221
  attr_accessor :gzip
1200
222
 
@@ -1303,7 +325,6 @@ class API
1303
325
  body = response.body
1304
326
  unless block
1305
327
  if ce = response.header['content-encoding']
1306
- require 'zlib'
1307
328
  if ce == 'gzip'
1308
329
  infl = Zlib::Inflate.new(Zlib::MAX_WBITS + 16)
1309
330
  begin
@@ -1377,7 +398,11 @@ class API
1377
398
  retry_delay *= 2
1378
399
  retry
1379
400
  else
1380
- $stderr.puts "Retrying stopped after #{@max_cumul_retry_delay} seconds."
401
+ if @retry_post_requests
402
+ $stderr.puts "Retrying stopped after #{@max_cumul_retry_delay} seconds."
403
+ else
404
+ $stderr.puts ""
405
+ end
1381
406
  raise e
1382
407
  end
1383
408
  rescue => e
@@ -1434,7 +459,7 @@ class API
1434
459
 
1435
460
  def build_endpoint(url, host)
1436
461
  schema = @ssl ? 'https' : 'http'
1437
- "#{schema}://#{host}:#{@port}/#{@base_path + url}"
462
+ "#{schema}://#{host}:#{@port}#{@base_path + url}"
1438
463
  end
1439
464
 
1440
465
  def guard_no_sslv3
@@ -1543,9 +568,14 @@ class API
1543
568
  # TODO error
1544
569
  end
1545
570
 
1546
- def e(s)
1547
- require 'cgi'
1548
- CGI.escape(s.to_s)
571
+ if ''.respond_to?(:encode)
572
+ def e(s)
573
+ CGI.escape(s.to_s.encode("UTF-8"))
574
+ end
575
+ else
576
+ def e(s)
577
+ CGI.escape(s.to_s)
578
+ end
1549
579
  end
1550
580
 
1551
581
  def checked_json(body, required)
@@ -1568,22 +598,3 @@ class API
1568
598
  end
1569
599
 
1570
600
  end # module TreasureData
1571
-
1572
- require 'openssl'
1573
- module OpenSSL
1574
- module SSL
1575
- class SSLContext
1576
-
1577
- # For disabling SSLv3 connection in favor of POODLE Attack protection
1578
- #
1579
- # Allow 'options' customize through Thread local storage since
1580
- # Net::HTTP does not support 'options' configuration.
1581
- #
1582
- alias original_set_params set_params
1583
- def set_params(params={})
1584
- original_set_params(params)
1585
- self.options |= OP_NO_SSLv3 if Thread.current[:SET_SSL_OP_NO_SSLv3]
1586
- end
1587
- end
1588
- end
1589
- end