fluent-plugin-bigquery 1.0.0 → 1.1.0

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: 570345f8dc6c8f21186e633cb5be03d40ed3480d
4
- data.tar.gz: 26e474f9c08a3d8b7df2cd5de3a4121d37847f95
3
+ metadata.gz: 28048ac73908138fd792fce4513a880702cbcba1
4
+ data.tar.gz: fdf18e7653e8da3fe3d9d736ec3ab053fcc18e67
5
5
  SHA512:
6
- metadata.gz: dc887f24c5d975cea0c07f4bc1ede48755b396d1bc174c1ade842c6fe6e5d7cd729b75a9ab4bebaf2fc96aa5895f15aed41851a3eb11256ae681e2e347c073f3
7
- data.tar.gz: b7e6fe7d2eb2562264b195cca7bc9f6f605dfeea8dd46c73cea8261e399e994f6d181cb0ac5a352fcdc92e665be9baca297b65861083dec2743f90118aeb42c9
6
+ metadata.gz: 314b093ee2bd215a297a9e32d20a66f831dab816d24bbc9175c38be63b34f6a2433d66206c3962d354ddf256417c0b33300d6cbb9c4b2fe86e9261245ccbebb9
7
+ data.tar.gz: a671371a498ceefee7d4f4617b95cddf9b377485d2c1dba669c4ed2c6ec5d4fcde83b10d6f2bc17b72b787068582aee7c7715b190a20f677c97a695d63c2105b
@@ -0,0 +1,16 @@
1
+ <!-- Please check your config and docs of fluentd !! -->
2
+
3
+ ## Environments
4
+
5
+ - fluentd version:
6
+ - plugin version:
7
+
8
+ ## Configuration
9
+ <!-- Please write your configuration -->
10
+
11
+ ## Expected Behavior
12
+
13
+ ## Actual Behavior
14
+
15
+ ## Log (if you have)
16
+
data/README.md CHANGED
@@ -31,43 +31,42 @@ Because embbeded gem dependency sometimes restricts ruby environment.
31
31
 
32
32
  ### Options
33
33
 
34
- | name | type | required? | placeholder? | default | description |
35
- | :------------------------------------- | :------------ | :----------- | :---------- | :------------------------- | :----------------------- |
36
- | method | string | no | no | insert | `insert` (Streaming Insert) or `load` (load job) |
37
- | auth_method | enum | yes | no | private_key | `private_key` or `json_key` or `compute_engine` or `application_default` |
38
- | email | string | yes (private_key) | no | nil | GCP Service Account Email |
39
- | private_key_path | string | yes (private_key) | no | nil | GCP Private Key file path |
40
- | private_key_passphrase | string | yes (private_key) | no | nil | GCP Private Key Passphrase |
41
- | json_key | string | yes (json_key) | no | nil | GCP JSON Key file path or JSON Key string |
42
- | project | string | yes | yes | nil | |
43
- | dataset | string | yes | yes | nil | |
44
- | table | string | yes (either `tables`) | yes | nil | |
45
- | tables | array(string) | yes (either `table`) | yes | nil | can set multi table names splitted by `,` |
46
- | template_suffix | string | no | yes | nil | can use `%{time_slice}` placeholder replaced by `time_slice_format` |
47
- | auto_create_table | bool | no | no | false | If true, creates table automatically |
48
- | skip_invalid_rows | bool | no | no | false | Only `insert` method. |
49
- | max_bad_records | integer | no | no | 0 | Only `load` method. If the number of bad records exceeds this value, an invalid error is returned in the job result. |
50
- | ignore_unknown_values | bool | no | no | false | Accept rows that contain values that do not match the schema. The unknown values are ignored. |
51
- | schema | array | yes (either `fetch_schema` or `schema_path`) | no | nil | Schema Definition. It is formatted by JSON. |
52
- | schema_path | string | yes (either `fetch_schema`) | no | nil | Schema Definition file path. It is formatted by JSON. |
53
- | fetch_schema | bool | yes (either `schema_path`) | no | false | If true, fetch table schema definition from Bigquery table automatically. |
54
- | fetch_schema_table | string | no | yes | nil | If set, fetch table schema definition from this table, If fetch_schema is false, this param is ignored |
55
- | schema_cache_expire | integer | no | no | 600 | Value is second. If current time is after expiration interval, re-fetch table schema definition. |
56
- | field_string | string | no | no | nil | see examples. |
57
- | field_integer | string | no | no | nil | see examples. |
58
- | field_float | string | no | no | nil | see examples. |
59
- | field_boolean | string | no | no | nil | see examples. |
60
- | field_timestamp | string | no | no | nil | see examples. |
61
- | replace_record_key | bool | no | no | false | see examples. |
62
- | replace_record_key_regexp{1-10} | string | no | no | nil | see examples. |
63
- | convert_hash_to_json | bool | no | no | false | If true, converts Hash value of record to JSON String. |
64
- | insert_id_field | string | no | no | nil | Use key as `insert_id` of Streaming Insert API parameter. |
65
- | add_insert_timestamp | string | no | no | nil | Adds a timestamp column just before sending the rows to BigQuery, so that buffering time is not taken into account. Gives a field in BigQuery which represents the insert time of the row. |
66
- | allow_retry_insert_errors | bool | no | false | Retry to insert rows when an insertErrors occurs. There is a possibility that rows are inserted in duplicate. |
67
- | request_timeout_sec | integer | no | no | nil | Bigquery API response timeout |
68
- | request_open_timeout_sec | integer | no | no | 60 | Bigquery API connection, and request timeout. If you send big data to Bigquery, set large value. |
69
- | time_partitioning_type | enum | no (either day) | no | nil | Type of bigquery time partitioning feature(experimental feature on BigQuery). |
70
- | time_partitioning_expiration | time | no | no | nil | Expiration milliseconds for bigquery time partitioning. (experimental feature on BigQuery) |
34
+ | name | type | required? | placeholder? | default | description |
35
+ | :------------------------------------- | :------------ | :----------- | :---------- | :------------------------- | :----------------------- |
36
+ | method | string | no | no | insert | `insert` (Streaming Insert) or `load` (load job) |
37
+ | auth_method | enum | yes | no | private_key | `private_key` or `json_key` or `compute_engine` or `application_default` |
38
+ | email | string | yes (private_key) | no | nil | GCP Service Account Email |
39
+ | private_key_path | string | yes (private_key) | no | nil | GCP Private Key file path |
40
+ | private_key_passphrase | string | yes (private_key) | no | nil | GCP Private Key Passphrase |
41
+ | json_key | string | yes (json_key) | no | nil | GCP JSON Key file path or JSON Key string |
42
+ | project | string | yes | yes | nil | |
43
+ | dataset | string | yes | yes | nil | |
44
+ | table | string | yes (either `tables`) | yes | nil | |
45
+ | tables | array(string) | yes (either `table`) | yes | nil | can set multi table names splitted by `,` |
46
+ | template_suffix | string | no | yes | nil | can use `%{time_slice}` placeholder replaced by `time_slice_format` |
47
+ | auto_create_table | bool | no | no | false | If true, creates table automatically |
48
+ | skip_invalid_rows | bool | no | no | false | Only `insert` method. |
49
+ | max_bad_records | integer | no | no | 0 | Only `load` method. If the number of bad records exceeds this value, an invalid error is returned in the job result. |
50
+ | ignore_unknown_values | bool | no | no | false | Accept rows that contain values that do not match the schema. The unknown values are ignored. |
51
+ | schema | array | yes (either `fetch_schema` or `schema_path`) | no | nil | Schema Definition. It is formatted by JSON. |
52
+ | schema_path | string | yes (either `fetch_schema`) | no | nil | Schema Definition file path. It is formatted by JSON. |
53
+ | fetch_schema | bool | yes (either `schema_path`) | no | false | If true, fetch table schema definition from Bigquery table automatically. |
54
+ | fetch_schema_table | string | no | yes | nil | If set, fetch table schema definition from this table, If fetch_schema is false, this param is ignored |
55
+ | schema_cache_expire | integer | no | no | 600 | Value is second. If current time is after expiration interval, re-fetch table schema definition. |
56
+ | insert_id_field | string | no | no | nil | Use key as `insert_id` of Streaming Insert API parameter. |
57
+ | add_insert_timestamp | string | no | no | nil | Adds a timestamp column just before sending the rows to BigQuery, so that buffering time is not taken into account. Gives a field in BigQuery which represents the insert time of the row. |
58
+ | allow_retry_insert_errors | bool | no | no | false | Retry to insert rows when an insertErrors occurs. There is a possibility that rows are inserted in duplicate. |
59
+ | request_timeout_sec | integer | no | no | nil | Bigquery API response timeout |
60
+ | request_open_timeout_sec | integer | no | no | 60 | Bigquery API connection, and request timeout. If you send big data to Bigquery, set large value. |
61
+ | time_partitioning_type | enum | no (either day) | no | nil | Type of bigquery time partitioning feature(experimental feature on BigQuery). |
62
+ | time_partitioning_expiration | time | no | no | nil | Expiration milliseconds for bigquery time partitioning. (experimental feature on BigQuery) |
63
+
64
+ ### Deprecated
65
+
66
+ | name | type | required? | placeholder? | default | description |
67
+ | :------------------------------------- | :------------ | :----------- | :---------- | :------------------------- | :----------------------- |
68
+ | replace_record_key | bool | no | no | false | Use other filter plugin. |
69
+ | replace_record_key_regexp{1-10} | string | no | no | nil | |
71
70
 
