td 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -178,5 +178,184 @@ module TreasureData::Command
178
178
  expect(stderr_io.string).to include import_name
179
179
  end
180
180
  end
181
+
182
+ describe '#import_config' do
183
+ include_context 'quiet_out'
184
+
185
+ let :command do
186
+ Class.new { include TreasureData::Command }.new
187
+ end
188
+ let :option do
189
+ List::CommandParser.new("import:config", args, [], nil, arguments, true)
190
+ end
191
+ let :out_file do
192
+ Tempfile.new("seed.yml").tap {|s| s.close }
193
+ end
194
+ let(:endpoint) { 'http://example.com' }
195
+ let(:apikey) { '1234ABCDEFGHIJKLMN' }
196
+
197
+ before do
198
+ TreasureData::Config.stub(:endpoint).and_return(endpoint)
199
+ TreasureData::Config.stub(:apikey).and_return(apikey)
200
+ end
201
+
202
+ context 'unknown format' do
203
+ let(:args) { ['url'] }
204
+ let(:arguments) { ['localhost', '--format', 'msgpack', '-o', out_file.path] }
205
+
206
+ it 'exit command' do
207
+ expect {
208
+ command.import_config(option)
209
+ }.to raise_error TreasureData::Command::ParameterConfigurationError
210
+ end
211
+ end
212
+
213
+ context 'support format' do
214
+ let(:td_output_config) {
215
+ {
216
+ 'type' => 'td',
217
+ 'endpoint' => TreasureData::Config.endpoint,
218
+ 'apikey' => TreasureData::Config.apikey,
219
+ 'database' => '',
220
+ 'table' => '',
221
+ }
222
+ }
223
+
224
+ before do
225
+ command.import_config(option)
226
+ end
227
+
228
+ subject(:generated_config) { YAML.load_file(out_file.path) }
229
+
230
+ %w(csv tsv).each do |format|
231
+ context "--format #{format}" do
232
+ let(:args) { ['url'] }
233
+ context 'use path' do
234
+ let(:path_prefix) { 'path/to/prefix_' }
235
+ let(:arguments) { ["#{path_prefix}*.#{format}", '--format', format, '-o', out_file.path] }
236
+
237
+ it 'generate configuration file' do
238
+ expect(generated_config).to eq({
239
+ 'in' => {
240
+ 'type' => 'file',
241
+ 'decorders' => [{'type' => 'gzip'}],
242
+ 'path_prefix' => path_prefix,
243
+ },
244
+ 'out' => td_output_config
245
+ })
246
+ end
247
+ end
248
+
249
+ context 'use s3 scheme' do
250
+ let(:s3_access_key) { 'ABCDEFGHIJKLMN' }
251
+ let(:s3_secret_key) { '1234ABCDEFGHIJKLMN' }
252
+ let(:buckt_name) { 'my_bucket' }
253
+ let(:path_prefix) { 'path/to/prefix_' }
254
+ let(:s3_url) { "s3://#{s3_access_key}:#{s3_secret_key}@/#{buckt_name}/#{path_prefix}*.#{format}" }
255
+ let(:arguments) { [s3_url, '--format', format, '-o', out_file.path] }
256
+
257
+ it 'generate configuration file' do
258
+ expect(generated_config).to eq({
259
+ 'in' => {
260
+ 'type' => 's3',
261
+ 'access_key_id' => s3_access_key,
262
+ 'secret_access_key' => s3_secret_key,
263
+ 'bucket' => buckt_name,
264
+ 'path_prefix' => path_prefix,
265
+ },
266
+ 'out' => {
267
+ 'mode' => 'append'
268
+ }
269
+ })
270
+ end
271
+ end
272
+ end
273
+ end
274
+
275
+ context 'format is mysql' do
276
+ let(:format) { 'mysql' }
277
+ let(:host) { 'localhost' }
278
+ let(:database) { 'database' }
279
+ let(:user) { 'my_user' }
280
+ let(:password) { 'my_password' }
281
+ let(:table) { 'my_table' }
282
+
283
+ let(:expected_config) {
284
+ {
285
+ 'in' => {
286
+ 'type' => 'mysql',
287
+ 'host' => host,
288
+ 'port' => port,
289
+ 'database' => database,
290
+ 'user' => user,
291
+ 'password' => password,
292
+ 'table' => table,
293
+ 'select' => "*",
294
+ },
295
+ 'out' => td_output_config
296
+ }
297
+ }
298
+
299
+ context 'like import:prepare arguments' do
300
+ let(:args) { ['url'] }
301
+ let(:arguments) { [table, '--db-url', mysql_url, '--db-user', user, '--db-password', password, '--format', 'mysql', '-o', out_file.path] }
302
+
303
+ context 'scheme is jdbc' do
304
+ let(:port) { 3333 }
305
+ let(:mysql_url) { "jdbc:mysql://#{host}:#{port}/#{database}" }
306
+
307
+ it 'generate configuration file' do
308
+ expect(generated_config).to eq expected_config
309
+ end
310
+ end
311
+
312
+ context 'scheme is mysql' do
313
+ context 'with port' do
314
+ let(:port) { 3333 }
315
+ let(:mysql_url) { "mysql://#{host}:#{port}/#{database}" }
316
+
317
+ it 'generate configuration file' do
318
+ expect(generated_config).to eq expected_config
319
+ end
320
+ end
321
+
322
+ context 'without port' do
323
+ let(:mysql_url) { "mysql://#{host}/#{database}" }
324
+ let(:port) { 3306 }
325
+
326
+ it 'generate configuration file' do
327
+ expect(generated_config).to eq expected_config
328
+ end
329
+ end
330
+ end
331
+ end
332
+
333
+ context 'like import:upload arguments' do
334
+ let(:args) { ['session', 'url'] }
335
+ let(:arguments) { ['session', table, '--db-url', mysql_url, '--db-user', user, '--db-password', password, '--format', 'mysql', '-o', out_file.path] }
336
+ let(:mysql_url) { "jdbc:mysql://#{host}/#{database}" }
337
+ let(:port) { 3306 }
338
+
339
+ it 'generate configuration file' do
340
+ expect(generated_config).to eq expected_config
341
+ end
342
+ end
343
+ end
344
+ end
345
+
346
+ context 'not migrate options' do
347
+ %w(--columns --column-header).each do |opt|
348
+ context "with #{opt}" do
349
+ let(:args) { ['url'] }
350
+ let(:arguments) { ["path/to/prefix_*.csv", '--format', 'csv', opt, 'col1,col2'] }
351
+
352
+ it "#{opt} is not migrate" do
353
+ expect { command.import_config(option) }.not_to raise_error
354
+ expect(stderr_io.string).to include 'not migrate. Please, edit config file after execute guess commands.'
355
+ end
356
+ end
357
+ end
358
+ end
359
+ end
181
360
  end
