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.
@@ -426,13 +426,15 @@ end
426
426
 
427
427
 
428
428
  class ScheduledJob < Job
429
+ attr_reader :scheduled_at
430
+
429
431
  def initialize(client, scheduled_at, *super_args)
430
432
  super(client, *super_args)
431
- @scheduled_at = scheduled_at
432
- end
433
-
434
- def scheduled_at
435
- @scheduled_at ? Time.parse(@scheduled_at) : nil
433
+ if scheduled_at.to_s.empty?
434
+ @scheduled_at = nil
435
+ else
436
+ @scheduled_at = Time.parse(scheduled_at) rescue nil
437
+ end
436
438
  end
437
439
  end
438
440
 
@@ -1,5 +1,5 @@
1
1
  module TreasureData
2
2
  class Client
3
- VERSION = '0.8.67'
3
+ VERSION = '0.8.68'
4
4
  end
5
5
  end
@@ -0,0 +1,18 @@
1
+ require 'openssl'
2
+ module OpenSSL
3
+ module SSL
4
+ class SSLContext
5
+
6
+ # For disabling SSLv3 connection in favor of POODLE Attack protection
7
+ #
8
+ # Allow 'options' customize through Thread local storage since
9
+ # Net::HTTP does not support 'options' configuration.
10
+ #
11
+ alias original_set_params set_params
12
+ def set_params(params={})
13
+ original_set_params(params)
14
+ self.options |= OP_NO_SSLv3 if Thread.current[:SET_SSL_OP_NO_SSLv3]
15
+ end
16
+ end
17
+ end
18
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,13 @@
1
1
  require 'rubygems'
2
2
 
3
- begin
4
- require 'simplecov'
5
- SimpleCov.start
6
- rescue LoadError
3
+ if ENV['SIMPLE_COV']
4
+ begin
5
+ require 'simplecov'
6
+ SimpleCov.start do
7
+ add_filter '/spec/'
8
+ end
9
+ rescue LoadError
10
+ end
7
11
  end
8
12
 
9
13
  require 'rspec'
@@ -0,0 +1,37 @@
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
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'td/client/spec_resources'
3
+ require 'json'
4
+
5
+ describe 'Account API' do
6
+ include_context 'spec symbols'
7
+ include_context 'common helper'
8
+
9
+ let :api do
10
+ API.new(nil)
11
+ end
12
+
13
+ describe 'show_account' do
14
+ it 'returns account properties' do
15
+ stub_api_request(:get, "/v3/account/show").
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.should == [1, 0, 2, 3, 4, "2014-12-14T17:24:00+0900"]
18
+ end
19
+ end
20
+
21
+ describe 'account_core_utilization' do
22
+ it 'returns core utilization' do
23
+ from = '2014-12-01T00:00:00+0900'
24
+ to = '2015-01-01T00:00:00+0900'
25
+ stub_api_request(:get, "/v3/account/core_utilization", :query => {'from' => from, 'to' => to}).
26
+ to_return(:body => {'from' => from, 'to' => to, 'interval' => 1, 'history' => ['dummy']}.to_json)
27
+ r = api.account_core_utilization(from, to)
28
+ r[0].should == Time.parse(from)
29
+ r[1].should == Time.parse(to)
30
+ r[2].should == 1
31
+ r[3].should == ['dummy']
32
+ end
33
+ end
34
+ end
@@ -20,13 +20,16 @@ describe 'API SSL connection' do
20
20
  }.to raise_error OpenSSL::SSL::SSLError
21
21
  end
22
22
 
23
- it 'should success to connect TLSv1 only server' do
24
- @server = setup_server(:TLSv1)
25
- api = API.new(nil, :endpoint => "https://localhost:#{@serverport}", :retry_post_requests => false)
26
- api.ssl_ca_file = File.join(DIR, 'ca-all.cert')
27
- expect {
28
- api.delete_database('no_such_database')
29
- }.to raise_error TreasureData::NotFoundError
23
+ # 1.8.7's net/http does not call SSLContext#set_params. Use 1.8.7 where you don't care security.
24
+ if RUBY_VERSION >= '1.9.0'
25
+ it 'should success to connect TLSv1 only server' do
26
+ @server = setup_server(:TLSv1)
27
+ api = API.new(nil, :endpoint => "https://localhost:#{@serverport}", :retry_post_requests => false)
28
+ api.ssl_ca_file = File.join(DIR, 'ca-all.cert')
29
+ expect {
30
+ api.delete_database('no_such_database')
31
+ }.to raise_error TreasureData::NotFoundError
32
+ end
30
33
  end
31
34
 
32
35
  def setup_server(ssl_version)
@@ -1,5 +1,8 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'spec_helper'
2
4
  require 'td/client/spec_resources'
5
+ require 'tempfile'
3
6
 