72
71
  ### Buffer section
73
72
 
@@ -114,6 +113,27 @@ For example.
114
113
 
115
114
  see. https://github.com/fluent/fluentd/blob/master/lib/fluent/plugin_helper/inject.rb
116
115
 
116
+ ### Formatter section
117
+
118
+ This section is for `load` mode only.
119
+ If you use `insert` mode, used formatter is `json` only.
120
+
121
+ Bigquery supports `csv`, `json` and `avro` format. Default is `json`
122
+ I recommend to use `json` for now.
123
+
124
+ For example.
125
+
126
+ ```
127
+ source_format csv
128
+
129
+ <format>
130
+ @type csv
131
+ fields col1, col2, col3
132
+ </format>
133
+ ```
134
+
135
+ see. https://github.com/fluent/fluentd/blob/master/lib/fluent/plugin_helper/formatter.rb
136
+
117
137
  ## Examples
118
138
 
119
139
  ### Streaming inserts
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency "test-unit"
24
24
  spec.add_development_dependency "test-unit-rr"
25
25
 
26
- spec.add_runtime_dependency "google-api-client", ">= 0.9.3", "< 0.14"
26
+ spec.add_runtime_dependency "google-api-client", ">= 0.11.0"
27
27
  spec.add_runtime_dependency "googleauth", ">= 0.5.0"
28
28
  spec.add_runtime_dependency "multi_json"
29
29
  spec.add_runtime_dependency "fluentd", "~> 0.14.0"
@@ -3,7 +3,7 @@ module Fluent
3
3
  # @abstract
4
4
  class Error < StandardError
5
5
  RETRYABLE_ERROR_REASON = %w(backendError internalError rateLimitExceeded tableUnavailable).freeze
6
- RETRYABLE_INSERT_ERRORS_REASON = %w(timeout).freeze
6
+ RETRYABLE_INSERT_ERRORS_REASON = %w(timeout backendError internalError rateLimitExceeded).freeze
7
7
  RETRYABLE_STATUS_CODE = [500, 502, 503, 504]
8
8
 
9
9
  class << self
@@ -1,5 +1,5 @@
1
1
  module Fluent
2
2
  module BigQueryPlugin
3
- VERSION = "1.0.0".freeze
3
+ VERSION = "1.1.0".freeze
4
4
  end
5
5
  end
@@ -16,6 +16,9 @@ module Fluent
16
16
 
17
17
  client = Google::Apis::BigqueryV2::BigqueryService.new.tap do |cl|
18
18
  cl.authorization = get_auth
19
+ cl.client_options.open_timeout_sec = @options[:open_timeout_sec] if @options[:open_timeout_sec]
20
+ cl.client_options.read_timeout_sec = @options[:timeout_sec] if @options[:timeout_sec]
21
+ cl.client_options.send_timeout_sec = @options[:timeout_sec] if @options[:timeout_sec]
19
22
  end
20
23
 
21
24
  @cached_client_expiration = Time.now + 1800
@@ -91,9 +94,7 @@ module Fluent
91
94
  ignore_unknown_values: @options[:ignore_unknown_values],
92
95
  }
93
96
  body.merge!(template_suffix: template_suffix) if template_suffix
94
- res = client.insert_all_table_data(project, dataset, table_id, body, {
95
- options: {timeout_sec: @options[:timeout_sec], open_timeout_sec: @options[:open_timeout_sec]}
96
- })
97
+ res = client.insert_all_table_data(project, dataset, table_id, body, {})
97
98
  log.debug "insert rows", project_id: project, dataset: dataset, table: table_id, count: rows.size
98
99
 
99
100
  if res.insert_errors && !res.insert_errors.empty?
@@ -137,7 +138,7 @@ module Fluent
137
138
  fields: fields.to_a,
138
139
  },
139
140
  write_disposition: "WRITE_APPEND",
140
- source_format: "NEWLINE_DELIMITED_JSON",
141
+ source_format: source_format,
141
142
  ignore_unknown_values: @options[:ignore_unknown_values],
142
143
  max_bad_records: @options[:max_bad_records],
143
144
  }
@@ -164,10 +165,6 @@ module Fluent
164
165
  {
165
166
  upload_source: upload_source,
166
167
  content_type: "application/octet-stream",
167
- options: {
168
- timeout_sec: @options[:timeout_sec],
169
- open_timeout_sec: @options[:open_timeout_sec],
170
- }
171
168
  }
172
169
  )
173
170
  wait_load_job(chunk_id, project, dataset, res.job_reference.job_id, table_id)
@@ -299,6 +296,19 @@ module Fluent
299
296
  @log.debug "job_id_key: #{job_id_key}"
300
297
  "fluentd_job_" + Digest::SHA1.hexdigest(job_id_key)
301
298
  end
299
+
300
+ def source_format
301
+ case @options[:source_format]
302
+ when :json
303
+ "NEWLINE_DELIMITED_JSON"
304
+ when :avro
305
+ "AVRO"
306
+ when :csv
307
+ "CSV"
308
+ else
309
+ "NEWLINE_DELIMITED_JSON"
310
+ end
311
+ end
302
312
  end
303
313
  end
304
314
  end
@@ -14,7 +14,7 @@ module Fluent
14
14
  class BigQueryOutput < Output
15
15
  Fluent::Plugin.register_output('bigquery', self)
16
16
 
17
- helpers :inject
17
+ helpers :inject, :formatter
18
18
 
19
19
  # https://developers.google.com/bigquery/browser-tool-quickstart
