influxparser 0.0.1 → 0.0.2

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: 300f243df83f6c65d0f5206e32623be63fe60d3e
4
- data.tar.gz: 51a7aa6aec089ed64d1b721431ab2dbfdbc3ad06
3
+ metadata.gz: 599f27d2ff8251400b2b593b79e0ca7facad4aff
4
+ data.tar.gz: 84c2792855209669e2bc8e4a41aec43c393be2e1
5
5
  SHA512:
6
- metadata.gz: 6edc1719f7f50394732b52257adacb96fd12445dd94d3124bb58a8bf919d7d179aca26ba2c179e5c71709144eceaa01da61eeea3eb5c60d5693ea06043f8d63c
7
- data.tar.gz: fe002ca912eb0783ca328786744672eb525096ec503dd509cb00d21ceb777f545bccb2925698b7dc0b4e1ae2cd49ef89ec3bab5b29dbf8a8294d5657cd84e38b
6
+ metadata.gz: a5a8a3e29905ac2f31915e7be9dc2e7dcb987f5375a0a80f871720b8e859386397d70c1e73a2b58982db45946a04d0146a82660d6c95dd3f646a2460c3be4335
7
+ data.tar.gz: 1f97c90e05e42b503ef03283f72cb22ba176f00564c18e356cd998a5dd587f78f2990aa732da1e1e985c8f3f054263149d306efaa15851641c8862c93952d365
@@ -1,56 +1,79 @@
1
- class InfluxParser
2
- class << self
3
- def parse_point(s)
4
- point = {}
5
- point['_raw'] = s
6
- s = s.strip # trim whitespace
7
-
8
- parts = s.split(/(?<!\\) /) # split on unescaped spaces for the initial sections
9
- return false if parts.length < 2 # at bare minimum there needs to be a measurement and some fields
10
-
11
- mparts = parts[0].split(/(?<!\\),/) # split on unescaped commas for the measurement name and tags
12
- point['measurement'] = unescape_measurement mparts[0]
13
-
14
- # if any tags were attached to the measurement iterate over them now
15
- point['tags'] = {}
16
- mparts.drop(1).each do |t|
17
- tag = t.split(/(?<!\\)=/)
18
- point['tags'][unescape_tag tag[0]] = unescape_tag tag[1]
19
- end
20
-
21
- # now iterate over the values
22
- point['values'] = {}
23
- vparts = parts[1].split(/(?<!\\),/)
24
- vparts.each do |v|
25
- value = v.split(/(?<!\\)=/)
26
- point['values'][unescape_tag value[0]] = unescape_point value[1]
27
- end
28
-
29
- if parts.length == 3 # handle the timestamp
30
- point['time'] = parts[2]
31
- else
32
- # no time left for you, on my way to better things
33
- point['time'] = nil
34
- end
35
- point
36
- end
37
- def unescape_measurement(s)
38
- s.gsub(/\\ /,' ').gsub(/\\,/,',')
39
- end
40
- def unescape_tag(s)
41
- t = unescape_measurement s
42
- t.gsub(/\\=/,'=')
43
- end
44
- def unescape_point(s)
45
- if s[-1,1] == '"'
46
-
47
- # it's a quoted string and should be unescaped
48
- s = s[1..-2] # take the wrapping quotes off
49
- return s.gsub(/\\\\/,',').gsub(/\\"/,',')
50
- else
51
- # it's a number and if it's trailing an i we need to strip it because we're not handling precision here
52
- return s.chomp('i')
53
- end
54
- end
55
- end
56
- end
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 CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: influxparser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Labrie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-27 00:00:00.000000000 Z
11
+ date: 2020-02-03 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Unofficial influx line protocol parser
14
14
  email: robert.labrie@gmail.com
@@ -17,6 +17,7 @@ extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
19
  - lib/influxparser.rb
20
+ - test/test_parse_point_from_docs.rb
20
21
  homepage:
21
22
  licenses: []
22
23
  metadata: {}
@@ -40,4 +41,5 @@ rubygems_version: 2.5.2.1
40
41
  signing_key:
41
42
  specification_version: 3
42
43
  summary: InfluxDB line protocol parser
43
- test_files: []
44
+ test_files:
45
+ - test/test_parse_point_from_docs.rb