4
7
  describe 'BulkImport API' do
5
8
  include_context 'spec symbols'
@@ -9,6 +12,18 @@ describe 'BulkImport API' do
9
12
  API.new(nil)
10
13
  end
11
14
 
15
+ let :packed do
16
+ s = StringIO.new
17
+ Zlib::GzipWriter.wrap(s) do |f|
18
+ pk = MessagePack::Packer.new(f)
19
+ pk.write([1, '2', 3.0])
20
+ pk.write([4, '5', 6.0])
21
+ pk.write([7, '8', 9.0])
22
+ pk.flush
23
+ end
24
+ s.string
25
+ end
26
+
12
27
  describe 'create_bulk_import' do
13
28
  it 'should create a new bulk_import' do
14
29
  stub_api_request(:post, "/v3/bulk_import/create/#{e(bi_name)}/#{e(db_name)}/#{e(table_name)}").
@@ -50,4 +65,133 @@ describe 'BulkImport API' do
50
65
  }.to raise_error(TreasureData::APIError, /#{err_msg}/)
51
66
  end
52
67
  end
68
+
69
+ describe 'delete_bulk_import' do
70
+ it 'runs' do
71
+ stub_api_request(:post, '/v3/bulk_import/delete/name').
72
+ with(:body => 'foo=bar')
73
+ api.delete_bulk_import('name', 'foo' => 'bar').should == nil
74
+ end
75
+ end
76
+
77
+ describe 'show_bulk_import' do
78
+ it 'runs' do
79
+ stub_api_request(:get, '/v3/bulk_import/show/name').
80
+ to_return(:body => {'status' => 'status', 'other' => 'other'}.to_json)
81
+ api.show_bulk_import('name')['status'].should == 'status'
82
+ end
83
+ end
84
+
85
+ describe 'list_bulk_imports' do
86
+ it 'runs' do
87
+ stub_api_request(:get, '/v3/bulk_import/list').
88
+ with(:query => 'foo=bar').
89
+ to_return(:body => {'bulk_imports' => %w(1 2 3)}.to_json)
90
+ api.list_bulk_imports('foo' => 'bar').should == %w(1 2 3)
91
+ end
92
+ end
93
+
94
+ describe 'list_bulk_import_parts' do
95
+ it 'runs' do
96
+ stub_api_request(:get, '/v3/bulk_import/list_parts/name').
97
+ with(:query => 'foo=bar').
98
+ to_return(:body => {'parts' => %w(1 2 3)}.to_json)
99
+ api.list_bulk_import_parts('name', 'foo' => 'bar').should == %w(1 2 3)
100
+ end
101
+ end
102
+
103
+ describe 'bulk_import_upload_part' do
104
+ it 'runs' do
105
+ t = Tempfile.new('bulk_import_spec')
106
+ File.open(t.path, 'w') do |f|
107
+ f << '12345'
108
+ end
109
+ stub_request(:put, 'http://api.treasure-data.com/v3/bulk_import/upload_part/name/part').
110
+ with(:body => '12345')
111
+ File.open(t.path) do |f|
112
+ api.bulk_import_upload_part('name', 'part', f, 5).should == nil
113
+ end
114
+ end
115
+
116
+ if ''.respond_to?(:encode)
117
+ it 'encodes part_name in UTF-8' do
118
+ t = Tempfile.new('bulk_import_spec')
119
+ File.open(t.path, 'w') do |f|
120
+ f << '12345'
121
+ end
122
+ stub_request(:put, 'http://api.treasure-data.com/v3/bulk_import/upload_part/name/' + CGI.escape('日本語(Japanese)'.encode('UTF-8'))).
123
+ with(:body => '12345')
124
+ File.open(t.path) do |f|
125
+ api.bulk_import_upload_part('name', '日本語(Japanese)'.encode('Windows-31J'), f, 5).should == nil
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ describe 'bulk_import_delete_part' do
132
+ it 'runs' do
133
+ stub_api_request(:post, '/v3/bulk_import/delete_part/name/part')
134
+ api.bulk_import_delete_part('name', 'part').should == nil
135
+ end
136
+ end
137
+
138
+ describe 'freeze_bulk_import' do
139
+ it 'runs' do
140
+ stub_api_request(:post, '/v3/bulk_import/freeze/name')
141
+ api.freeze_bulk_import('name').should == nil
142
+ end
143
+ end
144
+
145
+ describe 'unfreeze_bulk_import' do
146
+ it 'runs' do
147
+ stub_api_request(:post, '/v3/bulk_import/unfreeze/name')
148
+ api.unfreeze_bulk_import('name').should == nil
149
+ end
150
+ end
151
+
152
+ describe 'perform_bulk_import' do
153
+ it 'runs' do
154
+ stub_api_request(:post, '/v3/bulk_import/perform/name').
155
+ to_return(:body => {'job_id' => 12345}.to_json)
156
+ api.perform_bulk_import('name').should == '12345'
157
+ end
158
+ end
159
+
160
+ describe 'commit_bulk_import' do
161
+ it 'runs' do
162
+ stub_api_request(:post, '/v3/bulk_import/commit/name').
163
+ to_return(:body => {'job_id' => 12345}.to_json)
164
+ api.commit_bulk_import('name').should == nil
165
+ end
166
+ end
167
+
168
+ describe 'bulk_import_error_records' do
169
+ it 'returns [] on empty' do
170
+ stub_api_request(:get, '/v3/bulk_import/error_records/name').
171
+ to_return(:body => '')
172
+ api.bulk_import_error_records('name').should == []
173
+ end
174
+
175
+ it 'returns nil on empty if block given' do
176
+ stub_api_request(:get, '/v3/bulk_import/error_records/name').
177
+ to_return(:body => '')
178
+ api.bulk_import_error_records('name'){}.should == nil
179
+ end
180
+
181
+ it 'returns unpacked result' do
182
+ stub_api_request(:get, '/v3/bulk_import/error_records/name').
183
+ to_return(:body => packed)
184
+ api.bulk_import_error_records('name').should == [[1, '2', 3.0], [4, '5', 6.0], [7, '8', 9.0]]
185
+ end
186
+
187
+ it 'yields unpacked result if block given' do
188
+ stub_api_request(:get, '/v3/bulk_import/error_records/name').
189
+ to_return(:body => packed)
190
+ result = []
191
+ api.bulk_import_error_records('name') do |row|
192
+ result << row
193
+ end
194
+ result.should == [[1, '2', 3.0], [4, '5', 6.0], [7, '8', 9.0]]
195
+ end
196
+ end
53
197
  end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+ require 'td/client/spec_resources'
3
+ require 'json'
4
+ require 'tempfile'
5
+
6
+ describe 'Import API' do
7
+ include_context 'spec symbols'
8
+ include_context 'common helper'
9
+
10
+ let :api do
11
+ API.new(nil, :endpoint => 'https://api.treasuredata.com')
12
+ end
13
+
14
+ let :api_old do
15
+ API.new(nil, :endpoint => 'http://api.treasure-data.com')
16
+ end
17
+
18
+ describe 'import' do
19
+ it 'runs with unique_id' do
20
+ t = Tempfile.new('import_api_spec')
21
+ File.open(t.path, 'w') do |f|
22
+ f << '12345'
23
+ end
24
+ stub_request(:put, "https://api-import.treasuredata.com/v3/table/import_with_id/db/table/unique_id/format").
25
+ with(:body => '12345').
26
+ to_return(:body => '{"elapsed_time":"1.23"}')
27
+ File.open(t.path) do |f|
28
+ api.import('db', 'table', 'format', f, 5, 'unique_id').should == 1.23
29
+ end
30
+ end
31
+
32
+ it 'runs without unique_id' do
33
+ t = Tempfile.new('import_api_spec')
34
+ File.open(t.path, 'w') do |f|
35
+ f << '12345'
36
+ end
37
+ stub_request(:put, "https://api-import.treasuredata.com/v3/table/import/db/table/format").
38
+ with(:body => '12345').
39
+ to_return(:body => '{"elapsed_time":"1.23"}')
40
+ File.open(t.path) do |f|
41
+ api.import('db', 'table', 'format', f, 5).should == 1.23
42
+ end
43
+ end
44
+
45
+ it 'runs for old endpoint' do
46
+ t = Tempfile.new('import_api_spec')
47
+ File.open(t.path, 'w') do |f|
48
+ f << '12345'
49
+ end
50
+ stub_request(:put, "http://api-import.treasure-data.com/v3/table/import/db/table/format").
51
+ with(:body => '12345').
52
+ to_return(:body => '{"elapsed_time":"1.23"}')
53
+ File.open(t.path) do |f|
54
+ api_old.import('db', 'table', 'format', f, 5).should == 1.23
55
+ end
56
+ end
57
+
58
+ it 'raises APIError' do
59
+ t = Tempfile.new('import_api_spec')
60
+ File.open(t.path, 'w') do |f|
61
+ f << '12345'
62
+ end
63
+ stub_request(:put, "https://api-import.treasuredata.com/v3/table/import/db/table/format").
64
+ with(:body => '12345').
65
+ to_return(:status => 500)
66
+ File.open(t.path) do |f|
67
+ expect {
68
+ api.import('db', 'table', 'format', f, 5)
69
+ }.to raise_error(TreasureData::APIError)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,6 +1,8 @@
1
1
  require 'spec_helper'
2
2
  require 'td/client/spec_resources'
3
3
  require 'json'
4
+ require 'webrick'
5
+ require 'logger'
4
6
 
5
7
  describe 'Job API' do
6
8
  include_context 'spec symbols'
@@ -11,7 +13,7 @@ describe 'Job API' do
11
13
  end
12
14
 
13
15
  let :api_with_short_retry do
14
- API.new(nil, {:max_cumul_retry_delay => 10})
16
+ API.new(nil, {:max_cumul_retry_delay => 0})
15
17
  end
16
18
 
17
19
  describe 'list_jobs' do
@@ -159,4 +161,125 @@ describe 'Job API' do
159
161
  job_id.should == '1'
160
162
  end
161
163
  end
164
+
165
+ describe 'job_result' do
166
+ let :packed do
167
+ s = StringIO.new
168
+ pk = MessagePack::Packer.new(s)
169
+ pk.write('hello')
170
+ pk.write('world')
171
+ pk.flush
172
+ s.string
173
+ end
174
+
175
+ it 'returns job result' do
176
+ stub_api_request(:get, '/v3/job/result/12345').
177
+ with(:query => {'format' => 'msgpack'}).
178
+ to_return(:body => packed)
179
+ api.job_result(12345).should == ['hello', 'world']
180
+ end
181
+ end
182
+
183
+ describe 'job_result_format' do
184
+ let :packed do
185
+ s = StringIO.new
186
+ Zlib::GzipWriter.wrap(s) do |f|
187
+ f << ['hello', 'world'].to_json
188
+ end
189
+ s.string
190
+ end
191
+
192
+ it 'returns formatted job result' do
193
+ stub_api_request(:get, '/v3/job/result/12345').
194
+ with(:query => {'format' => 'json'}).
195
+ to_return(
196
+ :headers => {'Content-Encoding' => 'gzip'},
197
+ :body => packed
198
+ )
199
+ api.job_result_format(12345, 'json').should == ['hello', 'world'].to_json
200
+ end
201
+
202
+ it 'writes formatted job result' do
203
+ stub_api_request(:get, '/v3/job/result/12345').
204
+ with(:query => {'format' => 'json'}).
205
+ to_return(
206
+ :headers => {'Content-Encoding' => 'gzip'},
207
+ :body => packed
208
+ )
209
+ s = StringIO.new
210
+ api.job_result_format(12345, 'json', s)
211
+ s.string.should == ['hello', 'world'].to_json
212
+ end
213
+ end
214
+
215
+ describe 'job_result_each' do
216
+ let :packed do
217
+ s = StringIO.new
218
+ Zlib::GzipWriter.wrap(s) do |f|
219
+ pk = MessagePack::Packer.new(f)
220
+ pk.write('hello')
221
+ pk.write('world')
222
+ pk.flush
223
+ end
224
+ s.string
225
+ end
226
+
227
+ it 'yields job result for each row' do
228
+ stub_api_request(:get, '/v3/job/result/12345').
229
+ with(:query => {'format' => 'msgpack'}).
230
+ to_return(
231
+ :headers => {'Content-Encoding' => 'gzip'},
232
+ :body => packed
233
+ )
234
+ result = []
235
+ api.job_result_each(12345) do |row|
236
+ result << row
237
+ end
238
+ result.should == ['hello', 'world']
239
+ end
240
+ end
241
+
242
+ describe 'job_result_each_with_compr_size' do
243
+ let :packed do
244
+ s = StringIO.new
245
+ Zlib::GzipWriter.wrap(s) do |f|
246
+ pk = MessagePack::Packer.new(f)
247
+ pk.write('hello')
248
+ pk.write('world')
249
+ pk.flush
250
+ end
251
+ s.string
252
+ end
253
+
254
+ it 'yields job result for each row with progress' do
255
+ stub_api_request(:get, '/v3/job/result/12345').
256
+ with(:query => {'format' => 'msgpack'}).
257
+ to_return(
258
+ :headers => {'Content-Encoding' => 'gzip'},
259
+ :body => packed
260
+ )
261
+ result = []
262
+ api.job_result_each_with_compr_size(12345) do |row, size|
263
+ result << [row, size]
264
+ end
265
+ result.should == [['hello', 32], ['world', 32]]
266
+ end
267
+ end
268
+
269
+ describe 'job_result_raw' do
270
+ it 'returns raw result' do
271
+ stub_api_request(:get, '/v3/job/result/12345').
272
+ with(:query => {'format' => 'json'}).
273
+ to_return(:body => 'raw binary')
274
+ api.job_result_raw(12345, 'json').should == 'raw binary'
275
+ end
276
+ end
277
+
278
+ describe 'kill' do
279
+ it 'kills a job' do
280
+ stub_api_request(:post, '/v3/job/kill/12345').
281
+ to_return(:body => {'former_status' => 'status'}.to_json)
282
+ api.kill(12345).should == 'status'
283
+ end
284
+ end
162
285
  end