influxdb-client 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +157 -0
  3. data/.circleci/setup-rubygems.sh +3 -0
  4. data/.codecov.yml +3 -0
  5. data/.github/PULL_REQUEST_TEMPLATE +8 -0
  6. data/.gitignore +16 -0
  7. data/.rubocop.yml +38 -0
  8. data/CHANGELOG.md +27 -0
  9. data/Gemfile +24 -0
  10. data/LICENSE +21 -0
  11. data/README.md +248 -0
  12. data/Rakefile +37 -0
  13. data/bin/generate-sources.sh +23 -0
  14. data/bin/influxdb-onboarding.sh +39 -0
  15. data/bin/influxdb-restart.sh +60 -0
  16. data/bin/pom.xml +34 -0
  17. data/bin/swagger.yml +9867 -0
  18. data/influxdb-client.gemspec +53 -0
  19. data/lib/influxdb2/client.rb +29 -0
  20. data/lib/influxdb2/client/client.rb +89 -0
  21. data/lib/influxdb2/client/default_api.rb +87 -0
  22. data/lib/influxdb2/client/delete_api.rb +80 -0
  23. data/lib/influxdb2/client/flux_csv_parser.rb +251 -0
  24. data/lib/influxdb2/client/flux_table.rb +99 -0
  25. data/lib/influxdb2/client/influx_error.rb +31 -0
  26. data/lib/influxdb2/client/models/delete_predicate_request.rb +215 -0
  27. data/lib/influxdb2/client/models/dialect.rb +317 -0
  28. data/lib/influxdb2/client/models/query.rb +284 -0
  29. data/lib/influxdb2/client/point.rb +215 -0
  30. data/lib/influxdb2/client/query_api.rb +93 -0
  31. data/lib/influxdb2/client/version.rb +23 -0
  32. data/lib/influxdb2/client/worker.rb +115 -0
  33. data/lib/influxdb2/client/write_api.rb +243 -0
  34. data/test/influxdb/client_test.rb +70 -0
  35. data/test/influxdb/delete_api_integration_test.rb +100 -0
  36. data/test/influxdb/delete_api_test.rb +121 -0
  37. data/test/influxdb/flux_csv_parser_test.rb +401 -0
  38. data/test/influxdb/point_test.rb +221 -0
  39. data/test/influxdb/query_api_integration_test.rb +58 -0
  40. data/test/influxdb/query_api_stream_test.rb +98 -0
  41. data/test/influxdb/query_api_test.rb +96 -0
  42. data/test/influxdb/write_api_batching_test.rb +292 -0
  43. data/test/influxdb/write_api_integration_test.rb +76 -0
  44. data/test/influxdb/write_api_test.rb +275 -0
  45. data/test/test_helper.rb +39 -0
  46. metadata +214 -0
@@ -0,0 +1,70 @@
1
+ # The MIT License
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'test_helper'
22
+
23
+ class ClientTest < Minitest::Test
24
+ def test_defined_version_number
25
+ refute_nil ::InfluxDB2::VERSION
26
+ end
27
+
28
+ def test_client_new
29
+ refute_nil InfluxDB2::Client.new('http://localhost:9999', 'my-token')
30
+ end
31
+
32
+ def test_client_hash
33
+ client1 = InfluxDB2::Client.new('http://localhost:9999', 'my-token')
34
+ client2 = InfluxDB2::Client.new('http://localhost:9999', 'my-token-diff')
35
+
36
+ refute_equal client1.hash, client2.hash
37
+ assert_equal client1.hash, client1.hash
38
+ end
39
+
40
+ def test_client_eq
41
+ client1 = InfluxDB2::Client.new('http://localhost:9999', 'my-token')
42
+ client2 = InfluxDB2::Client.new('http://localhost:9999', 'my-token-diff')
43
+
44
+ refute_equal client1, client2
45
+ assert_equal client1, client1
46
+ end
47
+
48
+ def test_client_options
49
+ client = InfluxDB2::Client.new('http://localhost:9999', 'my-token')
50
+
51
+ assert_equal 'http://localhost:9999', client.options[:url]
52
+ assert_equal 'my-token', client.options[:token]
53
+ end
54
+
55
+ def test_close
56
+ client = InfluxDB2::Client.new('http://localhost:9999', 'my-token')
57
+
58
+ assert_equal true, client.close!
59
+ assert_equal true, client.close!
60
+ end
61
+
62
+ def test_get_write_api
63
+ client = InfluxDB2::Client.new('http://localhost:9999', 'my-token')
64
+
65
+ write_api = client.create_write_api
66
+
67
+ refute_nil write_api
68
+ assert_instance_of InfluxDB2::WriteApi, write_api
69
+ end
70
+ end
@@ -0,0 +1,100 @@
1
+ # The MIT License
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'test_helper'
22
+
23
+ class DeleteApiIntegrationTest < MiniTest::Test
24
+ def setup
25
+ WebMock.allow_net_connect!
26
+
27
+ @client = InfluxDB2::Client.new('http://localhost:9999', 'my-token',
28
+ bucket: 'my-bucket',
29
+ org: 'my-org',
30
+ precision: InfluxDB2::WritePrecision::NANOSECOND,
31
+ use_ssl: false)
32
+
33
+ now = Time.now.utc
34
+ @measurement = 'h2o_delete_' + now.to_i.to_s + +now.nsec.to_s
35
+
36
+ data = [InfluxDB2::Point.new(name: @measurement)
37
+ .add_tag('location', 'europe')
38
+ .add_field('level', 2)
39
+ .time(Time.utc(2015, 10, 15, 8, 20, 15), InfluxDB2::WritePrecision::MILLISECOND),
40
+ InfluxDB2::Point.new(name: @measurement)
41
+ .add_tag('location', 'us')
42
+ .add_field('level', 2)
43
+ .time(Time.utc(2016, 10, 15, 8, 20, 15), InfluxDB2::WritePrecision::MILLISECOND),
44
+ InfluxDB2::Point.new(name: @measurement)
45
+ .add_tag('location', 'india')
46
+ .add_field('level', 2)
47
+ .time(Time.utc(2017, 10, 15, 8, 20, 15), InfluxDB2::WritePrecision::MILLISECOND),
48
+ InfluxDB2::Point.new(name: @measurement)
49
+ .add_tag('location', 'europe')
50
+ .add_field('level', 2)
51
+ .time(Time.utc(2018, 10, 15, 8, 20, 15), InfluxDB2::WritePrecision::MILLISECOND)]
52
+
53
+ @client.create_write_api.write(data: data, precision: InfluxDB2::WritePrecision::MILLISECOND)
54
+
55
+ assert_equal 4, _query_count
56
+ end
57
+
58
+ def test_delete
59
+ @client.create_delete_api.delete(Time.utc(2015, 10, 16, 8, 20, 15), Time.utc(2020, 10, 16, 8, 20, 15),
60
+ predicate: 'location="europe"')
61
+
62
+ assert_equal 3, _query_count
63
+ end
64
+
65
+ def test_delete_without_predicate
66
+ @client.create_delete_api.delete(Time.utc(2016, 10, 15, 7, 20, 15), Time.utc(2018, 10, 14, 8, 20, 15))
67
+
68
+ assert_equal 2, _query_count
69
+ end
70
+
71
+ def test_delete_all
72
+ @client.create_delete_api.delete(Time.utc(2010, 10, 15, 7, 20, 15), Time.utc(2020, 10, 14, 8, 20, 15))
73
+
74
+ assert_equal 0, _query_count
75
+ end
76
+
77
+ def test_delete_without_interval
78
+ error = assert_raises InfluxDB2::InfluxError do
79
+ @client.create_delete_api.delete(nil, nil)
80
+ end
81
+
82
+ assert error.message.include?('invalid request'),
83
+ "Error message: '#{error.message}' doesn't contains 'invalid request'"
84
+ end
85
+
86
+ private
87
+
88
+ def _query_count
89
+ query = 'from(bucket: "my-bucket") |> range(start: 0) |> ' \
90
+ "filter(fn: (r) => r._measurement == \"#{@measurement}\") " \
91
+ '|> drop(columns: ["location"]) |> count()'
92
+
93
+ table = @client.create_query_api.query(query: InfluxDB2::Query.new(query: query,
94
+ dialect: InfluxDB2::QueryApi::DEFAULT_DIALECT,
95
+ type: nil))[0]
96
+ return 0 if table.nil?
97
+
98
+ table.records[0].value
99
+ end
100
+ end
@@ -0,0 +1,121 @@
1
+ # The MIT License
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'test_helper'
22
+
23
+ class DeleteApiTest < MiniTest::Test
24
+ def setup
25
+ WebMock.disable_net_connect!
26
+ end
27
+
28
+ def test_delete
29
+ stub_request(:any, 'http://localhost:9999/api/v2/delete?bucket=my-bucket&org=my-org')
30
+ .to_return(status: 204)
31
+ client = InfluxDB2::Client.new('http://localhost:9999', 'my-token',
32
+ bucket: 'my-bucket',
33
+ org: 'my-org',
34
+ precision: InfluxDB2::WritePrecision::NANOSECOND,
35
+ use_ssl: false)
36
+
37
+ client.create_delete_api.delete(Time.utc(2019, 10, 15, 8, 20, 15), Time.utc(2019, 11, 15, 8, 20, 15),
38
+ predicate: 'key1="value1" AND key2="value"', bucket: 'my-bucket', org: 'my-org')
39
+
40
+ body = '{"start":"2019-10-15T08:20:15+00:00","stop":"2019-11-15T08:20:15+00:00","predicate":"key1=\"value1\" ' \
41
+ 'AND key2=\"value\""}'
42
+
43
+ assert_requested(:post, 'http://localhost:9999/api/v2/delete?bucket=my-bucket&org=my-org', times: 1, body: body)
44
+ end
45
+
46
+ def test_delete_time_as_date_time
47
+ stub_request(:any, 'http://localhost:9999/api/v2/delete?bucket=my-bucket&org=my-org')
48
+ .to_return(status: 204)
49
+ client = InfluxDB2::Client.new('http://localhost:9999', 'my-token',
50
+ bucket: 'my-bucket',
51
+ org: 'my-org',
52
+ precision: InfluxDB2::WritePrecision::NANOSECOND,
53
+ use_ssl: false)
54
+
55
+ client.create_delete_api.delete(DateTime.rfc3339('2019-02-03T04:05:06+07:00'),
56
+ DateTime.rfc3339('2019-03-03T04:05:06+07:00'),
57
+ predicate: 'key1="value1" AND key2="value"', bucket: 'my-bucket', org: 'my-org')
58
+
59
+ body = '{"start":"2019-02-03T04:05:06+07:00","stop":"2019-03-03T04:05:06+07:00","predicate":"key1=\"value1\" ' \
60
+ 'AND key2=\"value\""}'
61
+
62
+ assert_requested(:post, 'http://localhost:9999/api/v2/delete?bucket=my-bucket&org=my-org', times: 1, body: body)
63
+ end
64
+
65
+ def test_delete_time_as_string
66
+ stub_request(:any, 'http://localhost:9999/api/v2/delete?bucket=my-bucket&org=my-org')
67
+ .to_return(status: 204)
68
+ client = InfluxDB2::Client.new('http://localhost:9999', 'my-token',
69
+ bucket: 'my-bucket',
70
+ org: 'my-org',
71
+ precision: InfluxDB2::WritePrecision::NANOSECOND,
72
+ use_ssl: false)
73
+
74
+ client.create_delete_api.delete('2019-02-03T04:05:06+07:00', '2019-04-03T04:05:06+07:00',
75
+ predicate: 'key1="value1" AND key2="value"', bucket: 'my-bucket', org: 'my-org')
76
+
77
+ body = '{"start":"2019-02-03T04:05:06+07:00","stop":"2019-04-03T04:05:06+07:00","predicate":"key1=\"value1\" ' \
78
+ 'AND key2=\"value\""}'
79
+
80
+ assert_requested(:post, 'http://localhost:9999/api/v2/delete?bucket=my-bucket&org=my-org', times: 1, body: body)
81
+ end
82
+
83
+ def test_without_predicate
84
+ stub_request(:any, 'http://localhost:9999/api/v2/delete?bucket=my-bucket&org=my-org')
85
+ .to_return(status: 204)
86
+ client = InfluxDB2::Client.new('http://localhost:9999', 'my-token',
87
+ bucket: 'my-bucket',
88
+ org: 'my-org',
89
+ precision: InfluxDB2::WritePrecision::NANOSECOND,
90
+ use_ssl: false)
91
+
92
+ client.create_delete_api.delete('2019-02-03T04:05:06+07:00', '2019-04-03T04:05:06+07:00',
93
+ bucket: 'my-bucket', org: 'my-org')
94
+
95
+ body = '{"start":"2019-02-03T04:05:06+07:00","stop":"2019-04-03T04:05:06+07:00"}'
96
+
97
+ assert_requested(:post, 'http://localhost:9999/api/v2/delete?bucket=my-bucket&org=my-org', times: 1, body: body)
98
+ end
99
+
100
+ def test_user_agent_header
101
+ stub_request(:any, 'http://localhost:9999/api/v2/delete?bucket=my-bucket&org=my-org')
102
+ .to_return(status: 204)
103
+ client = InfluxDB2::Client.new('http://localhost:9999', 'my-token',
104
+ bucket: 'my-bucket',
105
+ org: 'my-org',
106
+ precision: InfluxDB2::WritePrecision::NANOSECOND,
107
+ use_ssl: false)
108
+
109
+ client.create_delete_api.delete('2019-02-03T04:05:06+07:00', '2019-04-03T04:05:06+07:00',
110
+ bucket: 'my-bucket', org: 'my-org')
111
+
112
+ body = '{"start":"2019-02-03T04:05:06+07:00","stop":"2019-04-03T04:05:06+07:00"}'
113
+ headers = {
114
+ 'Authorization' => 'Token my-token',
115
+ 'User-Agent' => "influxdb-client-ruby/#{InfluxDB2::VERSION}",
116
+ 'Content-Type' => 'application/json'
117
+ }
118
+ assert_requested(:post, 'http://localhost:9999/api/v2/delete?bucket=my-bucket&org=my-org',
119
+ times: 1, body: body, headers: headers)
120
+ end
121
+ end
@@ -0,0 +1,401 @@
1
+ # The MIT License
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'test_helper'
22
+
23
+ class FluxCsvParserTest < MiniTest::Test
24
+ def test_multiple_values
25
+ data = "#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,string,string,string,string,long,long,string\n" \
26
+ "#group,false,false,true,true,true,true,true,true,false,false,false\n" \
27
+ "#default,_result,,,,,,,,,,\n" \
28
+ ",result,table,_start,_stop,_field,_measurement,host,region,_value2,value1,value_str\n" \
29
+ ",,0,1677-09-21T00:12:43.145224192Z,2018-07-16T11:21:02.547596934Z,free,mem,A,west,121,11,test\n" \
30
+ ",,1,1677-09-21T00:12:43.145224192Z,2018-07-16T11:21:02.547596934Z,free,mem,B,west,484,22,test\n" \
31
+ ",,2,1677-09-21T00:12:43.145224192Z,2018-07-16T11:21:02.547596934Z,usage_system,cpu,A,west,1444,38,test\n" \
32
+ ',,3,1677-09-21T00:12:43.145224192Z,2018-07-16T11:21:02.547596934Z,user_usage,cpu,A,west,2401,49,test'
33
+
34
+ tables = InfluxDB2::FluxCsvParser.new(data).parse.tables
35
+
36
+ column_headers = tables[0].columns
37
+ assert_equal 11, column_headers.size
38
+
39
+ values = [false, false, true, true, true, true, true, true, false, false, false]
40
+ _assert_columns(column_headers, values: values)
41
+ assert_equal 4, tables.size
42
+
43
+ _assert_multiple_record(tables)
44
+ end
45
+
46
+ def test_parse_shortcut
47
+ data = '#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,' \
48
+ "dateTime:RFC3339,long,string,string,string,boolean\n" \
49
+ "#group,false,false,false,false,false,false,false,false,false,true\n" \
50
+ "#default,_result,,,,,,,,,true\n" \
51
+ ",result,table,_start,_stop,_time,_value,_field,_measurement,host,value\n" \
52
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,true\n"
53
+
54
+ tables = InfluxDB2::FluxCsvParser.new(data).parse.tables
55
+
56
+ assert_equal 1, tables.size
57
+ assert_equal 1, tables[0].records.size
58
+
59
+ record = tables[0].records[0]
60
+
61
+ assert_equal _parse_time('1970-01-01T00:00:10Z'), record.start
62
+ assert_equal _parse_time('1970-01-01T00:00:20Z'), record.stop
63
+ assert_equal _parse_time('1970-01-01T00:00:10Z'), record.time
64
+ assert_equal 10, record.value
65
+ assert_equal 'free', record.field
66
+ assert_equal 'mem', record.measurement
67
+ end
68
+
69
+ def test_mapping_boolean
70
+ data = '#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,' \
71
+ "dateTime:RFC3339,long,string,string,string,boolean\n" \
72
+ "#group,false,false,false,false,false,false,false,false,false,true\n" \
73
+ "#default,_result,,,,,,,,,true\n" \
74
+ ",result,table,_start,_stop,_time,_value,_field,_measurement,host,value\n" \
75
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,true\n" \
76
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,false\n" \
77
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,x\n" \
78
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,\n"
79
+
80
+ tables = InfluxDB2::FluxCsvParser.new(data).parse.tables
81
+ records = tables[0].records
82
+
83
+ assert_equal true, records[0].values['value']
84
+ assert_equal false, records[1].values['value']
85
+ assert_equal false, records[2].values['value']
86
+ assert_equal true, records[3].values['value']
87
+ end
88
+
89
+ def test_mapping_unsigned_long
90
+ data = '#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,' \
91
+ "dateTime:RFC3339,long,string,string,string,unsignedLong\n" \
92
+ "#group,false,false,false,false,false,false,false,false,false,true\n" \
93
+ "#default,_result,,,,,,,,,\n" \
94
+ ",result,table,_start,_stop,_time,_value,_field,_measurement,host,value\n" \
95
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,17916881237904312345\n" \
96
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,\n"
97
+
98
+ expected = 17_916_881_237_904_312_345
99
+
100
+ tables = InfluxDB2::FluxCsvParser.new(data).parse.tables
101
+ records = tables[0].records
102
+
103
+ assert_equal expected, records[0].values['value']
104
+ assert_nil records[1].values['value']
105
+ end
106
+
107
+ def test_mapping_double
108
+ data = '#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,' \
109
+ "dateTime:RFC3339,long,string,string,string,double\n" \
110
+ "#group,false,false,false,false,false,false,false,false,false,true\n" \
111
+ "#default,_result,,,,,,,,,\n" \
112
+ ",result,table,_start,_stop,_time,_value,_field,_measurement,host,value\n" \
113
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,12.25\n" \
114
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,\n" \
115
+
116
+ tables = InfluxDB2::FluxCsvParser.new(data).parse.tables
117
+ records = tables[0].records
118
+
119
+ assert_equal 12.25, records[0].values['value']
120
+ assert_nil records[1].values['value']
121
+ end
122
+
123
+ def test_mapping_base64_binary
124
+ binary_data = 'test value'
125
+ encoded_data = Base64.encode64(binary_data)
126
+
127
+ data = '#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,' \
128
+ "dateTime:RFC3339,long,string,string,string,base64Binary\n" \
129
+ "#group,false,false,false,false,false,false,false,false,false,true\n" \
130
+ "#default,_result,,,,,,,,,\n" \
131
+ ",result,table,_start,_stop,_time,_value,_field,_measurement,host,value\n" \
132
+ ',,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,' + encoded_data + "\n" \
133
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,\n"
134
+
135
+ tables = InfluxDB2::FluxCsvParser.new(data).parse.tables
136
+ records = tables[0].records
137
+
138
+ value = records[0].values['value']
139
+
140
+ assert !value.nil?
141
+ assert_equal binary_data, value
142
+
143
+ assert_nil records[1].values['value']
144
+ end
145
+
146
+ def test_mapping_rfc3339
147
+ data = '#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,' \
148
+ "dateTime:RFC3339,long,string,string,string,dateTime:RFC3339\n" \
149
+ "#group,false,false,false,false,false,false,false,false,false,true\n" \
150
+ "#default,_result,,,,,,,,,\n" \
151
+ ",result,table,_start,_stop,_time,_value,_field,_measurement,host,value\n" \
152
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,1970-01-01T00:00:10Z\n" \
153
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,\n"
154
+
155
+ tables = InfluxDB2::FluxCsvParser.new(data).parse.tables
156
+ records = tables[0].records
157
+
158
+ assert_equal Time.parse('1970-01-01T00:00:10Z').to_datetime.rfc3339, records[0].values['value']
159
+ assert_nil records[1].values['value']
160
+ end
161
+
162
+ def test_mapping_duration
163
+ data = '#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339' \
164
+ ",dateTime:RFC3339,long,string,string,string,duration\n" \
165
+ "#group,false,false,false,false,false,false,false,false,false,true\n" \
166
+ "#default,_result,,,,,,,,,\n" \
167
+ ",result,table,_start,_stop,_time,_value,_field,_measurement,host,value\n" \
168
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,125\n" \
169
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,\n"
170
+
171
+ tables = InfluxDB2::FluxCsvParser.new(data).parse.tables
172
+ records = tables[0].records
173
+
174
+ assert_equal 125, records[0].values['value']
175
+ assert_nil records[1].values['value']
176
+ end
177
+
178
+ def test_group_key
179
+ data = '#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,' \
180
+ "dateTime:RFC3339,long,string,string,string,duration\n" \
181
+ "#group,false,false,false,false,true,false,false,false,false,true\n" \
182
+ "#default,_result,,,,,,,,,\n" \
183
+ ",result,table,_start,_stop,_time,_value,_field,_measurement,host,value\n" \
184
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,125\n" \
185
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,\n" \
186
+
187
+ tables = InfluxDB2::FluxCsvParser.new(data).parse.tables
188
+
189
+ assert_equal 10, tables[0].columns.size
190
+ assert_equal 2, tables[0].group_key.size
191
+ end
192
+
193
+ def test_unknown_type_as_string
194
+ data = '#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,' \
195
+ "dateTime:RFC3339,long,string,string,string,unknown\n" \
196
+ "#group,false,false,false,false,false,false,false,false,false,true\n" \
197
+ "#default,_result,,,,,,,,,\n" \
198
+ ",result,table,_start,_stop,_time,_value,_field,_measurement,host,value\n" \
199
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,12.25\n" \
200
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,\n"
201
+
202
+ tables = InfluxDB2::FluxCsvParser.new(data).parse.tables
203
+ records = tables[0].records
204
+
205
+ assert_equal '12.25', records[0].values['value']
206
+ assert_nil records[1].values['value']
207
+ end
208
+
209
+ private
210
+
211
+ def _parse_time(time)
212
+ Time.parse(time).to_datetime.rfc3339
213
+ end
214
+
215
+ def _assert_record(flux_record, values: nil, size: 0, value: nil)
216
+ values.keys.each do |key|
217
+ assert_equal values[key], flux_record.values[key]
218
+ end
219
+
220
+ if value.nil?
221
+ assert_nil value
222
+ else
223
+ assert_equal value, flux_record.value
224
+ end
225
+
226
+ assert_equal size, flux_record.values.size
227
+ end
228
+
229
+ def _assert_columns(column_headers, values: nil)
230
+ i = 0
231
+ values.each do |value|
232
+ assert_equal value, column_headers[i].group
233
+ i += 1
234
+ end
235
+ end
236
+
237
+ def _assert_multiple_record(tables)
238
+ # Record 1
239
+ table_records = tables[0].records
240
+ assert_equal 1, table_records.size
241
+
242
+ values = { 'table' => 0, 'host' => 'A', 'region' => 'west', 'value1' => 11, '_value2' => 121,
243
+ 'value_str' => 'test' }
244
+
245
+ _assert_record(table_records[0], values: values, size: 11)
246
+
247
+ # Record 2
248
+ table_records = tables[1].records
249
+ assert_equal 1, table_records.size
250
+
251
+ values = { 'table' => 1, 'host' => 'B', 'region' => 'west', 'value1' => 22, '_value2' => 484,
252
+ 'value_str' => 'test' }
253
+
254
+ _assert_record(table_records[0], values: values, size: 11)
255
+
256
+ # Record 3
257
+ table_records = tables[2].records
258
+ assert_equal 1, table_records.size
259
+
260
+ values = { 'table' => 2, 'host' => 'A', 'region' => 'west', 'value1' => 38, '_value2' => 1444,
261
+ 'value_str' => 'test' }
262
+
263
+ _assert_record(table_records[0], values: values, size: 11)
264
+
265
+ # Record 4
266
+ table_records = tables[3].records
267
+ assert_equal 1, table_records.size
268
+
269
+ values = { 'table' => 3, 'host' => 'A', 'region' => 'west', 'value1' => 49, '_value2' => 2401,
270
+ 'value_str' => 'test' }
271
+
272
+ _assert_record(table_records[0], values: values, size: 11)
273
+ end
274
+ end
275
+
276
+ class FluxCsvParserErrorTest < MiniTest::Test
277
+ def test_error
278
+ data = "#datatype,string,string\n" \
279
+ "#group,true,true\n" \
280
+ "#default,,\n" \
281
+ ",error,reference\n" \
282
+ ',failed to create physical plan: invalid time bounds from procedure from: bounds contain zero time,897'
283
+
284
+ parser = InfluxDB2::FluxCsvParser.new(data)
285
+
286
+ error = assert_raises InfluxDB2::FluxQueryError do
287
+ parser.parse
288
+ end
289
+
290
+ assert_equal 'failed to create physical plan: invalid time bounds from procedure from: bounds contain zero time',
291
+ error.message
292
+ assert_equal 897, error.reference
293
+ end
294
+
295
+ def test_error_without_reference
296
+ data = "#datatype,string,string\n" \
297
+ "#group,true,true\n" \
298
+ "#default,,\n" \
299
+ ",error,reference\n" \
300
+ ',failed to create physical plan: invalid time bounds from procedure from: bounds contain zero time,'
301
+
302
+ parser = InfluxDB2::FluxCsvParser.new(data)
303
+
304
+ error = assert_raises InfluxDB2::FluxQueryError do
305
+ parser.parse
306
+ end
307
+
308
+ assert_equal 'failed to create physical plan: invalid time bounds from procedure from: bounds contain zero time',
309
+ error.message
310
+ assert_equal 0, error.reference
311
+ end
312
+
313
+ def test_without_table_definition
314
+ data = ",result,table,_start,_stop,_time,_value,_field,_measurement,host,value\n" \
315
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,12.25\n" \
316
+ ",,0,1970-01-01T00:00:10Z,1970-01-01T00:00:20Z,1970-01-01T00:00:10Z,10,free,mem,A,\n"
317
+
318
+ parser = InfluxDB2::FluxCsvParser.new(data)
319
+
320
+ error = assert_raises InfluxDB2::FluxCsvParserError do
321
+ parser.parse
322
+ end
323
+
324
+ assert_equal 'Unable to parse CSV response. FluxTable definition was not found.', error.message
325
+ end
326
+
327
+ def test_multiple_queries
328
+ data = "#datatype,string,long,string,string,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string\n" \
329
+ "#group,false,false,true,true,true,true,false,false,true\n" \
330
+ "#default,t1,,,,,,,,\n" \
331
+ ",result,table,_field,_measurement,_start,_stop,_time,_value,tag\n" \
332
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:20:00Z,2,test1\n" \
333
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:21:40Z,2,test1\n" \
334
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:23:20Z,2,test1\n" \
335
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:25:00Z,2,test1\n" \
336
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:26:40Z,2,test1\n" \
337
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:28:20Z,2,test1\n" \
338
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:30:00Z,2,test1\n" \
339
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:20:00Z,2,test2\n" \
340
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:21:40Z,2,test2\n" \
341
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:23:20Z,2,test2\n" \
342
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:25:00Z,2,test2\n" \
343
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:26:40Z,2,test2\n" \
344
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:28:20Z,2,test2\n" \
345
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:30:00Z,2,test2\n" \
346
+ "\n" \
347
+ "#datatype,string,long,string,string,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string\n" \
348
+ "#group,false,false,true,true,true,true,false,false,true\n" \
349
+ "#default,t2,,,,,,,,\n" \
350
+ ",result,table,_field,_measurement,_start,_stop,_time,_value,tag\n" \
351
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:20:00Z,2,test1\n" \
352
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:21:40Z,2,test1\n" \
353
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:23:20Z,2,test1\n" \
354
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:25:00Z,2,test1\n" \
355
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:26:40Z,2,test1\n" \
356
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:28:20Z,2,test1\n" \
357
+ ",,0,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:30:00Z,2,test1\n" \
358
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:20:00Z,2,test2\n" \
359
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:21:40Z,2,test2\n" \
360
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:23:20Z,2,test2\n" \
361
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:25:00Z,2,test2\n" \
362
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:26:40Z,2,test2\n" \
363
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:28:20Z,2,test2\n" \
364
+ ',,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:30:00Z,2,test2'
365
+
366
+ tables = InfluxDB2::FluxCsvParser.new(data).parse.tables
367
+
368
+ assert_equal 4, tables.size
369
+ assert_equal 7, tables[0].records.size
370
+ assert_equal 7, tables[1].records.size
371
+ assert_equal 7, tables[2].records.size
372
+ assert_equal 7, tables[3].records.size
373
+ end
374
+
375
+ def test_table_not_start_at_zero
376
+ data = "#datatype,string,long,string,string,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string\n" \
377
+ "#group,false,false,true,true,true,true,false,false,true\n" \
378
+ "#default,t1,,,,,,,,\n" \
379
+ ",result,table,_field,_measurement,_start,_stop,_time,_value,tag\n" \
380
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:20:00Z,2,test1\n" \
381
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:21:40Z,2,test1\n" \
382
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:23:20Z,2,test1\n" \
383
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:25:00Z,2,test1\n" \
384
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:26:40Z,2,test1\n" \
385
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:28:20Z,2,test1\n" \
386
+ ",,1,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:30:00Z,2,test1\n" \
387
+ ",,2,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:20:00Z,2,test2\n" \
388
+ ",,2,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:21:40Z,2,test2\n" \
389
+ ",,2,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:23:20Z,2,test2\n" \
390
+ ",,2,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:25:00Z,2,test2\n" \
391
+ ",,2,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:26:40Z,2,test2\n" \
392
+ ",,2,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:28:20Z,2,test2\n" \
393
+ ',,2,value,pct,2010-02-27T04:48:32.752600083Z,2020-02-27T16:48:32.752600083Z,2020-02-27T16:30:00Z,2,test2\n'
394
+
395
+ tables = InfluxDB2::FluxCsvParser.new(data).parse.tables
396
+
397
+ assert_equal 2, tables.size
398
+ assert_equal 7, tables[0].records.size
399
+ assert_equal 7, tables[1].records.size
400
+ end
401
+ end