td-client 0.8.67 → 0.8.68

Sign up to get free protection for your applications and to get access to all the features.
@@ -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