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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -0
- data/ChangeLog +15 -0
- data/lib/td/command/common.rb +96 -26
- data/lib/td/command/connector.rb +58 -36
- data/lib/td/command/import.rb +162 -1
- data/lib/td/command/job.rb +33 -23
- data/lib/td/command/list.rb +3 -2
- data/lib/td/command/query.rb +3 -5
- data/lib/td/command/runner.rb +1 -2
- data/lib/td/command/table.rb +29 -18
- data/lib/td/compact_format_yamler.rb +41 -0
- data/lib/td/connector_config_normalizer.rb +32 -0
- data/lib/td/version.rb +1 -1
- data/spec/spec_helper.rb +18 -0
- data/spec/td/command/connector_spec.rb +153 -0
- data/spec/td/command/import_spec.rb +179 -0
- data/spec/td/command/job_spec.rb +1 -1
- data/spec/td/command/table_spec.rb +113 -0
- data/spec/td/common_spec.rb +23 -1
- data/spec/td/compact_format_yamler_spec.rb +38 -0
- data/spec/td/connector_config_normalizer_spec.rb +62 -0
- data/td.gemspec +2 -1
- metadata +25 -5
@@ -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
|
data/spec/td/command/job_spec.rb
CHANGED
@@ -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([
|
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
|
data/spec/td/common_spec.rb
CHANGED
@@ -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(
|
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.
|
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"
|