From 52a1416dd9a588225ba0458ec8ee1e1cc86d4582 Mon Sep 17 00:00:00 2001 From: miverso2 Date: Wed, 23 Mar 2022 07:14:28 -0500 Subject: [PATCH 1/2] v0.2.0 --- CHANGELOG.md | 3 + README.md | 5 ++ lib/telemetry/metrics/parser/line_protocol.rb | 78 +++++++++++++++++++ lib/telemetry/metrics/parser/version.rb | 2 +- spec/validate_spec.rb | 59 ++++++++++++++ 5 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 spec/validate_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e91359..4d2efda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Telemetry::Metrics::Parser Changelog +## v0.2.0 +Adding new line validation methods + ## v0.1.1 * Adding more rspec tests for shellwords * Adding in **opts to from_line_protocol method diff --git a/README.md b/README.md index 3b821a3..e4675cc 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,11 @@ results[:fields] # => { temperature: 82 } results[:timestamp] # => 1465839830100400200 ``` +#### Validations +```ruby +Telemetry::Metrics::Parser::LineProtocol.line_is_valid?('weather,location=us-midwest temperature=82 1465839830100400200') # true +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 +``` Authors ---------- diff --git a/lib/telemetry/metrics/parser/line_protocol.rb b/lib/telemetry/metrics/parser/line_protocol.rb index 0fa5afb..018383d 100644 --- a/lib/telemetry/metrics/parser/line_protocol.rb +++ b/lib/telemetry/metrics/parser/line_protocol.rb @@ -55,6 +55,84 @@ def hash_to_line(hash) hash.map { |k, v| "#{k}=#{v}" }.join(',').strip.delete_suffix(',') end module_function :hash_to_line + + def line_is_current?(timestamp, limit: 86_400) + return false unless timestamp.is_a?(Integer) + return false unless limit.is_a?(Integer) + + current_time = DateTime.now.strftime('%s%9N').to_i + time_limit = current_time - (limit * 1000 * 1000 * 1000 * 3) + timestamp >= time_limit + end + module_function :line_is_current? + + def field_is_number?(value) + return false if value.nil? + return true if value.is_a?(Integer) + return true if value.is_a?(Float) + + %(f i).include?(value[-1]) + end + module_function :field_is_number? + + def tag_is_valid?(key, value) + return false if key.nil? || value.nil? + return false unless value.chars.detect { |ch| !valid_tag_chars.include?(ch) }.nil? + return false unless key.to_s.chars.detect { |ch| !valid_tag_chars.include?(ch) }.nil? + + true + end + module_function :tag_is_valid? + + def node_group_tag?(tags) + tags[:influxdb_node_group].is_a?(String) + end + module_function :node_group_tag? + + def database_tag?(tags) + tags[:influxdb_database].is_a?(String) + end + module_function :database_tag? + + def measurement_valid?(measurement) + return false unless measurement.is_a?(String) + + measurement.chars.detect { |ch| !valid_measurement_chars.include?(ch) }.nil? + end + module_function :measurement_valid? + + def valid_measurement_chars + @valid_measurement_chars ||= ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a + %w[_ - .] + end + module_function :valid_measurement_chars + + def valid_tag_chars + @valid_tag_chars ||= ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a + %w[_ - .] + end + module_function :valid_tag_chars + + def line_is_valid?(line) # rubocop:disable Metrics/AbcSize + line = parse(line) if line.is_a?(String) + return "line is too old, #{line}" unless line_is_current?(line[:timestamp]) + return "line is missing influxdb_database, #{line}" unless node_group_tag? line[:tags] + return "line is missing influxdb_node_group, #{line}" unless database_tag? line[:tags] + return "measurement name #{line[:measurement]} is not valid" unless measurement_valid?(line[:measurement]) + + line[:fields].each do |field, value| + next if field_is_number?(value) + + return "field values must be an Integer or String, #{field} :#{value} #{value.class}" + end + + line[:tags].each do |tag, value| + next if tag_is_valid?(tag, value) + + return "tags must be a-z0-9_-. but was given #{tag}: #{value}" + end + + true + end + module_function :line_is_valid? end end end diff --git a/lib/telemetry/metrics/parser/version.rb b/lib/telemetry/metrics/parser/version.rb index 4a4ebab..d176134 100644 --- a/lib/telemetry/metrics/parser/version.rb +++ b/lib/telemetry/metrics/parser/version.rb @@ -1,7 +1,7 @@ module Telemetry module Metrics module Parser - VERSION = '0.1.1'.freeze + VERSION = '0.2.0'.freeze end end end diff --git a/spec/validate_spec.rb b/spec/validate_spec.rb new file mode 100644 index 0000000..965660a --- /dev/null +++ b/spec/validate_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' +require 'telemetry/metrics/parser/line_protocol' + +RSpec.describe Telemetry::Metrics::Parser::LineProtocol do + it 'can validate the measurement' do + expect(described_class.measurement_valid?('foobar')).to eq true + expect(described_class.measurement_valid?('FooBar')).to eq true + expect(described_class.measurement_valid?('foo-bar')).to eq true + expect(described_class.measurement_valid?('foo-bar_test')).to eq true + expect(described_class.measurement_valid?('foo.test-bar_again')).to eq true + end + + it 'can validate tags' do + expect(described_class.tag_is_valid?('foobar', 'testing')).to eq true + expect(described_class.tag_is_valid?('foo_bar', 'testing')).to eq true + expect(described_class.tag_is_valid?('foo-bar', 'testing')).to eq true + expect(described_class.tag_is_valid?('foo.bar', 'testing')).to eq true + expect(described_class.tag_is_valid?('test', 'foo.bar')).to eq true + expect(described_class.tag_is_valid?('test', 'foo-bar')).to eq true + expect(described_class.tag_is_valid?('test', 'foo_bar')).to eq true + expect(described_class.tag_is_valid?('test world', 'foo_bar')).to eq false + expect(described_class.tag_is_valid?('test&world', 'foo_bar')).to eq false + expect(described_class.tag_is_valid?('test', 'foo%bar')).to eq false + end + + it 'can validate fields' do + expect(described_class.field_is_number?(1)).to eq true + expect(described_class.field_is_number?(0.11111)).to eq true + expect(described_class.field_is_number?('foobar')).to eq false + expect(described_class.field_is_number?('1i')).to eq true + expect(described_class.field_is_number?('1f')).to eq true + expect(described_class.field_is_number?('1.1f')).to eq true + end + + it 'can validate the line is current' do + expect(described_class.line_is_current?(1_465_839_830_100_400_200)).to eq false + expect(described_class.line_is_current?(2_665_839_830_100_400_200)).to eq true + expect(described_class.line_is_current?('11111')).to eq false + end + + it 'can verify a node_group tag exists' do + expect(described_class.node_group_tag?({ foo: 'bar' })).to eq false + expect(described_class.node_group_tag?({ influxdb_node_group: 'bar' })).to eq true + end + + it 'can verify a database_tag exists' do + expect(described_class.database_tag?({ foo: 'bar' })).to eq false + expect(described_class.database_tag?({ influxdb_database: 'bar' })).to eq true + end + + it 'can validate a line' do + expect(described_class.line_is_valid?('weather,location=us-midwest temperature=82 1465839830100400200')).to be_a String + expect(described_class.line_is_valid?('weather,location=us-midwest temperature=82 2465839830100400200')).to be_a String + expect(described_class.line_is_valid?('weather,location=us-midwest,influxdb_database=foo temperature=82 2465839830100400200')).to be_a String + expect(described_class.line_is_valid?('weather,location=us-midwest,influxdb_database=foo,influxdb_node_group=foo temperature=82 2465839830100400200')).to be_truthy + 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 + 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 + end +end From 8228a516cf1b7455ed7f263d4a0fcdac112e3674 Mon Sep 17 00:00:00 2001 From: cgerken Date: Wed, 23 Mar 2022 09:12:46 -0500 Subject: [PATCH 2/2] updating method and var names --- lib/telemetry/metrics/parser/line_protocol.rb | 13 ++++++------- spec/validate_spec.rb | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/telemetry/metrics/parser/line_protocol.rb b/lib/telemetry/metrics/parser/line_protocol.rb index 018383d..ed780de 100644 --- a/lib/telemetry/metrics/parser/line_protocol.rb +++ b/lib/telemetry/metrics/parser/line_protocol.rb @@ -56,15 +56,14 @@ def hash_to_line(hash) end module_function :hash_to_line - def line_is_current?(timestamp, limit: 86_400) + def line_is_recent?(timestamp, max_age_sec: 86_400) return false unless timestamp.is_a?(Integer) - return false unless limit.is_a?(Integer) + return false unless max_age_sec.is_a?(Integer) - current_time = DateTime.now.strftime('%s%9N').to_i - time_limit = current_time - (limit * 1000 * 1000 * 1000 * 3) - timestamp >= time_limit + current_epoch_ns = DateTime.now.strftime('%s%9N').to_i + timestamp >= current_epoch_ns - (max_age_sec * 1000 * 1000 * 1000 * 3) end - module_function :line_is_current? + module_function :line_is_recent? def field_is_number?(value) return false if value.nil? @@ -113,7 +112,7 @@ def valid_tag_chars def line_is_valid?(line) # rubocop:disable Metrics/AbcSize line = parse(line) if line.is_a?(String) - return "line is too old, #{line}" unless line_is_current?(line[:timestamp]) + return "line is too old, #{line}" unless line_is_recent?(line[:timestamp]) return "line is missing influxdb_database, #{line}" unless node_group_tag? line[:tags] return "line is missing influxdb_node_group, #{line}" unless database_tag? line[:tags] return "measurement name #{line[:measurement]} is not valid" unless measurement_valid?(line[:measurement]) diff --git a/spec/validate_spec.rb b/spec/validate_spec.rb index 965660a..458926d 100644 --- a/spec/validate_spec.rb +++ b/spec/validate_spec.rb @@ -33,9 +33,9 @@ end it 'can validate the line is current' do - expect(described_class.line_is_current?(1_465_839_830_100_400_200)).to eq false - expect(described_class.line_is_current?(2_665_839_830_100_400_200)).to eq true - expect(described_class.line_is_current?('11111')).to eq false + expect(described_class.line_is_recent?(1_465_839_830_100_400_200)).to eq false + expect(described_class.line_is_recent?(2_665_839_830_100_400_200)).to eq true + expect(described_class.line_is_recent?('11111')).to eq false end it 'can verify a node_group tag exists' do