20
20
  # https://developers.google.com/bigquery/bigquery-api-quickstart
@@ -23,22 +23,33 @@ module Fluent
23
23
  def configure_for_insert(conf)
24
24
  raise ConfigError unless conf["method"].nil? || conf["method"] == "insert"
25
25
 
26
+ formatter_config = conf.elements("format")[0]
27
+ if formatter_config && formatter_config['@type'] != "json"
28
+ log.warn "`insert` mode supports only json formatter."
29
+ formatter_config['@type'] = nil
30
+ end
31
+ @formatter = formatter_create(usage: 'out_bigquery_for_insert', type: 'json', conf: formatter_config)
32
+
26
33
  buffer_config = conf.elements("buffer")[0]
27
- return unless buffer_config
28
- buffer_config["@type"] = "memory" unless buffer_config["@type"]
29
- buffer_config["flush_mode"] = :interval unless buffer_config["flush_mode"]
30
- buffer_config["flush_interval"] = 0.25 unless buffer_config["flush_interval"]
31
- buffer_config["flush_thread_interval"] = 0.05 unless buffer_config["flush_thread_interval"]
32
- buffer_config["flush_thread_burst_interval"] = 0.05 unless buffer_config["flush_thread_burst_interval"]
33
- buffer_config["chunk_limit_size"] = 1 * 1024 ** 2 unless buffer_config["chunk_limit_size"] # 1MB
34
- buffer_config["total_limit_size"] = 1 * 1024 ** 3 unless buffer_config["total_limit_size"] # 1GB
35
- buffer_config["chunk_records_limit"] = 500 unless buffer_config["chunk_records_limit"]
34
+ if buffer_config
35
+ buffer_config["@type"] = "memory" unless buffer_config["@type"]
36
+ buffer_config["flush_mode"] = :interval unless buffer_config["flush_mode"]
37
+ buffer_config["flush_interval"] = 0.25 unless buffer_config["flush_interval"]
38
+ buffer_config["flush_thread_interval"] = 0.05 unless buffer_config["flush_thread_interval"]
39
+ buffer_config["flush_thread_burst_interval"] = 0.05 unless buffer_config["flush_thread_burst_interval"]
40
+ buffer_config["chunk_limit_size"] = 1 * 1024 ** 2 unless buffer_config["chunk_limit_size"] # 1MB
41
+ buffer_config["total_limit_size"] = 1 * 1024 ** 3 unless buffer_config["total_limit_size"] # 1GB
42
+ buffer_config["chunk_records_limit"] = 500 unless buffer_config["chunk_records_limit"]
43
+ end
36
44
  end
37
45
 
38
46
  ### default for loads
39
47
  def configure_for_load(conf)
40
48
  raise ConfigError unless conf["method"] == "load"
41
49
 
50
+ formatter_config = conf.elements("format")[0]
51
+ @formatter = formatter_create(usage: 'out_bigquery_for_load', conf: formatter_config, default_type: 'json')
52
+
42
53
  buffer_config = conf.elements("buffer")[0]
43
54
  return unless buffer_config
44
55
  buffer_config["@type"] = "file" unless buffer_config["@type"]
@@ -80,6 +91,8 @@ module Fluent
80
91
 
81
92
  config_param :auto_create_table, :bool, default: false
82
93
 
94
+ config_param :source_format, :enum, list: [:json, :avro, :csv], default: :json
95
+
83
96
  # skip_invalid_rows (only insert)
84
97
  # Insert all valid rows of a request, even if invalid rows exist.
85
98
  # The default value is false, which causes the entire request to fail if any invalid rows exist.
@@ -99,23 +112,11 @@ module Fluent
99
112
  config_param :fetch_schema, :bool, default: false
100
113
  config_param :fetch_schema_table, :string, default: nil
101
114
  config_param :schema_cache_expire, :time, default: 600
102
- config_param :field_string, :array, value_type: :string, default: nil
103
- config_param :field_integer, :array, value_type: :string, default: nil
104
- config_param :field_float, :array, value_type: :string, default: nil
105
- config_param :field_boolean, :array, value_type: :string, default: nil
106
- config_param :field_timestamp, :array, value_type: :string, default: nil
107
- ### TODO: record field stream inserts doesn't works well?
108
- ### At table creation, table type json + field type record -> field type validation fails
109
- ### At streaming inserts, schema cannot be specified
110
- # config_param :field_record, :string, defualt: nil
111
- # config_param :optional_data_field, :string, default: nil
112
115
 
113
116
  REGEXP_MAX_NUM = 10
114
117
  config_param :replace_record_key, :bool, default: false
115
118
  (1..REGEXP_MAX_NUM).each {|i| config_param :"replace_record_key_regexp#{i}", :string, default: nil }
116
119
 
117
- config_param :convert_hash_to_json, :bool, default: false
118
-
119
120
  # insert_id_field (only insert)
120
121
  config_param :insert_id_field, :string, default: nil
121
122
  # prevent_duplicate_load (only load)
@@ -157,6 +158,11 @@ module Fluent
157
158
  config_param :time_partitioning_type, :enum, list: [:day], default: nil
158
159
  config_param :time_partitioning_expiration, :time, default: nil
159
160
 
161
+ ## Formatter
162
+ config_section :format do
163
+ config_set_default :@type, 'json'
164
+ end
165
+
160
166
  ### Table types
161
167
  # https://developers.google.com/bigquery/docs/tables
162
168
  #
@@ -215,13 +221,14 @@ module Fluent
215
221
  raise Fluent::ConfigError, "unrecognized 'auth_method': #{@auth_method}"
216
222
  end
217
223
 
224
+ @writers = {}
225
+
218
226
  unless @table.nil? ^ @tables.nil?
219
227
  raise Fluent::ConfigError, "'table' or 'tables' must be specified, and both are invalid"
220
228
  end
221
229
 
222
230
  @tablelist = @tables ? @tables : [@table]
223
231
 
224
- legacy_schema_config_deprecation
225
232
  @table_schema = Fluent::BigQuery::RecordSchema.new('record')
226
233
  if @schema
227
234
  @table_schema.load_schema(@schema)
@@ -230,14 +237,7 @@ module Fluent
230
237
  @table_schema.load_schema(MultiJson.load(File.read(@schema_path)))
231
238
  end
232
239
 
233
- types = %i(string integer float boolean timestamp)
234
- types.each do |type|
235
- fields = instance_variable_get("@field_#{type}")
236
- next unless fields
237
- fields.each do |field|
238
- @table_schema.register_field field, type
239
- end
240
- end
240
+ warn "[DEPRECATION] `replace_record_key` param is deprecated. Please use filter_record_transformer or fluent-plugin-record-reformer" if @replace_record_key
241
241
 
242
242
  @regexps = {}
243
243
  (1..REGEXP_MAX_NUM).each do |i|
@@ -259,8 +259,6 @@ module Fluent
259
259
 
260
260
  placeholder_params = "project=#{@project}/dataset=#{@dataset}/table=#{@tablelist.join(",")}/fetch_schema_table=#{@fetch_schema_table}/template_suffix=#{@template_suffix}"
261
261
  placeholder_validate!(:bigquery, placeholder_params)
262
-
263
- warn "[DEPRECATION] `convert_hash_to_json` param is deprecated. If Hash value is inserted string field, plugin convert it to json automatically." if @convert_hash_to_json
264
262
  end
265
263
 
266
264
  def start
@@ -273,10 +271,11 @@ module Fluent
273
271
  end
274
272
 
275
273
  def writer
