fluent-plugin-bigquery 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0802b40a0e2792d20ca2e92f20cfe398d92aa5db
4
- data.tar.gz: a210aa8139c2d82467979adac99f6396096ef6d7
3
+ metadata.gz: 91a4bfe02680f2079998d36ab098c5498bdaea04
4
+ data.tar.gz: 234135f11479a6d364d1c5d0f58d90cfe7d72717
5
5
  SHA512:
6
- metadata.gz: 865a4f9cd3de0d912678768a5a29d3795b62382de2949ac7fc220446d2aaedb2c62616056ad2d44901c64c2782d45c3e2f2f1bb78ca73174394d1b2954291e5f
7
- data.tar.gz: 1bde3427c107b3ac396e808f4856daff92f9a50666d07567dfe1b3cdd0fd50a0ca550b1573368b1913cde125dfa4d874ed6aa96862014fdb2bd22e950ce78bbf
6
+ metadata.gz: a1e3df653dca491e163824f57e6d3d0932314ef59b2cc0cf36caa77f9007df624b183cd2b700caf5c02bd6a477322d136e44c14bed15f44c8c201eb9fb43838e
7
+ data.tar.gz: d20de3a18a7507fb5532480cd51704e23e79740d12a58bde482824095740681b10ede39a9e0611e878bba6e609078dc406e5e983b5ea5160e83b0632a4c83542
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.0
7
+
8
+ script: bundle exec rake test
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # fluent-plugin-bigquery
2
2
 
