Skip to content

Commit 9f4cd55

Browse files
authored
Merge pull request #1 from Optum/develop
v0.2.0
2 parents 1d5ce6f + 33b4382 commit 9f4cd55

File tree

5 files changed

+145
-1
lines changed

5 files changed

+145
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Telemetry::Metrics::Parser Changelog
22

3+
## v0.2.0
4+
Adding new line validation methods
5+
36
## v0.1.1
47
* Adding more rspec tests for shellwords
58
* Adding in **opts to from_line_protocol method

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ results[:fields] # => { temperature: 82 }
2424
results[:timestamp] # => 1465839830100400200
2525
```
2626

27+
#### Validations
28+
```ruby
29+
Telemetry::Metrics::Parser::LineProtocol.line_is_valid?('weather,location=us-midwest temperature=82 1465839830100400200') # true
30+
Telemetry::Metrics::Parser::LineProtocol.line_is_valid?('weather,location=us-midwest temperature=this_field_is_a_string 1465839830100400200') # false but returned as a string error
31+
```
2732

2833
Authors
2934
----------

lib/telemetry/metrics/parser/line_protocol.rb

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,83 @@ def hash_to_line(hash)
5555
hash.map { |k, v| "#{k}=#{v}" }.join(',').strip.delete_suffix(',')
5656
end
5757
module_function :hash_to_line
58+
59+
def line_is_recent?(timestamp, max_age_sec: 86_400)
60+
return false unless timestamp.is_a?(Integer)
61+
return false unless max_age_sec.is_a?(Integer)
62+
63+
current_epoch_ns = DateTime.now.strftime('%s%9N').to_i
64+
timestamp >= current_epoch_ns - (max_age_sec * 1000 * 1000 * 1000 * 3)
65+
end
66+
module_function :line_is_recent?
67+
68+
def field_is_number?(value)
69+
return false if value.nil?
70+
return true if value.is_a?(Integer)
71+
return true if value.is_a?(Float)
72+
73+
%(f i).include?(value[-1])
74+
end
75+
module_function :field_is_number?
76+
77+
def tag_is_valid?(key, value)
78+
return false if key.nil? || value.nil?
79+
return false unless value.chars.detect { |ch| !valid_tag_chars.include?(ch) }.nil?
80+
return false unless key.to_s.chars.detect { |ch| !valid_tag_chars.include?(ch) }.nil?
81+
82+
true
83+
end
84+
module_function :tag_is_valid?
85+
86+
def node_group_tag?(tags)
87+
tags[:influxdb_node_group].is_a?(String)
88+
end
89+
module_function :node_group_tag?
90+
91+
def database_tag?(tags)
92+
tags[:influxdb_database].is_a?(String)
93+
end
94+
module_function :database_tag?
95+
96+
def measurement_valid?(measurement)
97+
return false unless measurement.is_a?(String)
98+
99+
measurement.chars.detect { |ch| !valid_measurement_chars.include?(ch) }.nil?
100+
end
101+
module_function :measurement_valid?
102+
103+
def valid_measurement_chars
104+
@valid_measurement_chars ||= ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a + %w[_ - .]
105+
end
106+
module_function :valid_measurement_chars
107+
108+
def valid_tag_chars
109+
@valid_tag_chars ||= ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a + %w[_ - .]
110+
end
111+
module_function :valid_tag_chars
112+
113+
def line_is_valid?(line) # rubocop:disable Metrics/AbcSize
114+
line = parse(line) if line.is_a?(String)
115+
return "line is too old, #{line}" unless line_is_recent?(line[:timestamp])
116+
return "line is missing influxdb_database, #{line}" unless node_group_tag? line[:tags]
117+
return "line is missing influxdb_node_group, #{line}" unless database_tag? line[:tags]
118+
return "measurement name #{line[:measurement]} is not valid" unless measurement_valid?(line[:measurement])
119+
120+
line[:fields].each do |field, value|
121+
next if field_is_number?(value)
122+
123+
return "field values must be an Integer or String, #{field} :#{value} #{value.class}"
124+
end
125+
126+
line[:tags].each do |tag, value|
127+
next if tag_is_valid?(tag, value)
128+
129+
return "tags must be a-z0-9_-. but was given #{tag}: #{value}"
130+
end
131+
132+
true
133+
end
134+
module_function :line_is_valid?
58135
end
59136
end
60137
end
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module Telemetry
22
module Metrics
33
module Parser
4-
VERSION = '0.1.1'.freeze
4+
VERSION = '0.2.0'.freeze
55
end
66
end
77
end

spec/validate_spec.rb

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
require 'spec_helper'
2+
require 'telemetry/metrics/parser/line_protocol'
3+
4+
RSpec.describe Telemetry::Metrics::Parser::LineProtocol do
5+
it 'can validate the measurement' do
6+
expect(described_class.measurement_valid?('foobar')).to eq true
7+
expect(described_class.measurement_valid?('FooBar')).to eq true
8+
expect(described_class.measurement_valid?('foo-bar')).to eq true
9+
expect(described_class.measurement_valid?('foo-bar_test')).to eq true
10+
expect(described_class.measurement_valid?('foo.test-bar_again')).to eq true
11+
end
12+
13+
it 'can validate tags' do
14+
expect(described_class.tag_is_valid?('foobar', 'testing')).to eq true
15+
expect(described_class.tag_is_valid?('foo_bar', 'testing')).to eq true
16+
expect(described_class.tag_is_valid?('foo-bar', 'testing')).to eq true
17+
expect(described_class.tag_is_valid?('foo.bar', 'testing')).to eq true
18+
expect(described_class.tag_is_valid?('test', 'foo.bar')).to eq true
19+
expect(described_class.tag_is_valid?('test', 'foo-bar')).to eq true
20+
expect(described_class.tag_is_valid?('test', 'foo_bar')).to eq true
21+
expect(described_class.tag_is_valid?('test world', 'foo_bar')).to eq false
22+
expect(described_class.tag_is_valid?('test&world', 'foo_bar')).to eq false
23+
expect(described_class.tag_is_valid?('test', 'foo%bar')).to eq false
24+
end
25+
26+
it 'can validate fields' do
27+
expect(described_class.field_is_number?(1)).to eq true
28+
expect(described_class.field_is_number?(0.11111)).to eq true
29+
expect(described_class.field_is_number?('foobar')).to eq false
30+
expect(described_class.field_is_number?('1i')).to eq true
31+
expect(described_class.field_is_number?('1f')).to eq true
32+
expect(described_class.field_is_number?('1.1f')).to eq true
33+
end
34+
35+
it 'can validate the line is current' do
36+
expect(described_class.line_is_recent?(1_465_839_830_100_400_200)).to eq false
37+
expect(described_class.line_is_recent?(2_665_839_830_100_400_200)).to eq true
38+
expect(described_class.line_is_recent?('11111')).to eq false
39+
end
40+
41+
it 'can verify a node_group tag exists' do
42+
expect(described_class.node_group_tag?({ foo: 'bar' })).to eq false
43+
expect(described_class.node_group_tag?({ influxdb_node_group: 'bar' })).to eq true
44+
end
45+
46+
it 'can verify a database_tag exists' do
47+
expect(described_class.database_tag?({ foo: 'bar' })).to eq false
48+
expect(described_class.database_tag?({ influxdb_database: 'bar' })).to eq true
49+
end
50+
51+
it 'can validate a line' do
52+
expect(described_class.line_is_valid?('weather,location=us-midwest temperature=82 1465839830100400200')).to be_a String
53+
expect(described_class.line_is_valid?('weather,location=us-midwest temperature=82 2465839830100400200')).to be_a String
54+
expect(described_class.line_is_valid?('weather,location=us-midwest,influxdb_database=foo temperature=82 2465839830100400200')).to be_a String
55+
expect(described_class.line_is_valid?('weather,location=us-midwest,influxdb_database=foo,influxdb_node_group=foo temperature=82 2465839830100400200')).to be_truthy
56+
expect(described_class.line_is_valid?('weather,location=us-midwest,influxdb_database=foo,influxdb_node_group=foo temperature=82,field=aaa 2465839830100400200')).to be_a String
57+
expect(described_class.line_is_valid?('weather,locat%ion=us-midw%est,influxdb_database=foo,influxdb_node_group=foo temperature=82 2465839830100400200')).to be_a String
58+
end
59+
end

0 commit comments

Comments
 (0)