276
- @writer ||= Fluent::BigQuery::Writer.new(@log, @auth_method, {
274
+ @writers["thread-#{Thread.current.object_id}"] ||= Fluent::BigQuery::Writer.new(@log, @auth_method, {
277
275
  private_key_path: @private_key_path, private_key_passphrase: @private_key_passphrase,
278
276
  email: @email,
279
277
  json_key: @json_key,
278
+ source_format: @source_format,
280
279
  skip_invalid_rows: @skip_invalid_rows,
281
280
  ignore_unknown_values: @ignore_unknown_values,
282
281
  max_bad_records: @max_bad_records,
@@ -303,45 +302,25 @@ module Fluent
303
302
  new_record
304
303
  end
305
304
 
306
- def convert_hash_to_json(record)
307
- record.each do |key, value|
308
- if value.class == Hash
309
- record[key] = MultiJson.dump(value)
310
- end
311
- end
312
- record
313
- end
314
-
315
305
  def format(tag, time, record)
316
306
  if @replace_record_key
317
307
  record = replace_record_key(record)
318
308
  end
319
309
 
320
- if @convert_hash_to_json
321
- record = convert_hash_to_json(record)
322
- end
323
-
324
310
  record = inject_values_to_record(tag, time, record)
325
311
 
326
- begin
327
- meta = metadata(tag, time, record)
328
- schema =
329
- if @fetch_schema
330
- fetch_schema(meta)
331
- else
332
- @table_schema
333
- end
334
- ensure
335
- @buffer.metadata_list.delete(meta)
336
- end
312
+ meta = metadata(tag, time, record)
313
+ schema =
314
+ if @fetch_schema
315
+ fetch_schema(meta)
316
+ else
317
+ @table_schema
318
+ end
337
319
 
338
320
  begin
339
- buf = String.new
340
321
  row = schema.format(record)
341
- unless row.empty?
342
- buf << MultiJson.dump(row) + "\n"
343
- end
344
- buf
322
+ return if row.empty?
323
+ @formatter.format(tag, time, row)
345
324
  rescue
346
325
  log.error("format error", record: record, schema: schema)
347
326
  raise
@@ -357,12 +336,6 @@ module Fluent
357
336
  _write(chunk, table_id_format)
358
337
  end
359
338
 
360
- def legacy_schema_config_deprecation
361
- if [@field_string, @field_integer, @field_float, @field_boolean, @field_timestamp].any?
362
- warn "[DEPRECATION] `field_*` style schema config is deprecated. Instead of it, use `schema` config params that is array of json style."
363
- end
364
- end
365
-
366
339
  def fetch_schema(metadata)
367
340
  table_id = nil
368
341
  project = extract_placeholders(@project, metadata)
@@ -44,10 +44,12 @@ class BigQueryOutputTest < Test::Unit::TestCase
44
44
  Fluent::Test::Driver::Output.new(Fluent::Plugin::BigQueryOutput).configure(conf)
45
45
  end
46
46
 
47
- def stub_writer(driver)
48
- writer = driver.instance.writer
49
- stub(writer).get_auth { nil }
50
- writer
47
+ def stub_writer(driver, stub_auth: true)
48
+ stub.proxy(Fluent::BigQuery::Writer).new.with_any_args do |writer|
49
+ stub(writer).get_auth { nil } if stub_auth
50
+ yield writer
51
+ writer
52
+ end
51
53
  end
52
54
 
53
55
  def test_configure_table
@@ -65,37 +67,14 @@ class BigQueryOutputTest < Test::Unit::TestCase
65
67
  end
66
68
 
67
69
  def test_configure_auth_private_key
68
- key = stub!
69
- mock(Google::APIClient::KeyUtils).load_from_pkcs12('/path/to/key', 'notasecret') { key }
70
- authorization = Object.new
71
- stub(Signet::OAuth2::Client).new
72
- mock(Signet::OAuth2::Client).new(
73
- token_credential_uri: "https://accounts.google.com/o/oauth2/token",
74
- audience: "https://accounts.google.com/o/oauth2/token",
75
- scope: API_SCOPE,
76
- issuer: 'foo@bar.example',
77
- signing_key: key) { authorization }
78
-
79
- mock.proxy(Google::Apis::BigqueryV2::BigqueryService).new.with_any_args do |cl|
80
- mock(cl).__send__(:authorization=, authorization) {}
81
- cl
82
- end
83
-
84
70
  driver = create_driver
85
- mock.proxy(Fluent::BigQuery::Writer).new(duck_type(:info, :error, :warn), driver.instance.auth_method, is_a(Hash))
86
- driver.instance.writer
71
+ stub_writer(driver, stub_auth: false) do |writer|
72
+ mock(writer).get_auth_from_private_key { stub! }
73
+ end
87
74
  assert driver.instance.writer.client.is_a?(Google::Apis::BigqueryV2::BigqueryService)
88
75
  end
89
76
 
90
77
  def test_configure_auth_compute_engine
91
- authorization = Object.new
92
- mock(Google::Auth::GCECredentials).new { authorization }
93
-
94
- mock.proxy(Google::Apis::BigqueryV2::BigqueryService).new.with_any_args do |cl|
95
- mock(cl).__send__(:authorization=, authorization) {}
96
- cl
97
- end
98
-
99
78
  driver = create_driver(%[
100
79
  table foo
101
80
  auth_method compute_engine
@@ -107,25 +86,18 @@ class BigQueryOutputTest < Test::Unit::TestCase
107
86
  {"name": "bytes", "type": "INTEGER"}
108
87
  ]
109
88
  ])
110
- mock.proxy(Fluent::BigQuery::Writer).new(duck_type(:info, :error, :warn), driver.instance.auth_method, is_a(Hash))
111
- driver.instance.writer
89
+
90
+ stub_writer(driver, stub_auth: false) do |writer|
91
+ mock(writer).get_auth_from_compute_engine { stub! }
92
+ end
112
93
  assert driver.instance.writer.client.is_a?(Google::Apis::BigqueryV2::BigqueryService)
113
94
  end
114
95
 
115
96
  def test_configure_auth_json_key_as_file
116
- json_key_path = 'test/plugin/testdata/json_key.json'
117
- authorization = Object.new
118
- mock(Google::Auth::ServiceAccountCredentials).make_creds(json_key_io: File.open(json_key_path), scope: API_SCOPE) { authorization }
119
-
120
- mock.proxy(Google::Apis::BigqueryV2::BigqueryService).new.with_any_args do |cl|
121
- mock(cl).__send__(:authorization=, authorization) {}
122
- cl
123
- end
124
-
125
97
  driver = create_driver(%[
126
98
  table foo
127
99
  auth_method json_key
128
- json_key #{json_key_path}
100
+ json_key jsonkey.josn
129
101
  project yourproject_id
130
102
  dataset yourdataset_id
131
103
  schema [
@@ -134,8 +106,10 @@ class BigQueryOutputTest < Test::Unit::TestCase
134
106
  {"name": "bytes", "type": "INTEGER"}
135
107
  ]
136
108
  ])
137
- mock.proxy(Fluent::BigQuery::Writer).new(duck_type(:info, :error, :warn), driver.instance.auth_method, is_a(Hash))
138
- driver.instance.writer
109
+
110
+ stub_writer(driver, stub_auth: false) do |writer|
111
+ mock(writer).get_auth_from_json_key { stub! }
112
+ end
139
113
  assert driver.instance.writer.client.is_a?(Google::Apis::BigqueryV2::BigqueryService)
140
114
  end
141
115
 
@@ -170,12 +144,7 @@ class BigQueryOutputTest < Test::Unit::TestCase
170
144
  json_key = '{"private_key": "X", "client_email": "' + 'x' * 255 + '@developer.gserviceaccount.com"}'
171
145
  json_key_io = StringIO.new(json_key)
172
146
  authorization = Object.new
173
- mock(Google::Auth::ServiceAccountCredentials).make_creds(json_key_io: satisfy {|arg| JSON.parse(arg.read) == JSON.parse(json_key_io.read) }, scope: API_SCOPE) { authorization }
174
-
175
- mock.proxy(Google::Apis::BigqueryV2::BigqueryService).new.with_any_args do |cl|
176
- mock(cl).__send__(:authorization=, authorization) {}
177
- cl
178
- end
147
+ stub(Google::Auth::ServiceAccountCredentials).make_creds(json_key_io: satisfy {|arg| JSON.parse(arg.read) == JSON.parse(json_key_io.read) }, scope: API_SCOPE) { authorization }
179
148
 
180
149
  driver = create_driver(%[
181
150
  table foo
@@ -189,20 +158,13 @@ class BigQueryOutputTest < Test::Unit::TestCase
189
158
  {"name": "bytes", "type": "INTEGER"}
190
159
  ]
191
160
  ])
192
- mock.proxy(Fluent::BigQuery::Writer).new(duck_type(:info, :error, :warn), driver.instance.auth_method, is_a(Hash))
193
- driver.instance.writer
161
+ stub_writer(driver, stub_auth: false) do |writer|
162
+ mock.proxy(writer).get_auth_from_json_key { stub! }
163
+ end
194
164
  assert driver.instance.writer.client.is_a?(Google::Apis::BigqueryV2::BigqueryService)
195
165
  end
196
166
 
197
167
  def test_configure_auth_application_default
198
- authorization = Object.new
199
- mock(Google::Auth).get_application_default([API_SCOPE]) { authorization }
200
-
201
- mock.proxy(Google::Apis::BigqueryV2::BigqueryService).new.with_any_args do |cl|
202
- mock(cl).__send__(:authorization=, authorization) {}
203
- cl
204
- end
205
-
206
168
  driver = create_driver(%[
207
169
  table foo
208
170
  auth_method application_default
@@ -215,8 +177,9 @@ class BigQueryOutputTest < Test::Unit::TestCase
215
177
  ]
216
178
  ])
217
179
 
218
- mock.proxy(Fluent::BigQuery::Writer).new(duck_type(:info, :error, :warn), driver.instance.auth_method, is_a(Hash))
219
- driver.instance.writer
180
+ stub_writer(driver, stub_auth: false) do |writer|
181
+ mock.proxy(writer).get_auth_from_application_default { stub! }
182
+ end
220
183
  assert driver.instance.writer.client.is_a?(Google::Apis::BigqueryV2::BigqueryService)
221
184
  end
222
185
 
@@ -282,18 +245,18 @@ class BigQueryOutputTest < Test::Unit::TestCase
282
245
  [
283
246
  # <time_format>, <time field type>, <time expectation generator>, <assertion>
284
247
  [
285
- "%s.%6N", "field_float",
248
+ "%s.%6N",
286
249
  lambda{|t| t.strftime("%s.%6N").to_f },
287
250
  lambda{|recv, expected, actual|
288
251
  recv.assert_in_delta(expected, actual, Float::EPSILON / 10**3)
289
252
  }
290
253
  ],
291
254
  [
292
- "%Y-%m-%dT%H:%M:%S%:z", "field_string",
255
+ "%Y-%m-%dT%H:%M:%S%:z",
293
256
  lambda{|t| t.iso8601 },
294
257
  :assert_equal.to_proc
295
258
  ],
296
- ].each do |format, type, expect_time, assert|
259
+ ].each do |format, expect_time, assert|
297
260
  define_method("test_time_formats_#{format}") do
