fluent-plugin-bigquery 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/fluent/plugin/bigquery/version.rb +1 -1
- data/lib/fluent/plugin/out_bigquery.rb +42 -14
- data/test/plugin/test_out_bigquery.rb +78 -0
- data/test/plugin/testdata/sudo.schema +27 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 08a2d8a17b61a68a7b759d42d2c35c655660940e
|
4
|
+
data.tar.gz: 2e456086c9216056e9e4e7fe749521fa713ca2f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fac063ee55019a58253ec7a4a0de9ae973c3e9d6814ee74455a010d0ed84382200aa0e9f1bfe9cbc45856c6756cd34ea85d85071156020acf27441a4ee61a2a4
|
7
|
+
data.tar.gz: d2a3365ca5515b02a35f3cfaae8f7a867b4e71f241441c08d4b71f6b0c41954943aa4161be0f67b2aa77ac41c9ab785f329d6eef1c37b6ddbc26a1e7352945d2
|
data/README.md
CHANGED
@@ -236,7 +236,7 @@ With this configuration, flushing will be done in 0.25 seconds after record inpu
|
|
236
236
|
* support Load API
|
237
237
|
* with automatically configured flush/buffer options
|
238
238
|
* support optional data fields
|
239
|
-
* support NULLABLE/REQUIRED/REPEATED field options
|
239
|
+
* support NULLABLE/REQUIRED/REPEATED field options in field list style of configuration
|
240
240
|
* OAuth installed application credentials support
|
241
241
|
* Google API discovery expiration
|
242
242
|
* Error classes
|
@@ -145,7 +145,7 @@ module Fluent
|
|
145
145
|
|
146
146
|
@tablelist = @tables ? @tables.split(',') : [@table]
|
147
147
|
|
148
|
-
@fields = RecordSchema.new
|
148
|
+
@fields = RecordSchema.new('record')
|
149
149
|
if @schema_path
|
150
150
|
@fields.load_schema(JSON.parse(File.read(@schema_path)))
|
151
151
|
end
|
@@ -317,7 +317,30 @@ module Fluent
|
|
317
317
|
# end
|
318
318
|
|
319
319
|
class FieldSchema
|
320
|
+
def initialize(name, mode = :nullable)
|
321
|
+
unless [:nullable, :required, :repeated].include?(mode)
|
322
|
+
raise ConfigError, "Unrecognized mode for #{name}: #{mode}"
|
323
|
+
end
|
324
|
+
|
325
|
+
@name = name
|
326
|
+
@mode = mode
|
327
|
+
end
|
328
|
+
|
329
|
+
attr_reader :name, :mode
|
330
|
+
|
320
331
|
def format(value)
|
332
|
+
case @mode
|
333
|
+
when :nullable
|
334
|
+
format_one(value) unless value.nil?
|
335
|
+
when :required
|
336
|
+
raise "Required field #{name} cannot be null" if value.nil?
|
337
|
+
format_one(value)
|
338
|
+
when :repeated
|
339
|
+
value.nil? ? [] : value.map {|v| format_one(v) }
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def format_one(value)
|
321
344
|
raise NotImplementedError, "Must implement in a subclass"
|
322
345
|
end
|
323
346
|
end
|
@@ -327,7 +350,7 @@ module Fluent
|
|
327
350
|
:string
|
328
351
|
end
|
329
352
|
|
330
|
-
def
|
353
|
+
def format_one(value)
|
331
354
|
value.to_s
|
332
355
|
end
|
333
356
|
end
|
@@ -337,7 +360,7 @@ module Fluent
|
|
337
360
|
:integer
|
338
361
|
end
|
339
362
|
|
340
|
-
def
|
363
|
+
def format_one(value)
|
341
364
|
value.to_i
|
342
365
|
end
|
343
366
|
end
|
@@ -347,7 +370,7 @@ module Fluent
|
|
347
370
|
:float
|
348
371
|
end
|
349
372
|
|
350
|
-
def
|
373
|
+
def format_one(value)
|
351
374
|
value.to_f
|
352
375
|
end
|
353
376
|
end
|
@@ -357,7 +380,7 @@ module Fluent
|
|
357
380
|
:boolean
|
358
381
|
end
|
359
382
|
|
360
|
-
def
|
383
|
+
def format_one(value)
|
361
384
|
!!value
|
362
385
|
end
|
363
386
|
end
|
@@ -367,7 +390,7 @@ module Fluent
|
|
367
390
|
:timestamp
|
368
391
|
end
|
369
392
|
|
370
|
-
def
|
393
|
+
def format_one(value)
|
371
394
|
value
|
372
395
|
end
|
373
396
|
end
|
@@ -382,7 +405,8 @@ module Fluent
|
|
382
405
|
:record => RecordSchema
|
383
406
|
}.freeze
|
384
407
|
|
385
|
-
def initialize
|
408
|
+
def initialize(name, mode = :nullable)
|
409
|
+
super(name, mode)
|
386
410
|
@fields = {}
|
387
411
|
end
|
388
412
|
|
@@ -398,12 +422,15 @@ module Fluent
|
|
398
422
|
schema.each do |field|
|
399
423
|
raise ConfigError, 'field must have type' unless field.key?('type')
|
400
424
|
|
425
|
+
name = field['name']
|
426
|
+
mode = (field['mode'] || 'nullable').downcase.to_sym
|
427
|
+
|
401
428
|
type = field['type'].downcase.to_sym
|
402
429
|
field_schema_class = FIELD_TYPES[type]
|
403
430
|
raise ConfigError, "Invalid field type: #{field['type']}" unless field_schema_class
|
404
431
|
|
405
|
-
field_schema = field_schema_class.new
|
406
|
-
@fields[
|
432
|
+
field_schema = field_schema_class.new(name, mode)
|
433
|
+
@fields[name] = field_schema
|
407
434
|
if type == :record
|
408
435
|
raise ConfigError, "record field must have fields" unless field.key?('fields')
|
409
436
|
field_schema.load_schema(field['fields'])
|
@@ -423,16 +450,17 @@ module Fluent
|
|
423
450
|
else
|
424
451
|
schema = FIELD_TYPES[type]
|
425
452
|
raise ConfigError, "[Bug] Invalid field type #{type}" unless schema
|
426
|
-
@fields[name] = schema.new
|
453
|
+
@fields[name] = schema.new(name)
|
427
454
|
end
|
428
455
|
end
|
429
456
|
|
430
|
-
def
|
457
|
+
def format_one(record)
|
431
458
|
out = {}
|
432
459
|
@fields.each do |key, schema|
|
433
460
|
value = record[key]
|
434
|
-
|
435
|
-
|
461
|
+
formatted = schema.format(value)
|
462
|
+
next if formatted.nil? # field does not exists, or null value
|
463
|
+
out[key] = formatted
|
436
464
|
end
|
437
465
|
out
|
438
466
|
end
|
@@ -440,7 +468,7 @@ module Fluent
|
|
440
468
|
private
|
441
469
|
def register_record_field(name)
|
442
470
|
if !@fields.key?(name)
|
443
|
-
@fields[name] = RecordSchema.new
|
471
|
+
@fields[name] = RecordSchema.new(name)
|
444
472
|
else
|
445
473
|
unless @fields[name].kind_of?(RecordSchema)
|
446
474
|
raise ConfigError, "field #{name} is required to be a record but already registered as #{@field[name]}"
|
@@ -307,6 +307,84 @@ class BigQueryOutputTest < Test::Unit::TestCase
|
|
307
307
|
assert_equal expected, MessagePack.unpack(buf)
|
308
308
|
end
|
309
309
|
|
310
|
+
def test_format_repeated_field_with_schema
|
311
|
+
now = Time.now
|
312
|
+
input = [
|
313
|
+
now,
|
314
|
+
{
|
315
|
+
"tty" => nil,
|
316
|
+
"pwd" => "/home/yugui",
|
317
|
+
"user" => "fluentd",
|
318
|
+
"argv" => %w[ tail -f /var/log/fluentd/fluentd.log ]
|
319
|
+
}
|
320
|
+
]
|
321
|
+
expected = {
|
322
|
+
"json" => {
|
323
|
+
"time" => now.to_i,
|
324
|
+
"pwd" => "/home/yugui",
|
325
|
+
"user" => "fluentd",
|
326
|
+
"argv" => %w[ tail -f /var/log/fluentd/fluentd.log ]
|
327
|
+
}
|
328
|
+
}
|
329
|
+
|
330
|
+
driver = create_driver(<<-CONFIG)
|
331
|
+
table foo
|
332
|
+
email foo@bar.example
|
333
|
+
private_key_path /path/to/key
|
334
|
+
project yourproject_id
|
335
|
+
dataset yourdataset_id
|
336
|
+
|
337
|
+
time_format %s
|
338
|
+
time_field time
|
339
|
+
|
340
|
+
schema_path #{File.join(File.dirname(__FILE__), "testdata", "sudo.schema")}
|
341
|
+
field_integer time
|
342
|
+
CONFIG
|
343
|
+
mock_client(driver) do |expect|
|
344
|
+
expect.discovered_api("bigquery", "v2") { stub! }
|
345
|
+
end
|
346
|
+
driver.instance.start
|
347
|
+
buf = driver.instance.format_stream("my.tag", [input])
|
348
|
+
driver.instance.shutdown
|
349
|
+
|
350
|
+
assert_equal expected, MessagePack.unpack(buf)
|
351
|
+
end
|
352
|
+
|
353
|
+
def test_empty_value_in_required
|
354
|
+
now = Time.now
|
355
|
+
input = [
|
356
|
+
now,
|
357
|
+
{
|
358
|
+
"tty" => "pts/1",
|
359
|
+
"pwd" => "/home/yugui",
|
360
|
+
"user" => nil,
|
361
|
+
"argv" => %w[ tail -f /var/log/fluentd/fluentd.log ]
|
362
|
+
}
|
363
|
+
]
|
364
|
+
|
365
|
+
driver = create_driver(<<-CONFIG)
|
366
|
+
table foo
|
367
|
+
email foo@bar.example
|
368
|
+
private_key_path /path/to/key
|
369
|
+
project yourproject_id
|
370
|
+
dataset yourdataset_id
|
371
|
+
|
372
|
+
time_format %s
|
373
|
+
time_field time
|
374
|
+
|
375
|
+
schema_path #{File.join(File.dirname(__FILE__), "testdata", "sudo.schema")}
|
376
|
+
field_integer time
|
377
|
+
CONFIG
|
378
|
+
mock_client(driver) do |expect|
|
379
|
+
expect.discovered_api("bigquery", "v2") { stub! }
|
380
|
+
end
|
381
|
+
driver.instance.start
|
382
|
+
assert_raises(RuntimeError, /cannot be null/) do
|
383
|
+
driver.instance.format_stream("my.tag", [input])
|
384
|
+
end
|
385
|
+
driver.instance.shutdown
|
386
|
+
end
|
387
|
+
|
310
388
|
def test_write
|
311
389
|
entry = {"json" => {"a" => "b"}}, {"json" => {"b" => "c"}}
|
312
390
|
driver = create_driver(CONFIG)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"name": "time",
|
4
|
+
"type": "TIMESTAMP",
|
5
|
+
"mode": "REQUIRED"
|
6
|
+
},
|
7
|
+
{
|
8
|
+
"name": "tty",
|
9
|
+
"type": "STRING",
|
10
|
+
"mode": "NULLABLE"
|
11
|
+
},
|
12
|
+
{
|
13
|
+
"name": "pwd",
|
14
|
+
"type": "STRING",
|
15
|
+
"mode": "REQUIRED"
|
16
|
+
},
|
17
|
+
{
|
18
|
+
"name": "user",
|
19
|
+
"type": "STRING",
|
20
|
+
"mode": "REQUIRED"
|
21
|
+
},
|
22
|
+
{
|
23
|
+
"name": "argv",
|
24
|
+
"type": "STRING",
|
25
|
+
"mode": "REPEATED"
|
26
|
+
}
|
27
|
+
]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-bigquery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TAGOMORI Satoshi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -143,6 +143,7 @@ files:
|
|
143
143
|
- test/helper.rb
|
144
144
|
- test/plugin/test_out_bigquery.rb
|
145
145
|
- test/plugin/testdata/apache.schema
|
146
|
+
- test/plugin/testdata/sudo.schema
|
146
147
|
- test/test_load_request_body_wrapper.rb
|
147
148
|
homepage: https://github.com/tagomoris/fluent-plugin-bigquery
|
148
149
|
licenses:
|
@@ -172,5 +173,6 @@ test_files:
|
|
172
173
|
- test/helper.rb
|
173
174
|
- test/plugin/test_out_bigquery.rb
|
174
175
|
- test/plugin/testdata/apache.schema
|
176
|
+
- test/plugin/testdata/sudo.schema
|
175
177
|
- test/test_load_request_body_wrapper.rb
|
176
178
|
has_rdoc:
|