influxparser 0.0.2
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.
- checksums.yaml +7 -0
- data/lib/influxparser.rb +79 -0
- data/test/test_parse_point_from_docs.rb +250 -0
- metadata +45 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 599f27d2ff8251400b2b593b79e0ca7facad4aff
|
4
|
+
data.tar.gz: 84c2792855209669e2bc8e4a41aec43c393be2e1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a5a8a3e29905ac2f31915e7be9dc2e7dcb987f5375a0a80f871720b8e859386397d70c1e73a2b58982db45946a04d0146a82660d6c95dd3f646a2460c3be4335
|
7
|
+
data.tar.gz: 1f97c90e05e42b503ef03283f72cb22ba176f00564c18e356cd998a5dd587f78f2990aa732da1e1e985c8f3f054263149d306efaa15851641c8862c93952d365
|
data/lib/influxparser.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# TODO: the tags hash should be absent when there are no tags
|
2
|
+
# TODO: numbers shouldn't be strings
|
3
|
+
# TODO: numbers which aren't strings are floats
|
4
|
+
# TODO: numbers with a trailing i are integers -- ALERT this is actually broken
|
5
|
+
# TODO: optional timestamp parsing
|
6
|
+
# TODO: time key shouldn't exist if there is no time
|
7
|
+
# TODO: deal with improper line protocol
|
8
|
+
class InfluxParser
|
9
|
+
class << self
|
10
|
+
def parse_point(s)
|
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 = ''
|
30
|
+
last_key = ''
|
31
|
+
point['values'] = {}
|
32
|
+
vparts = s[measurement_end+1..-1].split(/(?<!\\),/)
|
33
|
+
vparts.each do |v|
|
34
|
+
value = v.split(/(?<!\\)=/)
|
35
|
+
last_value = unescape_point value[1]
|
36
|
+
last_key = unescape_tag value[0]
|
37
|
+
# puts "kv:#{last_key}==#{last_value}"
|
38
|
+
point['values'][last_key] = last_value
|
39
|
+
end
|
40
|
+
|
41
|
+
# check for a timestamp in the last value
|
42
|
+
# TODO: I hate this, but it's late and I just want to move past it for now
|
43
|
+
# TODO: what happens if the last character of the last value is an escaped quote?
|
44
|
+
has_space = last_value.rindex(/ /)
|
45
|
+
if has_space
|
46
|
+
time_stamp = last_value[has_space+1..-1] # take everything from the space to the end
|
47
|
+
if time_stamp.index(/"/)
|
48
|
+
point['time'] = nil
|
49
|
+
else
|
50
|
+
# it was a timestamp, strip it from the last value and set the timestamp
|
51
|
+
point['values'][last_key] = unescape_point last_value[0..has_space-1]
|
52
|
+
point['time'] = time_stamp
|
53
|
+
end
|
54
|
+
else
|
55
|
+
point['time'] = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
point
|
59
|
+
end
|
60
|
+
def unescape_measurement(s)
|
61
|
+
s.gsub(/\\ /,' ').gsub(/\\,/,',')
|
62
|
+
end
|
63
|
+
def unescape_tag(s)
|
64
|
+
t = unescape_measurement s
|
65
|
+
t.gsub(/\\=/,'=')
|
66
|
+
end
|
67
|
+
def unescape_point(s)
|
68
|
+
if s[-1,1] == '"'
|
69
|
+
|
70
|
+
# it's a quoted string and should be unescaped
|
71
|
+
s = s[1..-2] # take the wrapping quotes off
|
72
|
+
return s.gsub(/\\\\/,'\\').gsub(/\\"/,',')
|
73
|
+
else
|
74
|
+
# it's a number and if it's trailing an i we need to strip it because we're not handling precision here
|
75
|
+
return s.chomp('i')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,250 @@
|
|
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',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
|
+
# TODO: validate the parsed booleans are bools and expected
|
122
|
+
point = InfluxParser.parse_point("weather,location=us-midwest temperature=t 1465839830100400200")
|
123
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
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
|
+
|
128
|
+
point = InfluxParser.parse_point("weather,location=us-midwest temperature=true 1465839830100400200")
|
129
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
130
|
+
|
131
|
+
point = InfluxParser.parse_point("weather,location=us-midwest temperature=True 1465839830100400200")
|
132
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
133
|
+
|
134
|
+
point = InfluxParser.parse_point("weather,location=us-midwest temperature=TRUE 1465839830100400200")
|
135
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
136
|
+
|
137
|
+
point = InfluxParser.parse_point("weather,location=us-midwest temperature=f 1465839830100400200")
|
138
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
139
|
+
|
140
|
+
point = InfluxParser.parse_point("weather,location=us-midwest temperature=F 1465839830100400200")
|
141
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
142
|
+
|
143
|
+
point = InfluxParser.parse_point("weather,location=us-midwest temperature=false 1465839830100400200")
|
144
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
145
|
+
|
146
|
+
point = InfluxParser.parse_point("weather,location=us-midwest temperature=False 1465839830100400200")
|
147
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
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
|
+
|
152
|
+
end
|
153
|
+
def test_ridiculous_quotes
|
154
|
+
# 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.
|
155
|
+
point = InfluxParser.parse_point('"weather","location"="us-midwest" "temperature"=82 1465839830100400200')
|
156
|
+
|
157
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
158
|
+
# measurement
|
159
|
+
assert_equal('"weather"',point['measurement'])
|
160
|
+
|
161
|
+
# check tag
|
162
|
+
assert_equal(true,point['tags'].key?('"location"'))
|
163
|
+
assert_equal('"us-midwest"',point['tags']['"location"'])
|
164
|
+
|
165
|
+
# check values
|
166
|
+
|
167
|
+
assert_equal(true,point['values'].key?('"temperature"'))
|
168
|
+
assert_equal('82',point['values']['"temperature"'])
|
169
|
+
|
170
|
+
# time
|
171
|
+
assert_equal('1465839830100400200',point['time'])
|
172
|
+
|
173
|
+
|
174
|
+
|
175
|
+
# Do the same thing for ridiculous single quotes
|
176
|
+
point = InfluxParser.parse_point("'weather','location'='us-midwest' 'temperature'=82 1465839830100400200")
|
177
|
+
|
178
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
179
|
+
# measurement
|
180
|
+
assert_equal("'weather'",point['measurement'])
|
181
|
+
|
182
|
+
# check tag
|
183
|
+
assert_equal(true,point['tags'].key?("'location'"))
|
184
|
+
assert_equal("'us-midwest'",point['tags']["'location'"])
|
185
|
+
|
186
|
+
# check values
|
187
|
+
|
188
|
+
assert_equal(true,point['values'].key?("'temperature'"))
|
189
|
+
assert_equal('82',point['values']["'temperature'"])
|
190
|
+
|
191
|
+
# time
|
192
|
+
assert_equal('1465839830100400200',point['time'])
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_escaping
|
197
|
+
point = InfluxParser.parse_point('weather,location=us\,midwest temperature=82 1465839830100400200')
|
198
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
199
|
+
assert_equal('us,midwest',point['tags']['location'])
|
200
|
+
|
201
|
+
point = InfluxParser.parse_point('weather,location=us-midwest temp\=rature=82 1465839830100400200')
|
202
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
203
|
+
assert_equal('82',point['values']['temp=rature'])
|
204
|
+
|
205
|
+
point = InfluxParser.parse_point('weather,location\ place=us-midwest temperature=82 1465839830100400200')
|
206
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
207
|
+
assert_equal(true,point['tags'].key?('location place'))
|
208
|
+
|
209
|
+
point = InfluxParser.parse_point('wea\,ther,location=us-midwest temperature=82 1465839830100400200')
|
210
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
211
|
+
assert_equal('wea,ther',point['measurement'])
|
212
|
+
|
213
|
+
point = InfluxParser.parse_point('wea\ ther,location=us-midwest temperature=82 1465839830100400200')
|
214
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
215
|
+
assert_equal('wea ther',point['measurement'])
|
216
|
+
|
217
|
+
# so many slashes -- note they're extra terrible because I need to escape ruby slashes in the test strings
|
218
|
+
|
219
|
+
# forward slash
|
220
|
+
point = InfluxParser.parse_point('weather,location=us-midwest temperature_str="too hot/cold" 1465839830100400201')
|
221
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
222
|
+
assert_equal('too hot/cold',point['values']['temperature_str'])
|
223
|
+
|
224
|
+
# one slash
|
225
|
+
point = InfluxParser.parse_point('weather,location=us-midwest temperature_str="too hot\cold" 1465839830100400201')
|
226
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
227
|
+
assert_equal('too hot\cold',point['values']['temperature_str'])
|
228
|
+
|
229
|
+
# two slashes
|
230
|
+
point = InfluxParser.parse_point('weather,location=us-midwest temperature_str="too hot\\\cold" 1465839830100400201')
|
231
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
232
|
+
assert_equal('too hot\cold',point['values']['temperature_str'])
|
233
|
+
|
234
|
+
# three slashes
|
235
|
+
point = InfluxParser.parse_point('weather,location=us-midwest temperature_str="too hot\\\\\cold" 1465839830100400201')
|
236
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
237
|
+
assert_equal("too hot\\\\cold",point['values']['temperature_str'])
|
238
|
+
|
239
|
+
# four slashes
|
240
|
+
point = InfluxParser.parse_point('weather,location=us-midwest temperature_str="too hot\\\\\\\cold" 1465839830100400201')
|
241
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
242
|
+
assert_equal("too hot\\\\cold",point['values']['temperature_str'])
|
243
|
+
|
244
|
+
# five slashes
|
245
|
+
point = InfluxParser.parse_point('weather,location=us-midwest temperature_str="too hot\\\\\\\\\cold" 1465839830100400201')
|
246
|
+
assert_not_equal(false,point) # a straight up parse error will false
|
247
|
+
assert_equal("too hot\\\\\\cold",point['values']['temperature_str'])
|
248
|
+
|
249
|
+
end
|
250
|
+
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.2
|
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
|