298
261
  now = Fluent::Engine.now
299
262
  input = {}
@@ -311,7 +274,6 @@ class BigQueryOutputTest < Test::Unit::TestCase
311
274
  time_type string
312
275
  time_key time
313
276
  </inject>
314
- #{type} time
315
277
 
316
278
  schema [
317
279
  {"name": "metadata", "type": "RECORD", "fields": [
@@ -479,9 +441,10 @@ class BigQueryOutputTest < Test::Unit::TestCase
479
441
  schema [{"name": "time", "type": "INTEGER"}]
480
442
  CONFIG
481
443
 
482
- writer = stub_writer(driver)
483
- mock(writer).fetch_schema('yourproject_id', 'yourdataset_id', 'foo') do
484
- sudo_schema_response["schema"]["fields"]
444
+ stub_writer(driver) do |writer|
445
+ mock(writer).fetch_schema('yourproject_id', 'yourdataset_id', 'foo') do
446
+ sudo_schema_response["schema"]["fields"]
447
+ end
485
448
  end
486
449
 
487
450
  buf = nil
@@ -547,9 +510,10 @@ class BigQueryOutputTest < Test::Unit::TestCase
547
510
  </buffer>
548
511
  CONFIG
549
512
 
550
- writer = stub_writer(driver)
551
- mock(writer).fetch_schema('yourproject_id', 'yourdataset_id', 'foo') do
552
- sudo_schema_response["schema"]["fields"]
513
+ stub_writer(driver) do |writer|
514
+ mock(writer).fetch_schema('yourproject_id', 'yourdataset_id', 'foo') do
515
+ sudo_schema_response["schema"]["fields"]
516
+ end
553
517
  end
554
518
 
555
519
  buf = nil
@@ -689,72 +653,21 @@ class BigQueryOutputTest < Test::Unit::TestCase
689
653
  assert_equal expected, MultiJson.load(buf)
690
654
  end
691
655
 
692
- def test_convert_hash_to_json
693
- now = Fluent::EventTime.now
694
- input = {
695
- "vhost" => :bar,
696
- "referer" => "http://referer.example",
697
- "bot_access" => true,
698
- "loginsession" => false,
699
- "remote" => {
700
- "host" => "remote.example",
701
- "ip" => "192.0.2.1",
702
- "port" => 12345,
703
- "user" => "tagomoris",
704
- }
705
- }
706
- expected = {
707
- "time" => now.to_i,
708
- "vhost" => "bar",
709
- "referer" => "http://referer.example",
710
- "bot_access" => true,
711
- "loginsession" => false,
712
- "remote" => "{\"host\":\"remote.example\",\"ip\":\"192.0.2.1\",\"port\":12345,\"user\":\"tagomoris\"}"
713
- }
714
-
715
- driver = create_driver(<<-CONFIG)
716
- table foo
717
- email foo@bar.example
718
- private_key_path /path/to/key
719
- project yourproject_id
720
- dataset yourdataset_id
721
-
722
- convert_hash_to_json true
723
-
724
- <inject>
725
- time_format %s
726
- time_key time
727
- </inject>
728
-
729
- schema [
730
- {"name": "time", "type": "INTEGER"},
731
- {"name": "vhost", "type": "STRING"},
732
- {"name": "refere", "type": "STRING"},
733
- {"name": "bot_access", "type": "BOOLEAN"},
734
- {"name": "loginsession", "type": "BOOLEAN"}
735
- ]
736
- CONFIG
737
-
738
- buf = nil
739
- driver.run { buf = driver.instance.format("my.tag", now, input) }
740
-
741
- assert_equal expected, MultiJson.load(buf)
742
- end
743
-
744
656
  def test_write
745
657
  entry = {a: "b"}
746
658
  driver = create_driver
747
659
 
748
- writer = stub_writer(driver)
749
- mock.proxy(writer).insert_rows('yourproject_id', 'yourdataset_id', 'foo', [{json: hash_including(entry)}], template_suffix: nil)
750
- mock(writer.client).insert_all_table_data('yourproject_id', 'yourdataset_id', 'foo', {
751
- rows: [{json: hash_including(entry)}],
752
- skip_invalid_rows: false,
753
- ignore_unknown_values: false
754
- }, {options: {timeout_sec: nil, open_timeout_sec: 60}}) do
755
- s = stub!
756
- s.insert_errors { nil }
757
- s
660
+ stub_writer(driver) do |writer|
661
+ mock.proxy(writer).insert_rows('yourproject_id', 'yourdataset_id', 'foo', [{json: hash_including(entry)}], template_suffix: nil)
662
+ mock(writer.client).insert_all_table_data('yourproject_id', 'yourdataset_id', 'foo', {
663
+ rows: [{json: hash_including(entry)}],
664
+ skip_invalid_rows: false,
665
+ ignore_unknown_values: false
666
+ }, {}) do
667
+ s = stub!
668
+ s.insert_errors { nil }
669
+ s
670
+ end
758
671
  end
759
672
 
760
673
  driver.run do
@@ -810,14 +723,15 @@ class BigQueryOutputTest < Test::Unit::TestCase
810
723
  CONFIG
811
724
 
812
725
  entry = {a: "b"}
813
- writer = stub_writer(driver)
814
- mock(writer.client).insert_all_table_data('yourproject_id', 'yourdataset_id', 'foo', {
815
- rows: [{json: hash_including(entry)}],
816
- skip_invalid_rows: false,
817
- ignore_unknown_values: false
818
- }, {options: {timeout_sec: nil, open_timeout_sec: 60}}) do
819
- ex = Google::Apis::ServerError.new("error", status_code: d["status_code"])
820
- raise ex
726
+ stub_writer(driver) do |writer|
727
+ mock(writer.client).insert_all_table_data('yourproject_id', 'yourdataset_id', 'foo', {
728
+ rows: [{json: hash_including(entry)}],
729
+ skip_invalid_rows: false,
730
+ ignore_unknown_values: false
731
+ }, {}) do
732
+ ex = Google::Apis::ServerError.new("error", status_code: d["status_code"])
733
+ raise ex
734
+ end
821
735
  end
822
736
 
823
737
  assert_raise(Fluent::BigQuery::RetryableError) do
@@ -868,17 +782,18 @@ class BigQueryOutputTest < Test::Unit::TestCase
868
782
  CONFIG
869
783
 
870
784
  entry = {a: "b"}
871
- writer = stub_writer(driver)
872
- mock(writer.client).insert_all_table_data('yourproject_id', 'yourdataset_id', 'foo', {
873
- rows: [{json: hash_including(entry)}],
874
- skip_invalid_rows: false,
875
- ignore_unknown_values: false
876
- }, {options: {timeout_sec: nil, open_timeout_sec: 60}}) do
877
- ex = Google::Apis::ServerError.new("error", status_code: 501)
878
- def ex.reason
879
- "invalid"
785
+ stub_writer(driver) do |writer|
786
+ mock(writer.client).insert_all_table_data('yourproject_id', 'yourdataset_id', 'foo', {
787
+ rows: [{json: hash_including(entry)}],
788
+ skip_invalid_rows: false,
789
+ ignore_unknown_values: false
790
+ }, {}) do
791
+ ex = Google::Apis::ServerError.new("error", status_code: 501)
792
+ def ex.reason
793
+ "invalid"
794
+ end
795
+ raise ex
880
796
  end
881
- raise ex
882
797
  end
883
798
 
884
799
  driver.instance_start
@@ -915,33 +830,36 @@ class BigQueryOutputTest < Test::Unit::TestCase
915
830
  CONFIG
916
831
  schema_fields = Fluent::BigQuery::Helper.deep_symbolize_keys(MultiJson.load(File.read(schema_path)))
917
832
 
918
- writer = stub_writer(driver)
919
833
  io = StringIO.new("hello")
920
834
  mock(driver.instance).create_upload_source(is_a(Fluent::Plugin::Buffer::Chunk)).yields(io)
921
- mock(writer).wait_load_job(is_a(String), "yourproject_id", "yourdataset_id", "dummy_job_id", "foo") { nil }
922
- mock(writer.client).insert_job('yourproject_id', {
923
- configuration: {
924
- load: {
925
- destination_table: {
926
- project_id: 'yourproject_id',
927
- dataset_id: 'yourdataset_id',
928
- table_id: 'foo',
929
- },
930
- schema: {
931
- fields: schema_fields,
932
- },
933
- write_disposition: "WRITE_APPEND",
934
- source_format: "NEWLINE_DELIMITED_JSON",
935
- ignore_unknown_values: false,
936
- max_bad_records: 0,
835
+ stub_writer(driver) do |writer|
836
+ mock(writer).wait_load_job(is_a(String), "yourproject_id", "yourdataset_id", "dummy_job_id", "foo") { nil }
837
+ mock(writer.client).get_table('yourproject_id', 'yourdataset_id', 'foo') { nil }
838
+
839
+ mock(writer.client).insert_job('yourproject_id', {
840
+ configuration: {
841
+ load: {
842
+ destination_table: {
843
+ project_id: 'yourproject_id',
844
+ dataset_id: 'yourdataset_id',
845
+ table_id: 'foo',
846
+ },
847
+ schema: {
848
+ fields: schema_fields,
849
+ },
850
+ write_disposition: "WRITE_APPEND",
851
+ source_format: "NEWLINE_DELIMITED_JSON",
852
+ ignore_unknown_values: false,
853
+ max_bad_records: 0,
854
+ }
937
855
  }
938
- }
939
- }, {upload_source: io, content_type: "application/octet-stream", options: {timeout_sec: nil, open_timeout_sec: 60}}) do
940
- s = stub!
941
- job_reference_stub = stub!
942
- s.job_reference { job_reference_stub }
943
- job_reference_stub.job_id { "dummy_job_id" }
944
- s
856
+ }, {upload_source: io, content_type: "application/octet-stream"}) do
857
+ s = stub!
858
+ job_reference_stub = stub!
859
+ s.job_reference { job_reference_stub }
860
+ job_reference_stub.job_id { "dummy_job_id" }
861
+ s
862
+ end
945
863
  end
946
864
 
947
865
  driver.run do
@@ -973,32 +891,35 @@ class BigQueryOutputTest < Test::Unit::TestCase
973
891
 
974
892
  io = StringIO.new("hello")
975
893
  mock(driver.instance).create_upload_source(is_a(Fluent::Plugin::Buffer::Chunk)).yields(io)
976
- writer = stub_writer(driver)
977
- mock(writer).wait_load_job(is_a(String), "yourproject_id", "yourdataset_id", "dummy_job_id", "foo") { nil }
978
- mock(writer.client).insert_job('yourproject_id', {
979
- configuration: {
980
- load: {
981
- destination_table: {
982
- project_id: 'yourproject_id',
983
- dataset_id: 'yourdataset_id',
984
- table_id: 'foo',
894
+ stub_writer(driver) do |writer|
895
+ mock(writer).wait_load_job(is_a(String), "yourproject_id", "yourdataset_id", "dummy_job_id", "foo") { nil }
896
+ mock(writer.client).get_table('yourproject_id', 'yourdataset_id', 'foo') { nil }
897
+
898
+ mock(writer.client).insert_job('yourproject_id', {
899
+ configuration: {
900
+ load: {
901
+ destination_table: {
902
+ project_id: 'yourproject_id',
903
+ dataset_id: 'yourdataset_id',
904
+ table_id: 'foo',
905
+ },
906
+ schema: {
907
+ fields: schema_fields,
908
+ },
909
+ write_disposition: "WRITE_APPEND",
910
+ source_format: "NEWLINE_DELIMITED_JSON",
911
+ ignore_unknown_values: false,
912
+ max_bad_records: 0,
985
913
  },
986
- schema: {
987
- fields: schema_fields,
988
- },
989
- write_disposition: "WRITE_APPEND",
990
- source_format: "NEWLINE_DELIMITED_JSON",
991
- ignore_unknown_values: false,
992
- max_bad_records: 0,
993
914
  },
994
- },
995
- job_reference: {project_id: 'yourproject_id', job_id: satisfy { |x| x =~ /fluentd_job_.*/}} ,
996
- }, {upload_source: io, content_type: "application/octet-stream", options: {timeout_sec: nil, open_timeout_sec: 60}}) do
997
- s = stub!
998
- job_reference_stub = stub!
999
- s.job_reference { job_reference_stub }
1000
- job_reference_stub.job_id { "dummy_job_id" }
1001
- s
915
+ job_reference: {project_id: 'yourproject_id', job_id: satisfy { |x| x =~ /fluentd_job_.*/}} ,
916
+ }, {upload_source: io, content_type: "application/octet-stream"}) do
917
+ s = stub!
918
+ job_reference_stub = stub!
919
+ s.job_reference { job_reference_stub }
920
+ job_reference_stub.job_id { "dummy_job_id" }
921
+ s
922
+ end
1002
923
  end
1003
924
 
1004
925
  driver.run do
@@ -1013,7 +934,7 @@ class BigQueryOutputTest < Test::Unit::TestCase
1013
934
  table foo
1014
935
  email foo@bar.example
1015
936
  private_key_path /path/to/key
1016
- project yourproject_id
937
+ project yourproject-id
1017
938
  dataset yourdataset_id
1018
939
 
1019
940
  <inject>
@@ -1036,44 +957,48 @@ class BigQueryOutputTest < Test::Unit::TestCase
1036
957
 
1037
958
  io = StringIO.new("hello")
1038
959
  mock(driver.instance).create_upload_source(chunk).yields(io)
1039
- writer = stub_writer(driver)
1040
- mock(writer.client).insert_job('yourproject_id', {
1041
- configuration: {
1042
- load: {
1043
- destination_table: {
1044
- project_id: 'yourproject_id',
1045
- dataset_id: 'yourdataset_id',
1046
- table_id: 'foo',
1047
- },
1048
- schema: {
1049
- fields: schema_fields,
1050
- },
1051
- write_disposition: "WRITE_APPEND",
1052
- source_format: "NEWLINE_DELIMITED_JSON",
1053
- ignore_unknown_values: false,
1054
- max_bad_records: 0,
960
+
961
+ stub_writer(driver) do |writer|
962
+ mock(writer.client).get_table('yourproject-id', 'yourdataset_id', 'foo') { nil }
963
+
964
+ mock(writer.client).insert_job('yourproject-id', {
965
+ configuration: {
966
+ load: {
967
+ destination_table: {
968
+ project_id: 'yourproject-id',
969
+ dataset_id: 'yourdataset_id',
970
+ table_id: 'foo',
971
+ },
972
+ schema: {
973
+ fields: schema_fields,
974
+ },
975
+ write_disposition: "WRITE_APPEND",
976
+ source_format: "NEWLINE_DELIMITED_JSON",
977
+ ignore_unknown_values: false,
978
+ max_bad_records: 0,
979
+ }
1055
980
  }
1056
- }
1057
- }, {upload_source: io, content_type: "application/octet-stream", options: {timeout_sec: nil, open_timeout_sec: 60}}) do
1058
- s = stub!
1059
- job_reference_stub = stub!
1060
- s.job_reference { job_reference_stub }
1061
- job_reference_stub.job_id { "dummy_job_id" }
1062
- s
1063
- end
981
+ }, {upload_source: io, content_type: "application/octet-stream"}) do
982
+ s = stub!
983
+ job_reference_stub = stub!
984
+ s.job_reference { job_reference_stub }
985
+ job_reference_stub.job_id { "dummy_job_id" }
986
+ s
987
+ end
1064
988
 
1065
- mock(writer.client).get_job('yourproject_id', 'dummy_job_id') do
1066
- s = stub!
1067
- status_stub = stub!
1068
- error_result = stub!
1069
-
1070
- s.status { status_stub }
1071
- status_stub.state { "DONE" }
1072
- status_stub.error_result { error_result }
1073
- status_stub.errors { nil }
1074
- error_result.message { "error" }
1075
- error_result.reason { "backendError" }
1076
- s
989
+ mock(writer.client).get_job('yourproject-id', 'dummy_job_id') do
990
+ s = stub!
991
+ status_stub = stub!
992
+ error_result = stub!
993
+
994
+ s.status { status_stub }
995
+ status_stub.state { "DONE" }
996
+ status_stub.error_result { error_result }
997
+ status_stub.errors { nil }
998
+ error_result.message { "error" }
999
+ error_result.reason { "backendError" }
1000
+ s
1001
+ end
1077
1002
  end
1078
1003
 
1079
1004
  assert_raise Fluent::BigQuery::RetryableError do
@@ -1117,44 +1042,47 @@ class BigQueryOutputTest < Test::Unit::TestCase
1117
1042
 
1118
1043
  io = StringIO.new("hello")
1119
1044
  mock(driver.instance).create_upload_source(chunk).yields(io)
1120
- writer = stub_writer(driver)
1121
- mock(writer.client).insert_job('yourproject_id', {
1122
- configuration: {
1123
- load: {
1124
- destination_table: {
1125
- project_id: 'yourproject_id',
1126
- dataset_id: 'yourdataset_id',
1127
- table_id: 'foo',
1128
- },
1129
- schema: {
1130
- fields: schema_fields,
1131
- },
1132
- write_disposition: "WRITE_APPEND",
1133
- source_format: "NEWLINE_DELIMITED_JSON",
1134
- ignore_unknown_values: false,
1135
- max_bad_records: 0,
1045
+ stub_writer(driver) do |writer|
1046
+ mock(writer.client).get_table('yourproject_id', 'yourdataset_id', 'foo') { nil }
1047
+
1048
+ mock(writer.client).insert_job('yourproject_id', {
1049
+ configuration: {
1050
+ load: {
1051
+ destination_table: {
1052
+ project_id: 'yourproject_id',
1053
+ dataset_id: 'yourdataset_id',
1054
+ table_id: 'foo',
1055
+ },
1056
+ schema: {
1057
+ fields: schema_fields,
1058
+ },
1059
+ write_disposition: "WRITE_APPEND",
1060
+ source_format: "NEWLINE_DELIMITED_JSON",
1061
+ ignore_unknown_values: false,
1062
+ max_bad_records: 0,
1063
+ }
1136
1064
  }
1137
- }
1138
- }, {upload_source: io, content_type: "application/octet-stream", options: {timeout_sec: nil, open_timeout_sec: 60}}) do
1139
- s = stub!
1140
- job_reference_stub = stub!
1141
- s.job_reference { job_reference_stub }
1142
- job_reference_stub.job_id { "dummy_job_id" }
1143
- s
1144
- end
1065
+ }, {upload_source: io, content_type: "application/octet-stream"}) do
1066
+ s = stub!
1067
+ job_reference_stub = stub!
1068
+ s.job_reference { job_reference_stub }
1069
+ job_reference_stub.job_id { "dummy_job_id" }
1070
+ s
1071
+ end
1145
1072
 
1146
- mock(writer.client).get_job('yourproject_id', 'dummy_job_id') do
1147
- s = stub!
1148
- status_stub = stub!
1149
- error_result = stub!
1150
-
1151
- s.status { status_stub }
1152
- status_stub.state { "DONE" }
1153
- status_stub.error_result { error_result }
1154
- status_stub.errors { nil }
1155
- error_result.message { "error" }
1156
- error_result.reason { "invalid" }
1157
- s
1073
+ mock(writer.client).get_job('yourproject_id', 'dummy_job_id') do
1074
+ s = stub!
1075
+ status_stub = stub!
1076
+ error_result = stub!
1077
+
1078
+ s.status { status_stub }
1079
+ status_stub.state { "DONE" }
1080
+ status_stub.error_result { error_result }
1081
+ status_stub.errors { nil }
1082
+ error_result.message { "error" }
1083
+ error_result.reason { "invalid" }
1084
+ s
1085
+ end
1158
1086
  end
1159
1087
 
1160
1088
  assert_raise Fluent::BigQuery::UnRetryableError do
@@ -1182,12 +1110,13 @@ class BigQueryOutputTest < Test::Unit::TestCase
1182
1110
  ]
1183
1111
  CONFIG
1184
1112
 
1185
- writer = stub_writer(driver)
1186
- mock(writer.client).insert_all_table_data('yourproject_id', 'yourdataset_id', 'foo_2014_08_20', {
1187
- rows: [entry[0]],
1188
- skip_invalid_rows: false,
1189
- ignore_unknown_values: false
1190
- }, {options: {timeout_sec: nil, open_timeout_sec: 60}}) { stub!.insert_errors { nil } }
1113
+ stub_writer(driver) do |writer|
1114
+ mock(writer.client).insert_all_table_data('yourproject_id', 'yourdataset_id', 'foo_2014_08_20', {
1115
+ rows: [entry[0]],
1116
+ skip_invalid_rows: false,
1117
+ ignore_unknown_values: false
1118
+ }, {}) { stub!.insert_errors { nil } }
1119
+ end
1191
1120
 
1192
1121
  driver.run do
1193
1122
  driver.feed("tag", Time.now.to_i, {"a" => "b", "created_at" => Time.local(2014,8,20,9,0,0).strftime("%Y_%m_%d")})
@@ -1235,11 +1164,13 @@ class BigQueryOutputTest < Test::Unit::TestCase
1235
1164
  auto_create_table true
1236
1165
  schema_path #{File.join(File.dirname(__FILE__), "testdata", "apache.schema")}
1237
1166
  CONFIG
1238
- writer = stub_writer(driver)
1239
- mock(writer).insert_rows('yourproject_id', 'yourdataset_id', 'foo', [{json: Fluent::BigQuery::Helper.deep_symbolize_keys(message)}], template_suffix: nil) do
1240
- raise Fluent::BigQuery::RetryableError.new(nil, Google::Apis::ServerError.new("Not found: Table yourproject_id:yourdataset_id.foo", status_code: 404, body: "Not found: Table yourproject_id:yourdataset_id.foo"))
1167
+
1168
+ stub_writer(driver) do |writer|
1169
+ mock(writer).insert_rows('yourproject_id', 'yourdataset_id', 'foo', [{json: Fluent::BigQuery::Helper.deep_symbolize_keys(message)}], template_suffix: nil) do
1170
+ raise Fluent::BigQuery::RetryableError.new(nil, Google::Apis::ServerError.new("Not found: Table yourproject_id:yourdataset_id.foo", status_code: 404, body: "Not found: Table yourproject_id:yourdataset_id.foo"))
1171
+ end
1172
+ mock(writer).create_table('yourproject_id', 'yourdataset_id', 'foo', driver.instance.instance_variable_get(:@table_schema))
1241
1173
  end
1242
- mock(writer).create_table('yourproject_id', 'yourdataset_id', 'foo', driver.instance.instance_variable_get(:@table_schema))
1243
1174
 
1244
1175
  assert_raise(RuntimeError) do
1245
1176
  driver.run do
@@ -1292,11 +1223,13 @@ class BigQueryOutputTest < Test::Unit::TestCase
1292
1223
  time_partitioning_type day
1293
1224
  time_partitioning_expiration 1h
1294
1225
  CONFIG
1295
- writer = stub_writer(driver)
1296
- mock(writer).insert_rows('yourproject_id', 'yourdataset_id', 'foo', [message], template_suffix: nil) do
1297
- raise Fluent::BigQuery::RetryableError.new(nil, Google::Apis::ServerError.new("Not found: Table yourproject_id:yourdataset_id.foo", status_code: 404, body: "Not found: Table yourproject_id:yourdataset_id.foo"))
1226
+
1227
+ stub_writer(driver) do |writer|
1228
+ mock(writer).insert_rows('yourproject_id', 'yourdataset_id', 'foo', [message], template_suffix: nil) do
1229
+ raise Fluent::BigQuery::RetryableError.new(nil, Google::Apis::ServerError.new("Not found: Table yourproject_id:yourdataset_id.foo", status_code: 404, body: "Not found: Table yourproject_id:yourdataset_id.foo"))
1230
+ end
1231
+ mock(writer).create_table('yourproject_id', 'yourdataset_id', 'foo', driver.instance.instance_variable_get(:@table_schema))
1298
1232
  end
1299
- mock(writer).create_table('yourproject_id', 'yourdataset_id', 'foo', driver.instance.instance_variable_get(:@table_schema))
1300
1233
 
1301
1234
  assert_raise(RuntimeError) do
1302
1235
  driver.run do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-bigquery
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Naoya Ito
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-06-29 00:00:00.000000000 Z
12
+ date: 2017-10-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -73,20 +73,14 @@ dependencies:
73
73
  requirements:
74
74
  - - ">="
75
75
  - !ruby/object:Gem::Version
76
- version: 0.9.3
77
- - - "<"
78
- - !ruby/object:Gem::Version
79
- version: '0.14'
76
+ version: 0.11.0
80
77
  type: :runtime
81
78
  prerelease: false
82
79
  version_requirements: !ruby/object:Gem::Requirement
83
80
  requirements:
84
81
  - - ">="
85
82
  - !ruby/object:Gem::Version
86
- version: 0.9.3
87
- - - "<"
88
- - !ruby/object:Gem::Version
89
- version: '0.14'
83
+ version: 0.11.0
90
84
  - !ruby/object:Gem::Dependency
91
85
  name: googleauth
92
86
  requirement: !ruby/object:Gem::Requirement
@@ -138,6 +132,7 @@ executables: []
138
132
  extensions: []
139
133
  extra_rdoc_files: []
140
134
  files:
135
+ - ".github/ISSUE_TEMPLATE.md"
141
136
  - ".gitignore"
142
137
  - ".travis.yml"
143
138
  - Gemfile
@@ -179,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
179
174
  version: '0'
180
175
  requirements: []
181
176
  rubyforge_project:
182
- rubygems_version: 2.6.8
177
+ rubygems_version: 2.6.12
183
178
  signing_key:
184
179
  specification_version: 4
185
180
  summary: Fluentd plugin to store data on Google BigQuery