td 0.12.0 → 0.13.0

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.
@@ -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"