td-client 0.8.82 → 0.8.83
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/lib/td/client.rb +3 -3
- data/lib/td/client/api.rb +20 -20
- data/lib/td/client/api/bulk_load.rb +3 -3
- data/lib/td/client/model.rb +46 -12
- data/lib/td/client/version.rb +1 -1
- data/spec/td/client/api_spec.rb +29 -3
- data/spec/td/client/bulk_load_spec.rb +25 -0
- data/spec/td/client/model_job_spec.rb +4 -4
- data/spec/td/client/table_api_spec.rb +8 -8
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3662e5e2dfaa4e0eef6afb81bee1ba5eda81b2f
|
4
|
+
data.tar.gz: 4863761d3f0b4851a94348f776a09d923cec2e85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c40f28abe243f4ad108595155324da7c93f56e57ac81564ffcc7dfba6f310a3a22abae62ae8447eb5a9753e0abdad3cfc0611410606d8ba485e9e173f44d2eb
|
7
|
+
data.tar.gz: d5a1d2e957400ab340b0a5ba91634575f08d27029eb363bc8c9d57ba41a55cbf64b2b87e3846a6f200ea846d99371c8b9a9f80dff513a854c584eac050e455fa
|
data/lib/td/client.rb
CHANGED
@@ -574,9 +574,9 @@ class Client
|
|
574
574
|
@api.bulk_load_show(name)
|
575
575
|
end
|
576
576
|
|
577
|
-
# name: String,
|
578
|
-
def bulk_load_update(name,
|
579
|
-
@api.bulk_load_update(name,
|
577
|
+
# name: String, settings: Hash -> BulkLoad
|
578
|
+
def bulk_load_update(name, settings)
|
579
|
+
@api.bulk_load_update(name, settings)
|
580
580
|
end
|
581
581
|
|
582
582
|
# name: String -> BulkLoad
|
data/lib/td/client/api.rb
CHANGED
@@ -174,7 +174,26 @@ class API
|
|
174
174
|
|
175
175
|
# @param [String] name
|
176
176
|
def self.validate_column_name(name)
|
177
|
-
|
177
|
+
target = 'column'
|
178
|
+
min_len = 1
|
179
|
+
max_len = 128
|
180
|
+
name = name.to_s
|
181
|
+
if name.empty?
|
182
|
+
raise ParameterValidationError,
|
183
|
+
"Empty #{target} name is not allowed"
|
184
|
+
end
|
185
|
+
if name.length < min_len || name.length > max_len
|
186
|
+
raise ParameterValidationError,
|
187
|
+
"#{target.capitalize} name must be between #{min_len} and #{max_len} characters long. Got #{name.length} " +
|
188
|
+
(name.length == 1 ? "character" : "characters") + "."
|
189
|
+
end
|
190
|
+
|
191
|
+
name
|
192
|
+
end
|
193
|
+
|
194
|
+
# @param [String] name
|
195
|
+
def self.validate_sql_alias_name(name)
|
196
|
+
validate_name("sql_alias", 1, 128, name)
|
178
197
|
end
|
179
198
|
|
180
199
|
# @param [String] name
|
@@ -199,25 +218,6 @@ class API
|
|
199
218
|
normalize_database_name(name)
|
200
219
|
end
|
201
220
|
|
202
|
-
# TODO support array types
|
203
|
-
# @param [String] name
|
204
|
-
def self.normalize_type_name(name)
|
205
|
-
case name
|
206
|
-
when /int/i, /integer/i
|
207
|
-
"int"
|
208
|
-
when /long/i, /bigint/i
|
209
|
-
"long"
|
210
|
-
when /string/i
|
211
|
-
"string"
|
212
|
-
when /float/i
|
213
|
-
"float"
|
214
|
-
when /double/i
|
215
|
-
"double"
|
216
|
-
else
|
217
|
-
raise "Type name must either of int, long, string float or double"
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
221
|
# for fluent-plugin-td / td command to check table existence with import onlt user
|
222
222
|
# @return [String]
|
223
223
|
def self.create_empty_gz_data
|
@@ -116,10 +116,10 @@ module BulkLoad
|
|
116
116
|
JSON.load(res.body)
|
117
117
|
end
|
118
118
|
|
119
|
-
# name: String,
|
120
|
-
def bulk_load_update(name,
|
119
|
+
# name: String, settings: Hash -> Hash
|
120
|
+
def bulk_load_update(name, settings)
|
121
121
|
path = session_path(name)
|
122
|
-
res = api { put(path,
|
122
|
+
res = api { put(path, settings.to_json) }
|
123
123
|
unless res.ok?
|
124
124
|
raise_error("BulkLoadSession: #{name} update failed", res)
|
125
125
|
end
|
data/lib/td/client/model.rb
CHANGED
@@ -269,36 +269,62 @@ class Table < Model
|
|
269
269
|
def update_database!
|
270
270
|
@database = @client.database(@db_name)
|
271
271
|
end
|
272
|
+
|
273
|
+
# @return [String]
|
274
|
+
def inspect
|
275
|
+
%[#<%s:%#0#{1.size*2}x @db_name="%s" @table_name="%s">] %
|
276
|
+
[self.class.name, self.__id__*2, @db_name, @table_name]
|
277
|
+
end
|
272
278
|
end
|
273
279
|
|
274
280
|
class Schema
|
275
281
|
class Field
|
276
282
|
# @param [String] name
|
277
283
|
# @param [String] type
|
278
|
-
|
284
|
+
# @param [String] sql_alias
|
285
|
+
def initialize(name, type, sql_alias=nil)
|
286
|
+
if name == 'v' || name == 'time'
|
287
|
+
raise ParameterValidationError, "Column name '#{name}' is reserved."
|
288
|
+
end
|
289
|
+
API.validate_column_name(name)
|
290
|
+
API.validate_sql_alias_name(sql_alias) if sql_alias
|
279
291
|
@name = name
|
280
292
|
@type = type
|
293
|
+
@sql_alias = sql_alias
|
281
294
|
end
|
282
295
|
|
283
296
|
# @!attribute [r] name
|
284
297
|
# @!attribute [r] type
|
285
298
|
attr_reader :name
|
286
299
|
attr_reader :type
|
300
|
+
attr_reader :sql_alias
|
287
301
|
end
|
288
302
|
|
289
|
-
# @param [String]
|
303
|
+
# @param [String] columns
|
290
304
|
# @return [Schema]
|
291
|
-
def self.parse(
|
292
|
-
|
293
|
-
|
294
|
-
|
305
|
+
def self.parse(columns)
|
306
|
+
schema = Schema.new
|
307
|
+
columns.each {|column|
|
308
|
+
unless /\A(?<name>.*)(?::(?<type>[^:]+))(?:@(?<sql_alias>[^:@]+))?\z/ =~ column
|
309
|
+
raise ParameterValidationError, "type must be specified"
|
310
|
+
end
|
311
|
+
schema.add_field(name, type, sql_alias)
|
295
312
|
}
|
296
|
-
|
313
|
+
schema
|
297
314
|
end
|
298
315
|
|
299
316
|
# @param [Array] fields
|
300
317
|
def initialize(fields=[])
|
301
318
|
@fields = fields
|
319
|
+
@names = {}
|
320
|
+
@fields.each do |f|
|
321
|
+
raise ArgumentError, "Column name '#{f.name}' is duplicated." if @names.key?(f.name)
|
322
|
+
@names[f.name] = true
|
323
|
+
if f.sql_alias
|
324
|
+
raise ArgumentError, "SQL Column alias '#{f.sql_alias}' is duplicated." if @names.key?(f.sql_alias)
|
325
|
+
@names[f.sql_alias] = true
|
326
|
+
end
|
327
|
+
end
|
302
328
|
end
|
303
329
|
|
304
330
|
# @!attribute [r] fields
|
@@ -307,8 +333,16 @@ class Schema
|
|
307
333
|
# @param [String] name
|
308
334
|
# @param [String] type
|
309
335
|
# @return [Array]
|
310
|
-
def add_field(name, type)
|
311
|
-
@
|
336
|
+
def add_field(name, type, sql_alias=nil)
|
337
|
+
if @names.key?(name)
|
338
|
+
raise ParameterValidationError, "Column name '#{name}' is duplicated."
|
339
|
+
end
|
340
|
+
@names[name] = true
|
341
|
+
if sql_alias && @names.key?(sql_alias)
|
342
|
+
raise ParameterValidationError, "SQL Column alias '#{sql_alias}' is duplicated."
|
343
|
+
end
|
344
|
+
@names[sql_alias] = true
|
345
|
+
@fields << Field.new(name, type, sql_alias)
|
312
346
|
end
|
313
347
|
|
314
348
|
# @param [Schema] schema
|
@@ -327,14 +361,14 @@ class Schema
|
|
327
361
|
|
328
362
|
# @return [Array<Field>]
|
329
363
|
def to_json(*args)
|
330
|
-
@fields.map {|f| [f.name, f.type] }.to_json(*args)
|
364
|
+
@fields.map {|f| f.sql_alias ? [f.name, f.type, f.sql_alias] : [f.name, f.type] }.to_json(*args)
|
331
365
|
end
|
332
366
|
|
333
367
|
# @param [Object] obj
|
334
368
|
# @return [self]
|
335
369
|
def from_json(obj)
|
336
370
|
@fields = obj.map {|f|
|
337
|
-
Field.new(f
|
371
|
+
Field.new(*f)
|
338
372
|
}
|
339
373
|
self
|
340
374
|
end
|
@@ -416,7 +450,7 @@ class Job < Model
|
|
416
450
|
d = t - t1
|
417
451
|
t1 = t
|
418
452
|
timeout -= d if d > 0
|
419
|
-
raise ::
|
453
|
+
raise Timeout::Error, "timeout=#{orig_timeout} wait_interval=#{wait_interval}" if timeout <= 0
|
420
454
|
end
|
421
455
|
sleep wait_interval
|
422
456
|
yield self if block_given?
|
data/lib/td/client/version.rb
CHANGED
data/spec/td/client/api_spec.rb
CHANGED
@@ -126,7 +126,7 @@ describe API do
|
|
126
126
|
|
127
127
|
describe "'validate_column_name'" do
|
128
128
|
it 'should raise a ParameterValidationError exception' do
|
129
|
-
['
|
129
|
+
['', 'a'*129].each { |ng|
|
130
130
|
expect {
|
131
131
|
API.validate_column_name(ng)
|
132
132
|
}.to raise_error(ParameterValidationError)
|
@@ -137,14 +137,40 @@ describe API do
|
|
137
137
|
VALID_NAMES.each {|ok|
|
138
138
|
expect(API.validate_column_name(ok)).to eq(ok)
|
139
139
|
}
|
140
|
-
|
141
|
-
|
140
|
+
['a', 'a'*128].each {|ok|
|
141
|
+
expect(API.validate_column_name(ok)).to eq(ok)
|
142
|
+
}
|
142
143
|
end
|
143
144
|
end
|
144
145
|
|
146
|
+
describe "'validate_sql_alias_name'" do
|
147
|
+
it 'should raise a ParameterValidationError exception' do
|
148
|
+
['', 'a'*129].each { |ng|
|
149
|
+
expect{API.validate_sql_alias_name(ng)}.to raise_error(ParameterValidationError)
|
150
|
+
}
|
151
|
+
valid = ("a".."z").to_a.join<<("0".."9").to_a.join<<"_"
|
152
|
+
("\x00".."\x7F").each { |ng|
|
153
|
+
next if valid.include?(ng)
|
154
|
+
expect{API.validate_sql_alias_name(ng)}.to raise_error(ParameterValidationError)
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'should return valid data' do
|
159
|
+
VALID_NAMES.each {|ok|
|
160
|
+
expect(API.validate_sql_alias_name(ok)).to eq(ok)
|
161
|
+
}
|
162
|
+
['a', '_a', 'a_', 'a'*128].each {|ok|
|
163
|
+
expect(API.validate_sql_alias_name(ok)).to eq(ok)
|
164
|
+
}
|
165
|
+
end
|
166
|
+
end
|
145
167
|
|
146
168
|
describe "'generic validate_name'" do
|
147
169
|
it 'should raise a ParameterValidationError exception' do
|
170
|
+
# wrong target
|
171
|
+
expect {
|
172
|
+
API.validate_name("", 3, 256, '')
|
173
|
+
}.to raise_error(ParameterValidationError)
|
148
174
|
INVALID_NAMES.each_pair {|ng,ok|
|
149
175
|
expect {
|
150
176
|
API.validate_name("generic", 3, 256, ng)
|
@@ -336,6 +336,31 @@ describe 'BulkImport API' do
|
|
336
336
|
bulk_load_session
|
337
337
|
)).to eq(bulk_load_session)
|
338
338
|
end
|
339
|
+
|
340
|
+
it 'returns updated bulk_load_session' do
|
341
|
+
updated_bulk_load_session = bulk_load_session.merge({'timezone' => 'America/Los Angeles'})
|
342
|
+
stub_api_request(:put, '/v3/bulk_loads/nahi_test_1').
|
343
|
+
with(:body => updated_bulk_load_session.to_json).
|
344
|
+
to_return(:body => updated_bulk_load_session.to_json)
|
345
|
+
expect(api.bulk_load_update(
|
346
|
+
'nahi_test_1',
|
347
|
+
updated_bulk_load_session
|
348
|
+
)).to eq updated_bulk_load_session
|
349
|
+
end
|
350
|
+
|
351
|
+
it 'can remove the cron schedule ' do
|
352
|
+
updated_bulk_load_session = bulk_load_session.merge({'cron' => ''})
|
353
|
+
# NOTE: currently the API just ignores an empty 'cron' specification update
|
354
|
+
# I am assuming that once fixed, the API will return a nil for cron if unscheduled.
|
355
|
+
expected_bulk_load_session = (bulk_load_session['cron'] = nil)
|
356
|
+
stub_api_request(:put, '/v3/bulk_loads/nahi_test_1').
|
357
|
+
with(:body => updated_bulk_load_session.to_json).
|
358
|
+
to_return(:body => expected_bulk_load_session.to_json)
|
359
|
+
expect(api.bulk_load_update(
|
360
|
+
'nahi_test_1',
|
361
|
+
updated_bulk_load_session
|
362
|
+
)).to eq expected_bulk_load_session
|
363
|
+
end
|
339
364
|
end
|
340
365
|
|
341
366
|
describe 'delete' do
|
@@ -97,7 +97,7 @@ describe 'Job Model' do
|
|
97
97
|
}
|
98
98
|
sleep 0.3
|
99
99
|
change_job_status(Job::STATUS_SUCCESS)
|
100
|
-
thread.join(
|
100
|
+
thread.join(1)
|
101
101
|
expect(thread).to be_stop
|
102
102
|
ensure
|
103
103
|
thread.kill # just in case
|
@@ -108,10 +108,10 @@ describe 'Job Model' do
|
|
108
108
|
|
109
109
|
context 'with timeout' do
|
110
110
|
context 'the job running time is too long' do
|
111
|
-
it 'raise ::
|
111
|
+
it 'raise Timeout::Error' do
|
112
112
|
expect {
|
113
113
|
job.wait(0.1)
|
114
|
-
}.to raise_error(::
|
114
|
+
}.to raise_error(Timeout::Error)
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
@@ -121,7 +121,7 @@ describe 'Job Model' do
|
|
121
121
|
thread = Thread.start {
|
122
122
|
job.wait(0.3, 0.1, &b)
|
123
123
|
}
|
124
|
-
expect{ thread.value }.to raise_error(::
|
124
|
+
expect{ thread.value }.to raise_error(Timeout::Error)
|
125
125
|
expect(thread).to be_stop
|
126
126
|
ensure
|
127
127
|
thread.kill # just in case
|
@@ -82,10 +82,10 @@ describe 'Table API' do
|
|
82
82
|
describe "'tables' Client API" do
|
83
83
|
it 'should return an array of Table objects' do
|
84
84
|
tables = [
|
85
|
-
["table_1", "item", "[[\"
|
86
|
-
["table_2", "log", "[[\"
|
87
|
-
["table_3", "item", "[[\"
|
88
|
-
["table_4", "log", "[[\"
|
85
|
+
["table_1", "item", "[[\"value\",\"string\"]]", 111, "2013-01-21 01:51:41 UTC", "2014-01-21 01:51:41 UTC"],
|
86
|
+
["table_2", "log", "[[\"value\",\"long\"]]", 222, "2013-02-22 02:52:42 UTC", "2014-02-22 02:52:42 UTC"],
|
87
|
+
["table_3", "item", "[[\"value\",\"string\"]]", 333, "2013-03-23 03:53:43 UTC", "2014-03-23 03:53:43 UTC"],
|
88
|
+
["table_4", "log", "[[\"value\",\"long\"]]", 444, "2013-04-24 04:54:44 UTC", "2014-04-24 04:54:44 UTC"]
|
89
89
|
]
|
90
90
|
stub_api_request(:get, "/v3/table/list/#{e db_name}").
|
91
91
|
to_return(:body => {'tables' => [
|
@@ -138,10 +138,10 @@ describe 'Table API' do
|
|
138
138
|
describe "'table' Client API" do
|
139
139
|
it 'should return the Table object corresponding to the name' do
|
140
140
|
tables = [
|
141
|
-
["table_1", "item", "[[\"
|
142
|
-
["table_2", "log", "[[\"
|
143
|
-
["table_3", "item", "[[\"
|
144
|
-
["table_4", "log", "[[\"
|
141
|
+
["table_1", "item", "[[\"value\",\"string\"]]", 111, "2013-01-21 01:51:41 UTC", "2014-01-21 01:51:41 UTC"],
|
142
|
+
["table_2", "log", "[[\"value\",\"long\"]]", 222, "2013-02-22 02:52:42 UTC", "2014-02-22 02:52:42 UTC"],
|
143
|
+
["table_3", "item", "[[\"value\",\"string\"]]", 333, "2013-03-23 03:53:43 UTC", "2014-03-23 03:53:43 UTC"],
|
144
|
+
["table_4", "log", "[[\"value\",\"long\"]]", 444, "2013-04-24 04:54:44 UTC", "2014-04-24 04:54:44 UTC"]
|
145
145
|
]
|
146
146
|
stub_api_request(:get, "/v3/table/list/#{e db_name}").
|
147
147
|
to_return(:body => {'tables' => [
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: td-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.83
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Treasure Data, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -257,7 +257,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
257
257
|
requirements:
|
258
258
|
- - ">="
|
259
259
|
- !ruby/object:Gem::Version
|
260
|
-
version:
|
260
|
+
version: 2.0.0
|
261
261
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
262
262
|
requirements:
|
263
263
|
- - ">="
|
@@ -291,4 +291,3 @@ test_files:
|
|
291
291
|
- spec/td/client/user_api_spec.rb
|
292
292
|
- spec/td/client_sched_spec.rb
|
293
293
|
- spec/td/client_spec.rb
|
294
|
-
has_rdoc: false
|