182
361
  end
@@ -146,7 +146,7 @@ module TreasureData::Command
146
146
 
147
147
  it 'supports json output' do
148
148
  command.send(:show_result, job, file, nil, 'json', { header: true })
149
- File.read(file.path).should == %Q([[null,2.0,{"key":3}]])
149
+ File.read(file.path).should == %Q([{"c0":null,"c1":2.0,"v":{"key":3},"c3":null}])
150
150
  end
151
151
 
152
152
  it 'supports csv output' do
@@ -217,5 +217,118 @@ module TreasureData::Command
217
217
  end
218
218
  end
219
219
  end
220
+
221
+ describe '#table_rename' do
222
+ include_context 'quiet_out'
223
+
224
+ let(:db_name) { 'database' }
225
+ let(:from_table_name) { 'from_table' }
226
+ let(:dest_table_name) { 'dest_table' }
227
+ let(:client) { double('client') }
228
+ let(:database) { double('database') }
229
+ let(:command) { Class.new { include TreasureData::Command }.new }
230
+ let(:option) {
231
+ List::CommandParser.new('table:rename', [], %w(db_name from_table_name dest_table_name), false, cmd_args, true)
232
+ }
233
+
234
+ before do
235
+ command.stub(:get_client) { client }
236
+ command.stub(:get_database).with(client, db_name).and_return(database)
237
+ end
238
+
239
+ context "from table isn't exists" do
240
+ let(:cmd_args) { [db_name, from_table_name, dest_table_name] }
241
+
242
+ before do
243
+ database.stub(:table).with(from_table_name).and_return { raise }
244
+ end
245
+
246
+ it "can't rename table" do
247
+ command.should_receive(:exit).with(1).and_return { raise CallSystemExitError }
248
+
249
+ expect { command.table_rename(option) }.to raise_error
250
+ expect(stderr_io.string).to include(from_table_name)
251
+ expect(stderr_io.string).not_to include(dest_table_name)
252
+ end
253
+ end
254
+
255
+ context 'overwrite is false' do
256
+ let(:cmd_args) { [db_name, from_table_name, dest_table_name] }
257
+
258
+ before do
259
+ database.stub(:table).with(from_table_name).and_return
260
+ end
261
+
262
+ context "dest_table isn't exists" do
263
+ before do
264
+ database.stub(:table).with(dest_table_name).and_return { raise }
265
+ end
266
+
267
+ it 'create dest table and rename table' do
268
+ client.should_receive(:create_log_table).with(db_name, dest_table_name)
269
+ client.should_receive(:swap_table).with(db_name, from_table_name, dest_table_name)
270
+ client.should_receive(:delete_table).with(db_name, from_table_name)
271
+
272
+ command.table_rename(option)
273
+
274
+ expect(stderr_io.string).to include(from_table_name)
275
+ expect(stderr_io.string).to include(dest_table_name)
276
+ end
277
+ end
278
+
279
+ context "dest_tableis is exist" do
280
+ before do
281
+ database.stub(:table).with(dest_table_name).and_return
282
+ end
283
+
284
+ it "can't rename table" do
285
+ command.should_receive(:exit).with(1).and_return { raise CallSystemExitError }
286
+
287
+ expect { command.table_rename(option) }.to raise_error
288
+ expect(stderr_io.string).not_to include(from_table_name)
289
+ expect(stderr_io.string).to include(dest_table_name)
290
+ end
291
+ end
292
+ end
293
+
294
+ context 'overwrite is true' do
295
+ let(:cmd_args) { [db_name, from_table_name, dest_table_name, '--overwrite'] }
296
+
297
+ before do
298
+ database.stub(:table).with(from_table_name).and_return
299
+ end
300
+
301
+ context "dest_table isn't exists" do
302
+ before do
303
+ database.stub(:table).with(dest_table_name).and_return { raise }
304
+ end
305
+
306
+ it 'create dest table and rename table' do
307
+ client.should_receive(:create_log_table).with(db_name, dest_table_name)
308
+ client.should_receive(:swap_table).with(db_name, from_table_name, dest_table_name)
309
+ client.should_receive(:delete_table).with(db_name, from_table_name)
310
+
311
+ command.table_rename(option)
312
+ expect(stderr_io.string).to include(from_table_name)
313
+ expect(stderr_io.string).to include(dest_table_name)
314
+ end
315
+ end
316
+
317
+ context "dest_tableis is exist" do
318
+ before do
319
+ database.stub(:table).with(dest_table_name).and_return
320
+ end
321
+
322
+ it 'overwrite dest table' do
323
+ client.should_receive(:swap_table).with(db_name, from_table_name, dest_table_name)
324
+ client.should_receive(:delete_table).with(db_name, from_table_name)
325
+
326
+ command.table_rename(option)
327
+ expect(stderr_io.string).to include(from_table_name)
328
+ expect(stderr_io.string).to include(dest_table_name)
329
+ end
330
+ end
331
+ end
332
+ end
220
333
  end