3
- Fluentd output plugin to load/insert data into Google BigQuery.
3
+ [Fluentd](http://fluentd.org) output plugin to load/insert data into Google BigQuery.
4
4
 
5
5
  * insert data over streaming inserts
6
6
  * for continuous real-time insertions, under many limitations
@@ -104,6 +104,7 @@ Important options for high rate events are:
104
104
  ### Authentication
105
105
 
106
106
  There are two methods supported to fetch access token for the service account.
107
+
107
108
  1. Public-Private key pair
108
109
  2. Predefined access token (Compute Engine only)
109
110
 
@@ -135,6 +136,73 @@ Compute Engine instance, then you can configure fluentd like this.
135
136
  </match>
136
137
  ```
137
138
 
139
+ ### Table schema
140
+
141
+ There are two methods to describe the schema of the target table.
142
+
143
+ 1. List fields in fluent.conf
144
+ 2. Load a schema file in JSON.
145
+
146
+ The examples above use the first method. In this method,
147
+ you can also specify nested fields by prefixing their belonging record fields.
148
+
149
+ ```apache
150
+ <match dummy>
151
+ type bigquery
152
+
153
+ ...
154
+
155
+ time_format %s
156
+ time_field time
157
+
158
+ field_integer time,response.status,response.bytes
159
+ field_string request.vhost,request.path,request.method,request.protocol,request.agent,request.referer,remote.host,remote.ip,remote.user
160
+ field_float request.time
161
+ field_boolean request.bot_access,request.loginsession
162
+ </match>
163
+ ```
164
+
165
+ This schema accepts structured JSON data like:
166
+
167
+ ```json
168
+ {
169
+ "request":{
170
+ "time":1391748126.7000976,
171
+ "vhost":"www.example.com",
172
+ "path":"/",
173
+ "method":"GET",
174
+ "protocol":"HTTP/1.1",
175
+ "agent":"HotJava",
176
+ "bot_access":false
177
+ },
178
+ "remote":{ "ip": "192.0.2.1" },
179
+ "response":{
180
+ "status":200,
181
+ "bytes":1024
182
+ }
183
+ }
184
+ ```
185
+
186
+ The second method is to specify a path to a BigQuery schema file instead of listing fields. In this case, your fluent.conf looks like:
187
+
188
+ ```apache
189
+ <match dummy>
190
+ type bigquery
191
+
192
+ ...
193
+
194
+ time_format %s
195
+ time_field time
196
+
197
+ schema_path /path/to/httpd.schema
198
+ field_integer time
199
+ </match>
200
+ ```
201
+ where /path/to/httpd.schema is a path to the JSON-encoded schema file which you used for creating the table on BigQuery.
202
+
203
+ NOTE: Since JSON does not define how to encode data of TIMESTAMP type,
204
+ you are still recommended to specify JSON types for TIMESTAMP fields as "time" field does in the example.
205
+
138
206
 
139
207
  ### patches
140
208
 
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "rr"
22
23
  spec.add_runtime_dependency "google-api-client", "~> 0.7.1"
23
24
  spec.add_runtime_dependency "fluentd"
24
25
  spec.add_runtime_dependency "fluent-mixin-plaintextformatter", '>= 0.2.1'
@@ -1,6 +1,6 @@
1
1
  module Fluent
2
2
  module BigQueryPlugin
3
- VERSION = "0.0.3"
3
+ VERSION = "0.0.4"
4
4
  end
5
5
  end
6
6
 
@@ -63,6 +63,7 @@ module Fluent
63
63
  config_param :table, :string, :default => nil
64
64
  config_param :tables, :string, :default => nil
65
65
 
66
+ config_param :schema_path, :string, :default => nil
66
67
  config_param :field_string, :string, :default => nil
67
68
  config_param :field_integer, :string, :default => nil
68
69
  config_param :field_float, :string, :default => nil
@@ -117,6 +118,7 @@ module Fluent
117
118
 
118
119
  def initialize
119
120
  super
121
+ require 'json'
120
122
  require 'google/api_client'
121
123
  require 'google/api_client/client_secrets'
122
124
  require 'google/api_client/auth/installed_app'
@@ -137,13 +139,16 @@ module Fluent
137
139
  raise Fluent::ConfigError, "unrecognized 'auth_method': #{@auth_method}"
138
140
  end
139
141
 
140
- if (!@table && !@tables) || (@table && @table)
142
+ if (!@table && !@tables) || (@table && @tables)
141
143
  raise Fluent::ConfigError, "'table' or 'tables' must be specified, and both are invalid"
142
144
  end
143
145
 
144
146
  @tablelist = @tables ? @tables.split(',') : [@table]
145
147
 
146
148
  @fields = RecordSchema.new
149
+ if @schema_path
150
+ @fields.load_schema(JSON.parse(File.read(@schema_path)))
151
+ end
147
152
  if @field_string
148
153
  @field_string.split(',').each do |fieldname|
149
154
  @fields.register_field fieldname, :string
@@ -171,6 +176,17 @@ module Fluent
171
176
  end
172
177
  end
173
178
  @timef = TimeFormatter.new(@time_format, @localtime)
179
+
180
+ if @time_field
181
+ keys = @time_field.split('.')
182
+ last_key = keys.pop
183
+ @add_time_field = lambda {|record, time|
184
+ keys.inject(record) { |h, k| h[k] ||= {} }[last_key] = @timef.format(time)
185
+ record
186
+ }
187
+ else
188
+ @add_time_field = lambda {|record, time| record }
189
+ end
174
190
  end
175
191
 
176
192
  def start
@@ -260,11 +276,7 @@ module Fluent
260
276
  super
261
277
  buf = ''
262
278
  es.each do |time, record|
263
- row = if @time_field
264
- @fields.format(record.merge({@time_field => @timef.format(time)}))
265
- else
266
- @fields.format(record)
267
- end
279
+ row = @fields.format(@add_time_field.call(record, time))
268
280
  buf << {"json" => row}.to_msgpack unless row.empty?
269
281
  end
270
282
  buf
@@ -350,12 +362,24 @@ module Fluent
350
362
  end
351
363
  end
352
364
 
365
+ class TimestampFieldSchema < FieldSchema
366
+ def type
367
+ :timestamp
368
+ end
369
+
370
+ def format(value)
371
+ value
372
+ end
373
+ end
374
+
353
375
  class RecordSchema < FieldSchema
354
376
  FIELD_TYPES = {
355
377
  :string => StringFieldSchema,
356
378
  :integer => IntegerFieldSchema,
357
379
  :float => FloatFieldSchema,
358
- :boolean => BooleanFieldSchema
380
+ :boolean => BooleanFieldSchema,
381
+ :timestamp => TimestampFieldSchema,
382
+ :record => RecordSchema
359
383
  }.freeze
360
384
 
361
385
  def initialize
@@ -370,8 +394,27 @@ module Fluent
370
394
  @fields[name]
371
395
  end
372
396
 
397
+ def load_schema(schema)
398
+ schema.each do |field|
399
+ raise ConfigError, 'field must have type' unless field.key?('type')
400
+
401
+ type = field['type'].downcase.to_sym
402
+ field_schema_class = FIELD_TYPES[type]
403
+ raise ConfigError, "Invalid field type: #{field['type']}" unless field_schema_class
404
+
405
+ field_schema = field_schema_class.new
406
+ @fields[field['name']] = field_schema
407
+ if type == :record
408
+ raise ConfigError, "record field must have fields" unless field.key?('fields')
409
+ field_schema.load_schema(field['fields'])
410
+ end
411
+ end
412
+ end
413
+
373
414
  def register_field(name, type)
374
- raise ConfigError, "field #{name} is registered twice" if @fields.key?(name)
415
+ if @fields.key?(name) and @fields[name].type != :timestamp
416
+ raise ConfigError, "field #{name} is registered twice"
417
+ end
375
418
  if name[/\./]
376
419
  recordname = $`
377
420
  fieldname = $'
data/test/helper.rb CHANGED
@@ -29,5 +29,7 @@ require 'fluent/plugin/buf_file'
29
29
  require 'fluent/plugin/out_bigquery'
30
30
  require 'fluent/plugin/bigquery/load_request_body_wrapper'
31
31
 
32
+ require 'rr'
33
+
32
34
  class Test::Unit::TestCase
33
35
  end
@@ -0,0 +1,335 @@
1
+ require 'helper'
2
+ require 'google/api_client'
3
+ require 'fluent/plugin/buf_memory'
4
+
5
+ class BigQueryOutputTest < Test::Unit::TestCase
6
+ def setup
7
+ Fluent::Test.setup
8
+ end
9
+
10
+ CONFIG = %[
11
+ table foo
12
+ email foo@bar.example
13
+ private_key_path /path/to/key
14
+ project yourproject_id
15
+ dataset yourdataset_id
16
+
17
+ time_format %s
18
+ time_field time
19
+
20
+ field_integer time,status,bytes
21
+ field_string vhost,path,method,protocol,agent,referer,remote.host,remote.ip,remote.user
22
+ field_float requesttime
23
+ field_boolean bot_access,loginsession
24
+ ]
25
+
26
+ API_SCOPE = "https://www.googleapis.com/auth/bigquery"
27
+
28
+ def create_driver(conf = CONFIG)
29
+ Fluent::Test::OutputTestDriver.new(Fluent::BigQueryOutput).configure(conf)
30
+ end
31
+
32
+ def stub_client(driver)
33
+ stub(client = Object.new) do |expect|
34
+ expect.discovered_api("bigquery", "v2") { stub! }
35
+ yield expect if defined?(yield)
36
+ end
37
+ stub(driver.instance).client { client }
38
+ client
39
+ end
40
+
41
+ def mock_client(driver)
42
+ mock(client = Object.new) do |expect|
43
+ yield expect
44
+ end
45
+ stub(driver.instance).client { client }
46
+ client
47
+ end
48
+
49
+ def test_configure_table
50
+ driver = create_driver
51
+ assert_equal driver.instance.table, 'foo'
52
+ assert_nil driver.instance.tables
53
+
54
+ driver = create_driver(CONFIG.sub(/\btable\s+.*$/, 'tables foo,bar'))
55
+ assert_nil driver.instance.table
56
+ assert_equal driver.instance.tables, 'foo,bar'
57
+
58
+ assert_raise(Fluent::ConfigError, "'table' or 'tables' must be specified, and both are invalid") {
59
+ create_driver(CONFIG + "tables foo,bar")
60
+ }
61
+ end
62
+
63
+ def test_configure_auth
64
+ key = stub!
65
+ mock(Google::APIClient::PKCS12).load_key('/path/to/key', 'notasecret') { key }
66
+ authorization = Object.new
67
+ asserter = mock!.authorize { authorization }
68
+ mock(Google::APIClient::JWTAsserter).new('foo@bar.example', API_SCOPE, key) { asserter }
69
+
70
+ mock.proxy(Google::APIClient).new.with_any_args {
71
+ mock!.__send__(:authorization=, authorization) {}
72
+ }
73
+
74
+ driver = create_driver(CONFIG)
75
+ driver.instance.client()
76
+ end
77
+
78
+ def test_format_stream
79
+ now = Time.now
80
+ input = [
81
+ now,
82
+ {
83
+ "status" => "1",
84
+ "bytes" => 3.0,
85
+ "vhost" => :bar,
86
+ "path" => "/path/to/baz",
87
+ "method" => "GET",
88
+ "protocol" => "HTTP/0.9",
89
+ "agent" => "libwww",
90
+ "referer" => "http://referer.example",
91
+ "requesttime" => (now - 1).to_f.to_s,
92
+ "bot_access" => true,
93
+ "loginsession" => false,
94
+ "something-else" => "would be ignored",
95
+ "yet-another" => {
96
+ "foo" => "bar",
97
+ "baz" => 1,
98
+ },
99
+ "remote" => {
100
+ "host" => "remote.example",
101
+ "ip" => "192.0.2.1",
102
+ "port" => 12345,
103
+ "user" => "tagomoris",
104
+ }
105
+ }
106
+ ]
107
+ expected = {
108
+ "json" => {
109
+ "time" => now.to_i,
110
+ "status" => 1,
111
+ "bytes" => 3,
112
+ "vhost" => "bar",
113
+ "path" => "/path/to/baz",
114
+ "method" => "GET",
115
+ "protocol" => "HTTP/0.9",
116
+ "agent" => "libwww",
117
+ "referer" => "http://referer.example",
118
+ "requesttime" => (now - 1).to_f.to_s.to_f,
119
+ "bot_access" => true,
120
+ "loginsession" => false,
121
+ "remote" => {
122
+ "host" => "remote.example",
123
+ "ip" => "192.0.2.1",
124
+ "user" => "tagomoris",
125
+ }
126
+ }
127
+ }
128
+
129
+ driver = create_driver(CONFIG)
130
+ mock_client(driver) do |expect|
131
+ expect.discovered_api("bigquery", "v2") { stub! }
132
+ end
133
+ driver.instance.start
134
+ buf = driver.instance.format_stream("my.tag", [input])
135
+ driver.instance.shutdown
136
+
137
+ assert_equal expected, MessagePack.unpack(buf)
138
+ end
139
+
140
+ [
141
+ # <time_format>, <time field type>, <time expectation generator>, <assertion>
142
+ [
143
+ "%s.%6N", "field_float",
144
+ lambda{|t| t.strftime("%s.%6N").to_f },
145
+ lambda{|recv, expected, actual|
146
+ recv.assert_in_delta(expected, actual, Float::EPSILON / 10**3)
147
+ }
148
+ ],
149
+ [
150
+ "%Y-%m-%dT%H:%M:%SZ", "field_string",
151
+ lambda{|t| t.iso8601 },
152
+ :assert_equal.to_proc
153
+ ],
154
+ [
155
+ "%a, %d %b %Y %H:%M:%S GMT", "field_string",
156
+ lambda{|t| t.httpdate },
157
+ :assert_equal.to_proc
158
+ ],
159
+ ].each do |format, type, expect_time, assert|
160
+ define_method("test_time_formats_#{format}") do
161
+ now = Time.now.utc
162
+ input = [ now, {} ]
163
+ expected = { "json" => { "time" => expect_time[now], } }
164
+
165
+ driver = create_driver(<<-CONFIG)
166
+ table foo
167
+ email foo@bar.example
168
+ private_key_path /path/to/key
169
+ project yourproject_id
170
+ dataset yourdataset_id
171
+
172
+ time_format #{format}
173
+ time_field time
174
+ #{type} time
175
+ CONFIG
176
+ stub_client(driver)
177
+
178
+ driver.instance.start
179
+ buf = driver.instance.format_stream("my.tag", [input])
180
+ driver.instance.shutdown
181
+
182
+ assert[self, expected["json"]["time"], MessagePack.unpack(buf)["json"]["time"]]
183
+ end
184
+ end
185
+
186
+ def test_format_nested_time
187
+ now = Time.now
188
+ input = [
189
+ now,
190
+ {
191
+ "metadata" => {
192
+ "node" => "mynode.example",
193
+ },
194
+ "log" => "something",
195
+ }
196
+ ]
197
+ expected = {
198
+ "json" => {
199
+ "metadata" => {
200
+ "time" => now.strftime("%s").to_i,
201
+ "node" => "mynode.example",
202
+ },
203
+ "log" => "something",
204
+ }
205
+ }
206
+
207
+ driver = create_driver(<<-CONFIG)
208
+ table foo
209
+ email foo@bar.example
210
+ private_key_path /path/to/key
211
+ project yourproject_id
212
+ dataset yourdataset_id
213
+
214
+ time_format %s
215
+ time_field metadata.time
216
+
217
+ field_integer metadata.time
218
+ field_string metadata.node,log
219
+ CONFIG
220
+ stub_client(driver)
221
+ driver.instance.start
222
+ buf = driver.instance.format_stream("my.tag", [input])
223
+ driver.instance.shutdown
224
+
225
+ assert_equal expected, MessagePack.unpack(buf)
226
+ end
227
+
228
+ def test_format_with_schema
229
+ now = Time.now
230
+ input = [
231
+ now,
232
+ {
233
+ "request" => {
234
+ "vhost" => :bar,
235
+ "path" => "/path/to/baz",
236
+ "method" => "GET",
237
+ "protocol" => "HTTP/0.9",
238
+ "agent" => "libwww",
239
+ "referer" => "http://referer.example",
240
+ "time" => (now - 1).to_f,
241
+ "bot_access" => true,
242
+ "loginsession" => false,
243
+ },
244
+ "response" => {
245
+ "status" => "1",
246
+ "bytes" => 3.0,
247
+ },
248
+ "remote" => {
249
+ "host" => "remote.example",
250
+ "ip" => "192.0.2.1",
251
+ "port" => 12345,
252
+ "user" => "tagomoris",
253
+ },
254
+ "something-else" => "would be ignored",
255
+ "yet-another" => {
256
+ "foo" => "bar",
257
+ "baz" => 1,
258
+ },
259
+ }
260
+ ]
261
+ expected = {
262
+ "json" => {
263
+ "time" => now.to_i,
264
+ "request" => {
265
+ "vhost" => "bar",
266
+ "path" => "/path/to/baz",
267
+ "method" => "GET",
268
+ "protocol" => "HTTP/0.9",
269
+ "agent" => "libwww",
270
+ "referer" => "http://referer.example",
271
+ "time" => (now - 1).to_f,
272
+ "bot_access" => true,
273
+ "loginsession" => false,
274
+ },
275
+ "remote" => {
276
+ "host" => "remote.example",
277
+ "ip" => "192.0.2.1",
278
+ "user" => "tagomoris",
279
+ },
280
+ "response" => {
281
+ "status" => 1,
282
+ "bytes" => 3,
283
+ },
284
+ }
285
+ }
286
+
287
+ driver = create_driver(<<-CONFIG)
288
+ table foo
289
+ email foo@bar.example
290
+ private_key_path /path/to/key
291
+ project yourproject_id
292
+ dataset yourdataset_id
293
+
294
+ time_format %s
295
+ time_field time
296
+
297
+ schema_path #{File.join(File.dirname(__FILE__), "testdata", "apache.schema")}
298
+ field_integer time
299
+ CONFIG
300
+ mock_client(driver) do |expect|
301
+ expect.discovered_api("bigquery", "v2") { stub! }
302
+ end
303
+ driver.instance.start
304
+ buf = driver.instance.format_stream("my.tag", [input])
305
+ driver.instance.shutdown
306
+
307
+ assert_equal expected, MessagePack.unpack(buf)
308
+ end
309
+
310
+ def test_write
311
+ entry = {"json" => {"a" => "b"}}, {"json" => {"b" => "c"}}
312
+ driver = create_driver(CONFIG)
313
+ mock_client(driver) do |expect|
314
+ expect.discovered_api("bigquery", "v2") { mock!.tabledata.mock!.insert_all { Object.new } }
315
+ expect.execute(
316
+ :api_method => anything,
317
+ :parameters => {
318
+ 'projectId' => 'yourproject_id',
319
+ 'datasetId' => 'yourdataset_id',
320
+ 'tableId' => 'foo',
321
+ },
322
+ :body_object => {
323
+ 'rows' => [entry]
324
+ }
325
+ ) { stub!.success? { true } }
326
+ end
327
+
328
+ chunk = Fluent::MemoryBufferChunk.new("my.tag")
329
+ chunk << entry.to_msgpack
330
+
331
+ driver.instance.start
332
+ driver.instance.write(chunk)
333
+ driver.instance.shutdown
334
+ end
335
+ end
@@ -0,0 +1,98 @@
1
+ [
2
+ {
3
+ "name": "time",
4
+ "type": "TIMESTAMP",
5
+ "mode": "REQUIRED"
6
+ },
7
+ {
8
+ "name": "request",
9
+ "type": "RECORD",
10
+ "mode": "REQUIRED",
11
+ "fields": [
12
+ {
13
+ "name": "vhost",
14
+ "type": "STRING",
15
+ "mode": "NULLABLE"
16
+ },
17
+ {
18
+ "name": "path",
19
+ "type": "STRING",
20
+ "mode": "REQUIRED"
21
+ },
22
+ {
23
+ "name": "method",
24
+ "type": "STRING",
25
+ "mode": "REQUIRED"
26
+ },
27
+ {
28
+ "name": "protocol",
29
+ "type": "STRING",
30
+ "mode": "REQUIRED"
31
+ },
32
+ {
33
+ "name": "agent",
34
+ "type": "STRING",
35
+ "mode": "REQUIRED"
36
+ },
37
+ {
38
+ "name": "referer",
39
+ "type": "STRING",
40
+ "mode": "NULLABLE"
41
+ },
42
+ {
43
+ "name": "time",
44
+ "type": "TIMESTAMP",
45
+ "mode": "NULLABLE"
46
+ },
47
+ {
48
+ "name": "bot_access",
49
+ "type": "BOOLEAN",
50
+ "mode": "NULLABLE"
51
+ },
52
+ {
53
+ "name": "loginsession",
54
+ "type": "BOOLEAN",
55
+ "mode": "NULLABLE"
56
+ }
57
+ ]
58
+ },
59
+ {
60
+ "name": "remote",
61
+ "type": "RECORD",
62
+ "mode": "NULLABLE",
63
+ "fields": [
64
+ {
65
+ "name": "host",
66
+ "type": "STRING",
67
+ "mode": "NULLABLE"
68
+ },
69
+ {
70
+ "name": "ip",
71
+ "type": "STRING",
72
+ "mode": "REQUIRED"
73
+ },
74
+ {
75
+ "name": "user",
76
+ "type": "STRING",
77
+ "mode": "NULLABLE"
78
+ }
79
+ ]
80
+ },
81
+ {
82
+ "name": "response",
83
+ "type": "RECORD",
84
+ "mode": "REQUIRED",
85
+ "fields": [
86
+ {
87
+ "name": "status",
88
+ "type": "INTEGER",
89
+ "mode": "REQUIRED"
90
+ },
91
+ {
92
+ "name": "bytes",
93
+ "type": "INTEGER",
94
+ "mode": "REQUIRED"
95
+ }
96
+ ]
97
+ }
98
+ ]
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.3
4
+ version: 0.0.4
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-06 00:00:00.000000000 Z
11
+ date: 2014-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rr
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: google-api-client
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -117,6 +131,7 @@ extensions: []
117
131
  extra_rdoc_files: []
118
132
  files:
119
133
  - .gitignore
134
+ - .travis.yml
120
135
  - Gemfile
121
136
  - LICENSE.txt
122
137
  - README.md
@@ -126,6 +141,8 @@ files:
126
141
  - lib/fluent/plugin/bigquery/version.rb
127
142
  - lib/fluent/plugin/out_bigquery.rb
128
143
  - test/helper.rb
144
+ - test/plugin/test_out_bigquery.rb
145
+ - test/plugin/testdata/apache.schema
129
146
  - test/test_load_request_body_wrapper.rb
130
147
  homepage: https://github.com/tagomoris/fluent-plugin-bigquery
131
148
  licenses:
@@ -153,5 +170,7 @@ specification_version: 4
153
170
  summary: Fluentd plugin to store data on Google BigQuery
154
171
  test_files:
155
172
  - test/helper.rb
173
+ - test/plugin/test_out_bigquery.rb
174
+ - test/plugin/testdata/apache.schema
156
175
  - test/test_load_request_body_wrapper.rb
157
176
  has_rdoc: