influxparser 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8a35192cf489f487d8a42343384e1528d5168047
4
+ data.tar.gz: fa965f4209d17c97bf4bde7508c5cc9fe07cfcd0
5
+ SHA512:
6
+ metadata.gz: 870efd15c7c9df408b15a2da2dc11134c6c2bd45ff69bdd213b30a3913fb402b0d992626c42bfee7a944dbbfe4624a922baffe6e0c6d5c2cb995170c9a6c74d5
7
+ data.tar.gz: 194488b6bcb2ed5ce444c603789c6db5ce8b321f2ed15b70567f19b35afd7a0fb5e15cc78be828bf99d30809f0f23ea845a10d5b9da5d0949fe53db4ac43eae6
@@ -0,0 +1,88 @@
1
+ # TODO: the tags hash should be absent when there are no tags
2
+ # TODO: optional timestamp parsing
3
+ # TODO: time key shouldn't exist if there is no time
4
+ # TODO: deal with improper line protocol
5
+ class InfluxParser
6
+ class << self
7
+ def parse_point(s, options = {})
8
+ default_options = {:parse_types => true}
9
+ options = default_options.merge(options)
10
+
11
+ point = {}
12
+ point['_raw'] = s
13
+ s = s.strip # trim whitespace
14
+
15
+ measurement_end = s.index /(?<!\\) /
16
+
17
+
18
+ mparts = s[0..measurement_end-1].split(/(?<!\\),/) # split on unescaped commas for the measurement name and tags
19
+ point['measurement'] = unescape_measurement mparts[0]
20
+
21
+ # if any tags were attached to the measurement iterate over them now
22
+ point['tags'] = {}
23
+ mparts.drop(1).each do |t|
24
+ tag = t.split(/(?<!\\)=/)
25
+ point['tags'][unescape_tag tag[0]] = unescape_tag tag[1]
26
+ end
27
+
28
+ # now iterate over the values
29
+ last_value_raw = ''
30
+ last_key = ''
31
+ point['values'] = {}
32
+ vparts = s[measurement_end+1..-1].split(/(?<!\\),/)
33
+ # puts "vparts:#{vparts}"
34
+ vparts.each do |v|
35
+ value = v.split(/(?<!\\)=/)
36
+ last_value_raw = value[1]
37
+ last_key = unescape_tag value[0]
38
+ # puts "last k/v #{last_key}==#{last_value_raw}"
39
+ point['values'][last_key] = unescape_point(value[1],options)
40
+ end
41
+ # puts "-----\n#{point['values'].to_yaml}\n"
42
+ # check for a timestamp in the last value
43
+ # TODO: I hate this, but it's late and I just want to move past it for now
44
+ # TODO: what happens if the last character of the last value is an escaped quote?
45
+ has_space = last_value_raw.rindex(/ /)
46
+ if has_space
47
+ time_stamp = last_value_raw[has_space+1..-1] # take everything from the space to the end
48
+ if time_stamp.index(/"/)
49
+ point['time'] = nil
50
+ else
51
+ # it was a timestamp, strip it from the last value and set the timestamp
52
+ point['values'][last_key] = unescape_point(last_value_raw[0..has_space-1],options)
53
+ point['time'] = time_stamp
54
+ end
55
+ else
56
+ point['time'] = nil
57
+ end
58
+
59
+ point
60
+ end
61
+ def unescape_measurement(s)
62
+ s.gsub(/\\ /,' ').gsub(/\\,/,',')
63
+ end
64
+ def unescape_tag(s)
65
+ t = unescape_measurement s
66
+ t.gsub(/\\=/,'=')
67
+ end
68
+ def unescape_point(s,options)
69
+ # puts "unescape:#{s}"
70
+ # s = s.gsub(/\\\\/,'\\').gsub(/\\"/,'""') # handle escaped characters if present
71
+
72
+ # it is a string, return it
73
+ return s[1..-2].gsub(/\\\\/,'\\').gsub(/\\"/,'""') if s[0,1] == '"'
74
+
75
+ return s.sub(/(?<=\d)i/,'') if (!options[:parse_types]) # the customer doesn't care about types so just return it, but strip the trailing i from an integer because we care
76
+
77
+ # handle the booleans
78
+ return true if ['t','T','true','True','TRUE'].include?(s)
79
+ return false if ['f','F','false','False','FALSE'].include?(s)
80
+
81
+ # by here we have either an unquoted string or some numeric
82
+
83
+ return s.to_f if s =~ /^(\d|\.)+$/ # floats are only digits and dots
84
+ return s.chomp('i').to_i if s[0..-2] =~ /^(\d)+$/ # trailing i is an integer remove it
85
+ return s.gsub(/\\\\/,'\\').gsub(/\\"/,'"')
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,281 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
4
+ require 'test/unit'
5
+ require 'influxparser'
6
+ class TestParsePointFromDocs < Test::Unit::TestCase # def setup
7
+ # end
8
+
9
+
10
+ # def teardown
11
+ # end
12
+
13
+ def test_two_tags
14
+ point = InfluxParser.parse_point('weather,location=us-midwest,season=summer temperature=82 1465839830100400200')
15
+ assert_not_equal(false,point) # a straight up parse error will false
16
+
17
+ # measurement
18
+ assert_equal('weather',point['measurement'])
19
+
20
+ # tags
21
+ assert_equal(2,point['tags'].length)
22
+
23
+ # check location
24
+ assert_equal(true,point['tags'].key?('location'))
25
+ assert_equal('us-midwest',point['tags']['location'])
26
+
27
+ # check season
28
+ assert_equal(true,point['tags'].key?('season'))
29
+ assert_equal('summer',point['tags']['season'])
30
+
31
+ # value
32
+ assert_equal(true,point['values'].key?('temperature'))
33
+ assert_equal(82,point['values']['temperature'])
34
+
35
+ # time
36
+ assert_equal('1465839830100400200',point['time'])
37
+
38
+ end
39
+
40
+ def test_no_tags
41
+ point = InfluxParser.parse_point('weather temperature=82 1465839830100400200')
42
+ assert_not_equal(false,point) # a straight up parse error will false
43
+
44
+ # measurement
45
+ assert_equal('weather',point['measurement'])
46
+
47
+ # no tags
48
+ assert_equal(true,point.key?('tags'))
49
+ assert_equal(0,point['tags'].length)
50
+
51
+ # value
52
+ assert_equal(true,point['values'].key?('temperature'))
53
+ assert_equal(82,point['values']['temperature'])
54
+
55
+ # time
56
+ assert_equal('1465839830100400200',point['time'])
57
+
58
+ end
59
+
60
+ def test_two_values
61
+ point = InfluxParser.parse_point('weather,location=us-midwest temperature=82,humidity=71 1465839830100400200')
62
+ assert_not_equal(false,point) # a straight up parse error will false
63
+
64
+ # measurement
65
+ assert_equal('weather',point['measurement'])
66
+
67
+ # check location
68
+ assert_equal(true,point['tags'].key?('location'))
69
+ assert_equal('us-midwest',point['tags']['location'])
70
+
71
+ # values
72
+ assert_equal(2,point['values'].length)
73
+
74
+ assert_equal(true,point['values'].key?('temperature'))
75
+ assert_equal(82,point['values']['temperature'])
76
+
77
+ assert_equal(true,point['values'].key?('humidity'))
78
+ assert_equal(71,point['values']['humidity'])
79
+
80
+ # time
81
+ assert_equal('1465839830100400200',point['time'])
82
+
83
+ end
84
+ def test_timestamp
85
+ point = InfluxParser.parse_point('weather,location=us-midwest temperature=82')
86
+ assert_not_equal(false,point) # a straight up parse error will false
87
+ assert_nil(point['time'])
88
+ end
89
+
90
+ def test_float
91
+ point = InfluxParser.parse_point('weather,location=us-midwest temperature=82 1465839830100400200')
92
+ assert_not_equal(false,point) # a straight up parse error will false
93
+ assert_equal(82.0,point['values']['temperature'])
94
+ end
95
+ def test_integer
96
+ point = InfluxParser.parse_point('weather,location=us-midwest temperature=82i 1465839830100400200')
97
+ assert_not_equal(false,point) # a straight up parse error will false
98
+ assert_equal(82,point['values']['temperature'])
99
+ end
100
+
101
+ def test_string
102
+ point = InfluxParser.parse_point('weather,location=us-midwest temperature="too warm" 1465839830100400200')
103
+ assert_not_equal(false,point) # a straight up parse error will false
104
+ assert_equal('too warm',point['values']['temperature'])
105
+ end
106
+
107
+ def test_invalid_time
108
+ # TODO: this should be throwing a parse error
109
+ point = InfluxParser.parse_point('weather,location=us-midwest temperature=82 "1465839830100400200"')
110
+ assert_not_equal(false,point) # a straight up parse error will false
111
+
112
+ end
113
+
114
+ def test_invalid_string
115
+ # TODO: this should be throwing a parse error
116
+ point = InfluxParser.parse_point("weather,location=us-midwest temperature='too warm' 1465839830100400200")
117
+ assert_not_equal(false,point) # a straight up parse error will false
118
+ end
119
+
120
+ def test_boolean
121
+ point = InfluxParser.parse_point("weather,location=us-midwest temperature=t 1465839830100400200")
122
+ assert_not_equal(false,point) # a straight up parse error will false
123
+ assert_equal(true,point['values']['temperature'])
124
+
125
+ point = InfluxParser.parse_point("weather,location=us-midwest temperature=T 1465839830100400200")
126
+ assert_not_equal(false,point) # a straight up parse error will false
127
+ assert_equal(true,point['values']['temperature'])
128
+
129
+ point = InfluxParser.parse_point("weather,location=us-midwest temperature=true 1465839830100400200")
130
+ assert_not_equal(false,point) # a straight up parse error will false
131
+ assert_equal(true,point['values']['temperature'])
132
+
133
+ point = InfluxParser.parse_point("weather,location=us-midwest temperature=True 1465839830100400200")
134
+ assert_not_equal(false,point) # a straight up parse error will false
135
+ assert_equal(true,point['values']['temperature'])
136
+
137
+ point = InfluxParser.parse_point("weather,location=us-midwest temperature=TRUE 1465839830100400200")
138
+ assert_not_equal(false,point) # a straight up parse error will false
139
+ assert_equal(true,point['values']['temperature'])
140
+
141
+ point = InfluxParser.parse_point("weather,location=us-midwest temperature=f 1465839830100400200")
142
+ assert_not_equal(false,point) # a straight up parse error will false
143
+ assert_equal(false,point['values']['temperature'])
144
+
145
+ point = InfluxParser.parse_point("weather,location=us-midwest temperature=F 1465839830100400200")
146
+ assert_not_equal(false,point) # a straight up parse error will false
147
+ assert_equal(false,point['values']['temperature'])
148
+
149
+ point = InfluxParser.parse_point("weather,location=us-midwest temperature=false 1465839830100400200")
150
+ assert_not_equal(false,point) # a straight up parse error will false
151
+ assert_equal(false,point['values']['temperature'])
152
+
153
+ point = InfluxParser.parse_point("weather,location=us-midwest temperature=False 1465839830100400200")
154
+ assert_not_equal(false,point) # a straight up parse error will false
155
+ assert_equal(false,point['values']['temperature'])
156
+
157
+ point = InfluxParser.parse_point("weather,location=us-midwest temperature=FALSE 1465839830100400200")
158
+ assert_not_equal(false,point) # a straight up parse error will false
159
+ assert_equal(false,point['values']['temperature'])
160
+
161
+ end
162
+ def test_ridiculous_quotes
163
+ # from the influx docs: Do not double or single quote measurement names, tag keys, tag values, and field keys. It is valid line protocol but InfluxDB assumes that the quotes are part of the name.
164
+ point = InfluxParser.parse_point('"weather","location"="us-midwest" "temperature"=82 1465839830100400200')
165
+
166
+ assert_not_equal(false,point) # a straight up parse error will false
167
+ # measurement
168
+ assert_equal('"weather"',point['measurement'])
169
+
170
+ # check tag
171
+ assert_equal(true,point['tags'].key?('"location"'))
172
+ assert_equal('"us-midwest"',point['tags']['"location"'])
173
+
174
+ # check values
175
+
176
+ assert_equal(true,point['values'].key?('"temperature"'))
177
+ assert_equal(82,point['values']['"temperature"'])
178
+
179
+ # time
180
+ assert_equal('1465839830100400200',point['time'])
181
+
182
+
183
+
184
+ # Do the same thing for ridiculous single quotes
185
+ point = InfluxParser.parse_point("'weather','location'='us-midwest' 'temperature'=82 1465839830100400200")
186
+
187
+ assert_not_equal(false,point) # a straight up parse error will false
188
+ # measurement
189
+ assert_equal("'weather'",point['measurement'])
190
+
191
+ # check tag
192
+ assert_equal(true,point['tags'].key?("'location'"))
193
+ assert_equal("'us-midwest'",point['tags']["'location'"])
194
+
195
+ # check values
196
+
197
+ assert_equal(true,point['values'].key?("'temperature'"))
198
+ assert_equal(82,point['values']["'temperature'"])
199
+
200
+ # time
201
+ assert_equal('1465839830100400200',point['time'])
202
+
203
+ end
204
+
205
+ def test_escaping
206
+ point = InfluxParser.parse_point('weather,location=us\,midwest temperature=82 1465839830100400200')
207
+ assert_not_equal(false,point) # a straight up parse error will false
208
+ assert_equal('us,midwest',point['tags']['location'])
209
+
210
+ point = InfluxParser.parse_point('weather,location=us-midwest temp\=rature=82 1465839830100400200')
211
+ assert_not_equal(false,point) # a straight up parse error will false
212
+ assert_equal(82,point['values']['temp=rature'])
213
+
214
+ point = InfluxParser.parse_point('weather,location\ place=us-midwest temperature=82 1465839830100400200')
215
+ assert_not_equal(false,point) # a straight up parse error will false
216
+ assert_equal(true,point['tags'].key?('location place'))
217
+
218
+ point = InfluxParser.parse_point('wea\,ther,location=us-midwest temperature=82 1465839830100400200')
219
+ assert_not_equal(false,point) # a straight up parse error will false
220
+ assert_equal('wea,ther',point['measurement'])
221
+
222
+ point = InfluxParser.parse_point('wea\ ther,location=us-midwest temperature=82 1465839830100400200')
223
+ assert_not_equal(false,point) # a straight up parse error will false
224
+ assert_equal('wea ther',point['measurement'])
225
+
226
+ point = InfluxParser.parse_point('weather temperature=toohot\"')
227
+ assert_not_equal(false,point) # a straight up parse error will false
228
+ assert_equal('toohot"',point['values']['temperature'])
229
+
230
+
231
+ # so many slashes -- note they're extra terrible because I need to escape ruby slashes in the test strings
232
+
233
+ # forward slash
234
+ point = InfluxParser.parse_point('weather,location=us-midwest temperature_str="too hot/cold" 1465839830100400201')
235
+ assert_not_equal(false,point) # a straight up parse error will false
236
+ assert_equal('too hot/cold',point['values']['temperature_str'])
237
+
238
+ # one slash
239
+ point = InfluxParser.parse_point('weather,location=us-midwest temperature_str="too hot\cold" 1465839830100400201')
240
+ assert_not_equal(false,point) # a straight up parse error will false
241
+ assert_equal('too hot\cold',point['values']['temperature_str'])
242
+
243
+ # two slashes
244
+ point = InfluxParser.parse_point('weather,location=us-midwest temperature_str="too hot\\\cold" 1465839830100400201')
245
+ assert_not_equal(false,point) # a straight up parse error will false
246
+ assert_equal('too hot\cold',point['values']['temperature_str'])
247
+
248
+ # three slashes
249
+ point = InfluxParser.parse_point('weather,location=us-midwest temperature_str="too hot\\\\\cold" 1465839830100400201')
250
+ assert_not_equal(false,point) # a straight up parse error will false
251
+ assert_equal("too hot\\\\cold",point['values']['temperature_str'])
252
+
253
+ # four slashes
254
+ point = InfluxParser.parse_point('weather,location=us-midwest temperature_str="too hot\\\\\\\cold" 1465839830100400201')
255
+ assert_not_equal(false,point) # a straight up parse error will false
256
+ assert_equal("too hot\\\\cold",point['values']['temperature_str'])
257
+
258
+ # five slashes
259
+ point = InfluxParser.parse_point('weather,location=us-midwest temperature_str="too hot\\\\\\\\\cold" 1465839830100400201')
260
+ assert_not_equal(false,point) # a straight up parse error will false
261
+ assert_equal("too hot\\\\\\cold",point['values']['temperature_str'])
262
+
263
+ end
264
+ def test_types
265
+ point = InfluxParser.parse_point('weather float=82,integer=82i,impliedstring=helloworld,explicitstring="hello world" 1465839830100400200')
266
+ assert_not_equal(false,point) # a straight up parse error will false
267
+ assert_equal(82.0,point['values']['float'])
268
+ assert_equal(82,point['values']['integer'])
269
+ assert_equal('helloworld',point['values']['impliedstring'])
270
+ assert_equal('hello world',point['values']['explicitstring'])
271
+
272
+ # do it again but this time without type parsing
273
+ point = InfluxParser.parse_point('weather float=82,integer=82i,impliedstring=helloworld,explicitstring="hello world" 1465839830100400200',{:parse_types => false})
274
+ assert_not_equal(false,point) # a straight up parse error will false
275
+ assert_equal('82',point['values']['float'])
276
+ assert_equal('82',point['values']['integer'])
277
+ assert_equal('helloworld',point['values']['impliedstring'])
278
+ assert_equal('hello world',point['values']['explicitstring'])
279
+
280
+ end
281
+ end
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: influxparser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Robert Labrie
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-02-03 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Unofficial influx line protocol parser
14
+ email: robert.labrie@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/influxparser.rb
20
+ - test/test_parse_point_from_docs.rb
21
+ homepage:
22
+ licenses: []
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.5.2.1
41
+ signing_key:
42
+ specification_version: 3
43
+ summary: InfluxDB line protocol parser
44
+ test_files:
45
+ - test/test_parse_point_from_docs.rb