221
334
  end
@@ -144,7 +144,29 @@ module TreasureData::Command
144
144
  size_increments = 2
145
145
  curr_size = 0
146
146
  while (curr_size += size_increments) < size do
147
- indicator.update(size_increments)
147
+ indicator.update(curr_size)
148
+ sleep(0.05)
149
+ end
150
+ indicator.finish
151
+ end
152
+
153
+ it "shows in only current byte size if total is nil" do
154
+ indicator = TreasureData::Command::SizeBasedDownloadProgressIndicator.new("Downloading", nil)
155
+ size_increments = 2
156
+ curr_size = 0
157
+ while (curr_size += size_increments) < 200 do
158
+ indicator.update(curr_size)
159
+ sleep(0.05)
160
+ end
161
+ indicator.finish
162
+ end
163
+
164
+ it "shows in only current byte size if total is 0" do
165
+ indicator = TreasureData::Command::SizeBasedDownloadProgressIndicator.new("Downloading", 0)
166
+ size_increments = 2
167
+ curr_size = 0
168
+ while (curr_size += size_increments) < 200 do
169
+ indicator.update(curr_size)
148
170
  sleep(0.05)
149
171
  end
150
172
  indicator.finish
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'td/compact_format_yamler'
3
+
4
+ module TreasureData
5
+ describe TreasureData::CompactFormatYamler do
6
+ describe '.dump' do
7
+ let(:data) {
8
+ {
9
+ 'a' => {
10
+ 'b' => {
11
+ 'c' => 1,
12
+ 'd' => 'e'
13
+ },
14
+ 'f' => [1, 2, 3]
15
+ }
16
+ }
17
+ }
18
+
19
+ let(:comapct_format_yaml) {
20
+ <<-EOS
21
+ ---
22
+ a:
23
+ b: {c: 1, d: e}
24
+ f:
25
+ - 1
26
+ - 2
27
+ - 3
28
+ EOS
29
+ }
30
+
31
+ subject { TreasureData::CompactFormatYamler.dump data }
32
+
33
+ it 'use compact format for deepest Hash' do
34
+ expect(subject).to eq comapct_format_yaml
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+ require 'td/connector_config_normalizer'
3
+ require 'td/client/api_error'
4
+
5
+ module TreasureData
6
+ describe ConnectorConfigNormalizer do
7
+ describe '#normalized_config' do
8
+ subject { TreasureData::ConnectorConfigNormalizer.new(config).normalized_config }
9
+
10
+ context 'has key :in' do
11
+ context 'without :out, :exec' do
12
+ let(:config) { {'in' => {'type' => 's3'}} }
13
+
14
+ it { expect(subject).to eq config.merge('out' => {}, 'exec' => {}, 'filters' => []) }
15
+ end
16
+
17
+ context 'with :out, :exec, :filters' do
18
+ let(:config) {
19
+ {
20
+ 'in' => {'type' => 's3'},
21
+ 'out' => {'mode' => 'append'},
22
+ 'exec' => {'guess_plugins' => ['json', 'query_string']},
23
+ 'filters' => [{'type' => 'speedometer'}]
24
+ }
25
+ }
26
+
27
+ it { expect(subject).to eq config }
28
+ end
29
+ end
30
+
31
+ context 'has key :config' do
32
+ context 'with :in' do
33
+ let(:config) {
34
+ { 'config' =>
35
+ {
36
+ 'in' => {'type' => 's3'},
37
+ 'out' => {'mode' => 'append'},
38
+ 'exec' => {'guess_plugins' => ['json', 'query_string']},
39
+ 'filters' => [{'type' => 'speedometer'}]
40
+ }
41
+ }
42
+ }
43
+
44
+ it { expect(subject).to eq config['config'] }
45
+
46
+ end
47
+
48
+ context 'without :in' do
49
+ let(:config) { {'config' => {'type' => 's3'}} }
50
+
51
+ it { expect(subject).to eq({'in' => config['config'], 'out' => {}, 'exec' => {}, 'filters' => []}) }
52
+ end
53
+ end
54
+
55
+ context 'does not have key :in or :config' do
56
+ let(:config) { {'type' => 's3'} }
57
+
58
+ it { expect(subject).to eq({'in' => config, 'out' => {}, 'exec' => {}, 'filters' => []}) }
59
+ end
60
+ end
61
+ end
62
+ end
data/td.gemspec CHANGED
@@ -21,10 +21,11 @@ Gem::Specification.new do |gem|
21
21
  gem.add_dependency "yajl-ruby", "~> 1.1"
22
22
  gem.add_dependency "hirb", ">= 0.4.5"
23
23
  gem.add_dependency "parallel", "~> 0.6.1"
24
- gem.add_dependency "td-client", "~> 0.8.75"
24
+ gem.add_dependency "td-client", "~> 0.8.76"
25
25
  gem.add_dependency "td-logger", "~> 0.3.21"
26
26
  gem.add_dependency "rubyzip", "~> 1.1.7"
27
27
  gem.add_dependency "zip-zip", "~> 0.3"
28
+ gem.add_dependency "ruby-progressbar", "~> 1.7.5"
28
29
  gem.add_development_dependency "rake", "~> 0.9"
29
30
  gem.add_development_dependency "rspec", "~> 2.11.0"
30
31
  gem.add_development_dependency "simplecov", "~> 0.10.0"