From 0d96968819294b43ffe141d4da988a422ac64940 Mon Sep 17 00:00:00 2001 From: Sven Rebhan <36194019+srebhan@users.noreply.github.com> Date: Wed, 8 Jun 2022 21:39:02 +0200 Subject: [PATCH] feat: Migrate xpath parser to new style (#11218) --- config/config.go | 4 +- config/config_test.go | 20 --- plugins/parsers/all/all.go | 1 + plugins/parsers/registry.go | 59 ++++---- plugins/parsers/registry_test.go | 41 ++++-- plugins/parsers/xpath/parser.go | 113 +++++++++++---- plugins/parsers/xpath/parser_test.go | 204 ++++++++++++++++----------- 7 files changed, 261 insertions(+), 181 deletions(-) diff --git a/config/config.go b/config/config.go index 63717bddb..3f291a903 100644 --- a/config/config.go +++ b/config/config.go @@ -1842,9 +1842,7 @@ func (c *Config) missingTomlField(_ reflect.Type, key string) error { "prefix", "prometheus_export_timestamp", "prometheus_ignore_timestamp", "prometheus_sort_metrics", "prometheus_string_as_label", "separator", "splunkmetric_hec_routing", "splunkmetric_multimetric", "tag_keys", "tagdrop", "tagexclude", "taginclude", "tagpass", "tags", "template", "templates", - "value_field_name", "wavefront_source_override", "wavefront_use_strict", "wavefront_disable_prefix_conversion", - "xml", "xpath", "xpath_json", "xpath_msgpack", "xpath_protobuf", "xpath_print_document", - "xpath_protobuf_file", "xpath_protobuf_type", "xpath_protobuf_import_paths": + "value_field_name", "wavefront_source_override", "wavefront_use_strict", "wavefront_disable_prefix_conversion": // ignore fields that are common to all plugins. default: diff --git a/config/config_test.go b/config/config_test.go index 030100984..273e8fb33 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -406,7 +406,6 @@ func TestConfig_ParserInterfaceNewFormat(t *testing.T) { } override := map[string]struct { - cfg *parsers.Config param map[string]interface{} mask []string }{ @@ -420,11 +419,6 @@ func TestConfig_ParserInterfaceNewFormat(t *testing.T) { mask: []string{"Now"}, }, "xpath_protobuf": { - cfg: &parsers.Config{ - MetricName: "parser_test_new", - XPathProtobufFile: "testdata/addressbook.proto", - XPathProtobufType: "addressbook.AddressBook", - }, param: map[string]interface{}{ "ProtobufMessageDef": "testdata/addressbook.proto", "ProtobufMessageType": "addressbook.AddressBook", @@ -435,10 +429,6 @@ func TestConfig_ParserInterfaceNewFormat(t *testing.T) { expected := make([]telegraf.Parser, 0, len(formats)) for _, format := range formats { formatCfg := &cfg - settings, hasOverride := override[format] - if hasOverride && settings.cfg != nil { - formatCfg = settings.cfg - } formatCfg.DataFormat = format logger := models.NewLogger("parsers", format, cfg.MetricName) @@ -555,7 +545,6 @@ func TestConfig_ParserInterfaceOldFormat(t *testing.T) { } override := map[string]struct { - cfg *parsers.Config param map[string]interface{} mask []string }{ @@ -569,11 +558,6 @@ func TestConfig_ParserInterfaceOldFormat(t *testing.T) { mask: []string{"Now"}, }, "xpath_protobuf": { - cfg: &parsers.Config{ - MetricName: "parser_test_new", - XPathProtobufFile: "testdata/addressbook.proto", - XPathProtobufType: "addressbook.AddressBook", - }, param: map[string]interface{}{ "ProtobufMessageDef": "testdata/addressbook.proto", "ProtobufMessageType": "addressbook.AddressBook", @@ -584,10 +568,6 @@ func TestConfig_ParserInterfaceOldFormat(t *testing.T) { expected := make([]telegraf.Parser, 0, len(formats)) for _, format := range formats { formatCfg := &cfg - settings, hasOverride := override[format] - if hasOverride && settings.cfg != nil { - formatCfg = settings.cfg - } formatCfg.DataFormat = format logger := models.NewLogger("parsers", format, cfg.MetricName) diff --git a/plugins/parsers/all/all.go b/plugins/parsers/all/all.go index 2284bf78a..69baf2af9 100644 --- a/plugins/parsers/all/all.go +++ b/plugins/parsers/all/all.go @@ -3,4 +3,5 @@ package all import ( //Blank imports for plugins to register themselves _ "github.com/influxdata/telegraf/plugins/parsers/csv" + _ "github.com/influxdata/telegraf/plugins/parsers/xpath" ) diff --git a/plugins/parsers/registry.go b/plugins/parsers/registry.go index 0be7579a7..e5c2671e4 100644 --- a/plugins/parsers/registry.go +++ b/plugins/parsers/registry.go @@ -19,7 +19,6 @@ import ( "github.com/influxdata/telegraf/plugins/parsers/prometheusremotewrite" "github.com/influxdata/telegraf/plugins/parsers/value" "github.com/influxdata/telegraf/plugins/parsers/wavefront" - "github.com/influxdata/telegraf/plugins/parsers/xpath" ) // Creator is the function to create a new parser @@ -184,12 +183,12 @@ type Config struct { ValueFieldName string `toml:"value_field_name"` // XPath configuration - XPathPrintDocument bool `toml:"xpath_print_document"` - XPathProtobufFile string `toml:"xpath_protobuf_file"` - XPathProtobufType string `toml:"xpath_protobuf_type"` - XPathProtobufImportPaths []string `toml:"xpath_protobuf_import_paths"` - XPathAllowEmptySelection bool `toml:"xpath_allow_empty_selection"` - XPathConfig []XPathConfig + XPathPrintDocument bool `toml:"xpath_print_document"` + XPathProtobufFile string `toml:"xpath_protobuf_file"` + XPathProtobufType string `toml:"xpath_protobuf_type"` + XPathProtobufImportPaths []string `toml:"xpath_protobuf_import_paths"` + XPathAllowEmptySelection bool `toml:"xpath_allow_empty_selection"` + XPathConfig []XPathConfig `toml:"xpath"` // JSONPath configuration JSONV2Config []JSONV2Config `toml:"json_v2"` @@ -201,7 +200,29 @@ type Config struct { LogFmtTagKeys []string `toml:"logfmt_tag_keys"` } -type XPathConfig xpath.Config +// XPathConfig definition for backward compatibitlity ONLY. +// We need this here to avoid cyclic dependencies. However, we need +// to move this to plugins/parsers/xpath once we deprecate parser +// construction via `NewParser()`. +type XPathConfig struct { + MetricQuery string `toml:"metric_name"` + Selection string `toml:"metric_selection"` + Timestamp string `toml:"timestamp"` + TimestampFmt string `toml:"timestamp_format"` + Tags map[string]string `toml:"tags"` + Fields map[string]string `toml:"fields"` + FieldsInt map[string]string `toml:"fields_int"` + + FieldSelection string `toml:"field_selection"` + FieldNameQuery string `toml:"field_name"` + FieldValueQuery string `toml:"field_value"` + FieldNameExpand bool `toml:"field_name_expansion"` + + TagSelection string `toml:"tag_selection"` + TagNameQuery string `toml:"tag_name"` + TagValueQuery string `toml:"tag_value"` + TagNameExpand bool `toml:"tag_name_expansion"` +} type JSONV2Config struct { json_v2.Config @@ -280,17 +301,6 @@ func NewParser(config *Config) (Parser, error) { ) case "prometheusremotewrite": parser, err = NewPrometheusRemoteWriteParser(config.DefaultTags) - case "xml", "xpath_json", "xpath_msgpack", "xpath_protobuf": - parser = &xpath.Parser{ - Format: config.DataFormat, - ProtobufMessageDef: config.XPathProtobufFile, - ProtobufMessageType: config.XPathProtobufType, - ProtobufImportPaths: config.XPathProtobufImportPaths, - PrintDocument: config.XPathPrintDocument, - DefaultTags: config.DefaultTags, - AllowEmptySelection: config.XPathAllowEmptySelection, - Configs: NewXPathParserConfigs(config.MetricName, config.XPathConfig), - } case "json_v2": parser, err = NewJSONPathParser(config.JSONV2Config) default: @@ -429,17 +439,6 @@ func NewPrometheusRemoteWriteParser(defaultTags map[string]string) (Parser, erro }, nil } -func NewXPathParserConfigs(metricName string, cfgs []XPathConfig) []xpath.Config { - // Convert the config formats which is a one-to-one copy - configs := make([]xpath.Config, 0, len(cfgs)) - for _, cfg := range cfgs { - config := xpath.Config(cfg) - config.MetricDefaultName = metricName - configs = append(configs, config) - } - return configs -} - func NewJSONPathParser(jsonv2config []JSONV2Config) (Parser, error) { configs := make([]json_v2.Config, len(jsonv2config)) for i, cfg := range jsonv2config { diff --git a/plugins/parsers/registry_test.go b/plugins/parsers/registry_test.go index 472ba92a8..20c94b013 100644 --- a/plugins/parsers/registry_test.go +++ b/plugins/parsers/registry_test.go @@ -2,8 +2,11 @@ package parsers_test import ( "reflect" + "sync" "testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/require" "github.com/influxdata/telegraf" @@ -15,6 +18,8 @@ func TestRegistry_BackwardCompatibility(t *testing.T) { cfg := &parsers.Config{ MetricName: "parser_compatibility_test", CSVHeaderRowCount: 42, + XPathProtobufFile: "xpath/testcases/protos/addressbook.proto", + XPathProtobufType: "addressbook.AddressBook", } // Some parsers need certain settings to not error. Furthermore, we @@ -29,6 +34,12 @@ func TestRegistry_BackwardCompatibility(t *testing.T) { }, mask: []string{"TimeFunc"}, }, + "xpath_protobuf": { + param: map[string]interface{}{ + "ProtobufMessageDef": cfg.XPathProtobufFile, + "ProtobufMessageType": cfg.XPathProtobufType, + }, + }, } for name, creator := range parsers.Parsers { @@ -52,19 +63,23 @@ func TestRegistry_BackwardCompatibility(t *testing.T) { actual, err := parsers.NewParser(cfg) require.NoError(t, err) - // Compare with mask - if settings, found := override[name]; found { - a := reflect.Indirect(reflect.ValueOf(actual)) - e := reflect.Indirect(reflect.ValueOf(expected)) - for _, key := range settings.mask { - af := a.FieldByName(key) - ef := e.FieldByName(key) - - v := reflect.Zero(ef.Type()) - af.Set(v) - ef.Set(v) - } + // Determine the underlying type of the parser + stype := reflect.Indirect(reflect.ValueOf(expected)).Interface() + // Ignore all unexported fields and fields not relevant for functionality + options := []cmp.Option{ + cmpopts.IgnoreUnexported(stype), + cmpopts.IgnoreTypes(sync.Mutex{}), + cmpopts.IgnoreInterfaces(struct{ telegraf.Logger }{}), } - require.EqualValuesf(t, expected, actual, "format %q", name) + + // Add overrides and masks to compare options + if settings, found := override[name]; found { + options = append(options, cmpopts.IgnoreFields(stype, settings.mask...)) + } + + // Do a manual comparision as require.EqualValues will also work on unexported fields + // that cannot be cleared or ignored. + diff := cmp.Diff(expected, actual, options...) + require.Emptyf(t, diff, "Difference for %q", name) } } diff --git a/plugins/parsers/xpath/parser.go b/plugins/parsers/xpath/parser.go index 1a6dfda76..2f3290dcc 100644 --- a/plugins/parsers/xpath/parser.go +++ b/plugins/parsers/xpath/parser.go @@ -1,6 +1,7 @@ package xpath import ( + "errors" "fmt" "strconv" "strings" @@ -11,6 +12,7 @@ import ( "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/metric" + "github.com/influxdata/telegraf/plugins/parsers" ) type dataNode interface{} @@ -24,39 +26,25 @@ type dataDocument interface { } type Parser struct { - Format string - ProtobufMessageDef string - ProtobufMessageType string - ProtobufImportPaths []string - PrintDocument bool - AllowEmptySelection bool - Configs []Config - DefaultTags map[string]string - Log telegraf.Logger + Format string `toml:"-"` + ProtobufMessageDef string `toml:"xpath_protobuf_file"` + ProtobufMessageType string `toml:"xpath_protobuf_type"` + ProtobufImportPaths []string `toml:"xpath_protobuf_import_paths"` + PrintDocument bool `toml:"xpath_print_document"` + AllowEmptySelection bool `toml:"xpath_allow_empty_selection"` + Configs []Config `toml:"xpath"` + DefaultMetricName string `toml:"-"` + DefaultTags map[string]string `toml:"-"` + Log telegraf.Logger `toml:"-"` document dataDocument } -type Config struct { - MetricDefaultName string `toml:"-"` - MetricQuery string `toml:"metric_name"` - Selection string `toml:"metric_selection"` - Timestamp string `toml:"timestamp"` - TimestampFmt string `toml:"timestamp_format"` - Tags map[string]string `toml:"tags"` - Fields map[string]string `toml:"fields"` - FieldsInt map[string]string `toml:"fields_int"` - - FieldSelection string `toml:"field_selection"` - FieldNameQuery string `toml:"field_name"` - FieldValueQuery string `toml:"field_value"` - FieldNameExpand bool `toml:"field_name_expansion"` - - TagSelection string `toml:"tag_selection"` - TagNameQuery string `toml:"tag_name"` - TagValueQuery string `toml:"tag_value"` - TagNameExpand bool `toml:"tag_name_expansion"` -} +// Config definition +// This should be replaced by the actual definition once +// the compatibitlity-code is removed. +// Please check plugins/parsers/registry.go for now. +type Config parsers.XPathConfig func (p *Parser) Init() error { switch p.Format { @@ -81,6 +69,11 @@ func (p *Parser) Init() error { return fmt.Errorf("unknown data-format %q for xpath parser", p.Format) } + // Make sure we do have a metric name + if p.DefaultMetricName == "" { + return errors.New("missing default metric name") + } + return nil } @@ -129,7 +122,6 @@ func (p *Parser) Parse(buf []byte) ([]telegraf.Metric, error) { } func (p *Parser) ParseLine(line string) (telegraf.Metric, error) { - metrics, err := p.Parse([]byte(line)) if err != nil { return nil, err @@ -155,7 +147,7 @@ func (p *Parser) parseQuery(starttime time.Time, doc, selected dataNode, config // Determine the metric name. If a query was specified, use the result of this query and the default metric name // otherwise. - metricname = config.MetricDefaultName + metricname = p.DefaultMetricName if len(config.MetricQuery) > 0 { v, err := p.executeQuery(doc, selected, config.MetricQuery) if err != nil { @@ -512,3 +504,62 @@ func (p *Parser) debugEmptyQuery(operation string, root dataNode, initialquery s } } } + +func init() { + // Register all variants + parsers.Add("xml", + func(defaultMetricName string) telegraf.Parser { + return &Parser{ + Format: "xml", + DefaultMetricName: defaultMetricName, + } + }, + ) + parsers.Add("xpath_json", + func(defaultMetricName string) telegraf.Parser { + return &Parser{ + Format: "xpath_json", + DefaultMetricName: defaultMetricName, + } + }, + ) + parsers.Add("xpath_msgpack", + func(defaultMetricName string) telegraf.Parser { + return &Parser{ + Format: "xpath_msgpack", + DefaultMetricName: defaultMetricName, + } + }, + ) + parsers.Add("xpath_protobuf", + func(defaultMetricName string) telegraf.Parser { + return &Parser{ + Format: "xpath_protobuf", + DefaultMetricName: defaultMetricName, + } + }, + ) +} + +// InitFromConfig is a compatibitlity function to construct the parser the old way +func (p *Parser) InitFromConfig(config *parsers.Config) error { + p.Format = config.DataFormat + if p.Format == "xpath_protobuf" { + p.ProtobufMessageDef = config.XPathProtobufFile + p.ProtobufMessageType = config.XPathProtobufType + } + p.PrintDocument = config.XPathPrintDocument + p.DefaultMetricName = config.MetricName + p.DefaultTags = config.DefaultTags + + // Convert the config formats which is a one-to-one copy + if len(config.XPathConfig) > 0 { + p.Configs = make([]Config, 0, len(config.XPathConfig)) + for _, cfg := range config.XPathConfig { + config := Config(cfg) + p.Configs = append(p.Configs, config) + } + } + + return p.Init() +} diff --git a/plugins/parsers/xpath/parser_test.go b/plugins/parsers/xpath/parser_test.go index ec8fda00f..c85a3fef0 100644 --- a/plugins/parsers/xpath/parser_test.go +++ b/plugins/parsers/xpath/parser_test.go @@ -126,7 +126,12 @@ func TestParseInvalidXML(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}} + parser := &Parser{ + DefaultMetricName: "xml", + Configs: tt.configs, + DefaultTags: tt.defaultTags, + Log: testutil.Logger{Name: "parsers.xml"}, + } require.NoError(t, parser.Init()) _, err := parser.ParseLine(tt.input) @@ -149,8 +154,7 @@ func TestInvalidTypeQueriesFail(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix", + Timestamp: "/Device_1/Timestamp_unix", FieldsInt: map[string]string{ "a": "/Device_1/value_string", }, @@ -163,7 +167,12 @@ func TestInvalidTypeQueriesFail(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}} + parser := &Parser{ + DefaultMetricName: "xml", + Configs: tt.configs, + DefaultTags: tt.defaultTags, + Log: testutil.Logger{Name: "parsers.xml"}, + } require.NoError(t, parser.Init()) _, err := parser.ParseLine(tt.input) @@ -186,8 +195,7 @@ func TestInvalidTypeQueries(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix", + Timestamp: "/Device_1/Timestamp_unix", Fields: map[string]string{ "a": "number(/Device_1/value_string)", }, @@ -208,8 +216,7 @@ func TestInvalidTypeQueries(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix", + Timestamp: "/Device_1/Timestamp_unix", Fields: map[string]string{ "a": "boolean(/Device_1/value_string)", }, @@ -229,7 +236,12 @@ func TestInvalidTypeQueries(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}} + parser := &Parser{ + DefaultMetricName: "test", + Configs: tt.configs, + DefaultTags: tt.defaultTags, + Log: testutil.Logger{Name: "parsers.xml"}, + } require.NoError(t, parser.Init()) actual, err := parser.ParseLine(tt.input) @@ -253,8 +265,7 @@ func TestParseTimestamps(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix", + Timestamp: "/Device_1/Timestamp_unix", }, }, defaultTags: map[string]string{}, @@ -270,9 +281,8 @@ func TestParseTimestamps(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix", - TimestampFmt: "unix", + Timestamp: "/Device_1/Timestamp_unix", + TimestampFmt: "unix", }, }, defaultTags: map[string]string{}, @@ -288,9 +298,8 @@ func TestParseTimestamps(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix_ms", - TimestampFmt: "unix_ms", + Timestamp: "/Device_1/Timestamp_unix_ms", + TimestampFmt: "unix_ms", }, }, defaultTags: map[string]string{}, @@ -306,9 +315,8 @@ func TestParseTimestamps(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix_us", - TimestampFmt: "unix_us", + Timestamp: "/Device_1/Timestamp_unix_us", + TimestampFmt: "unix_us", }, }, defaultTags: map[string]string{}, @@ -324,9 +332,8 @@ func TestParseTimestamps(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix_ns", - TimestampFmt: "unix_ns", + Timestamp: "/Device_1/Timestamp_unix_ns", + TimestampFmt: "unix_ns", }, }, defaultTags: map[string]string{}, @@ -342,9 +349,8 @@ func TestParseTimestamps(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_iso", - TimestampFmt: "2006-01-02T15:04:05Z", + Timestamp: "/Device_1/Timestamp_iso", + TimestampFmt: "2006-01-02T15:04:05Z", }, }, defaultTags: map[string]string{}, @@ -359,7 +365,12 @@ func TestParseTimestamps(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}} + parser := &Parser{ + DefaultMetricName: "test", + Configs: tt.configs, + DefaultTags: tt.defaultTags, + Log: testutil.Logger{Name: "parsers.xml"}, + } require.NoError(t, parser.Init()) actual, err := parser.ParseLine(tt.input) @@ -383,8 +394,7 @@ func TestParseSingleValues(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix", + Timestamp: "/Device_1/Timestamp_unix", Fields: map[string]string{ "a": "/Device_1/value_int", "b": "/Device_1/value_float", @@ -411,8 +421,7 @@ func TestParseSingleValues(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix", + Timestamp: "/Device_1/Timestamp_unix", Fields: map[string]string{ "a": "number(Device_1/value_int)", "b": "number(/Device_1/value_float)", @@ -439,8 +448,7 @@ func TestParseSingleValues(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix", + Timestamp: "/Device_1/Timestamp_unix", Fields: map[string]string{ "b": "number(/Device_1/value_float)", "c": "boolean(/Device_1/value_bool)", @@ -469,8 +477,7 @@ func TestParseSingleValues(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix", + Timestamp: "/Device_1/Timestamp_unix", Fields: map[string]string{ "x": "substring-before(/Device_1/value_position, ';')", "y": "substring-after(/Device_1/value_position, ';')", @@ -493,8 +500,7 @@ func TestParseSingleValues(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix", + Timestamp: "/Device_1/Timestamp_unix", Fields: map[string]string{ "x": "number(substring-before(/Device_1/value_position, ';'))", "y": "number(substring-after(/Device_1/value_position, ';'))", @@ -517,8 +523,7 @@ func TestParseSingleValues(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix", + Timestamp: "/Device_1/Timestamp_unix", FieldsInt: map[string]string{ "x": "substring-before(/Device_1/value_position, ';')", "y": "substring-after(/Device_1/value_position, ';')", @@ -541,8 +546,7 @@ func TestParseSingleValues(t *testing.T) { input: singleMetricValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix", + Timestamp: "/Device_1/Timestamp_unix", Tags: map[string]string{ "state": "/Device_1/State", "name": "substring-after(/Device_1/Name, ' ')", @@ -564,7 +568,12 @@ func TestParseSingleValues(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}} + parser := &Parser{ + DefaultMetricName: "test", + Configs: tt.configs, + DefaultTags: tt.defaultTags, + Log: testutil.Logger{Name: "parsers.xml"}, + } require.NoError(t, parser.Init()) actual, err := parser.ParseLine(tt.input) @@ -588,8 +597,7 @@ func TestParseSingleAttributes(t *testing.T) { input: singleMetricAttributesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix/@value", + Timestamp: "/Device_1/Timestamp_unix/@value", }, }, defaultTags: map[string]string{}, @@ -605,9 +613,8 @@ func TestParseSingleAttributes(t *testing.T) { input: singleMetricAttributesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_iso/@value", - TimestampFmt: "2006-01-02T15:04:05Z", + Timestamp: "/Device_1/Timestamp_iso/@value", + TimestampFmt: "2006-01-02T15:04:05Z", }, }, defaultTags: map[string]string{}, @@ -623,8 +630,7 @@ func TestParseSingleAttributes(t *testing.T) { input: singleMetricAttributesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix/@value", + Timestamp: "/Device_1/Timestamp_unix/@value", Fields: map[string]string{ "a": "/Device_1/attr_int/@_", "b": "/Device_1/attr_float/@_", @@ -651,8 +657,7 @@ func TestParseSingleAttributes(t *testing.T) { input: singleMetricAttributesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix/@value", + Timestamp: "/Device_1/Timestamp_unix/@value", Fields: map[string]string{ "a": "number(/Device_1/attr_int/@_)", "b": "number(/Device_1/attr_float/@_)", @@ -679,8 +684,7 @@ func TestParseSingleAttributes(t *testing.T) { input: singleMetricAttributesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix/@value", + Timestamp: "/Device_1/Timestamp_unix/@value", Fields: map[string]string{ "b": "number(/Device_1/attr_float/@_)", "c": "boolean(/Device_1/attr_bool/@_)", @@ -709,8 +713,7 @@ func TestParseSingleAttributes(t *testing.T) { input: singleMetricAttributesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix/@value", + Timestamp: "/Device_1/Timestamp_unix/@value", Fields: map[string]string{ "name": "substring-after(/Device_1/Name/@value, ' ')", }, @@ -731,8 +734,7 @@ func TestParseSingleAttributes(t *testing.T) { input: singleMetricAttributesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix/@value", + Timestamp: "/Device_1/Timestamp_unix/@value", Tags: map[string]string{ "state": "/Device_1/State/@_", "name": "substring-after(/Device_1/Name/@value, ' ')", @@ -755,8 +757,7 @@ func TestParseSingleAttributes(t *testing.T) { input: singleMetricAttributesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Device_1/Timestamp_unix/@value", + Timestamp: "/Device_1/Timestamp_unix/@value", Fields: map[string]string{ "a": "/Device_1/attr_bool_numeric/@_ = 1", }, @@ -776,7 +777,12 @@ func TestParseSingleAttributes(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}} + parser := &Parser{ + DefaultMetricName: "test", + Configs: tt.configs, + DefaultTags: tt.defaultTags, + Log: testutil.Logger{Name: "parsers.xml"}, + } require.NoError(t, parser.Init()) actual, err := parser.ParseLine(tt.input) @@ -800,8 +806,7 @@ func TestParseMultiValues(t *testing.T) { input: singleMetricMultiValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Timestamp/@value", + Timestamp: "/Timestamp/@value", Fields: map[string]string{ "a": "number(/Device/Value[1])", "b": "number(/Device/Value[2])", @@ -832,8 +837,7 @@ func TestParseMultiValues(t *testing.T) { input: singleMetricMultiValuesXML, configs: []Config{ { - MetricDefaultName: "test", - Timestamp: "/Timestamp/@value", + Timestamp: "/Timestamp/@value", FieldsInt: map[string]string{ "a": "/Device/Value[1]", "b": "/Device/Value[2]", @@ -863,7 +867,12 @@ func TestParseMultiValues(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}} + parser := &Parser{ + DefaultMetricName: "test", + Configs: tt.configs, + DefaultTags: tt.defaultTags, + Log: testutil.Logger{Name: "parsers.xml"}, + } require.NoError(t, parser.Init()) actual, err := parser.ParseLine(tt.input) @@ -887,9 +896,8 @@ func TestParseMultiNodes(t *testing.T) { input: multipleNodesXML, configs: []Config{ { - MetricDefaultName: "test", - Selection: "/Device", - Timestamp: "/Timestamp/@value", + Selection: "/Device", + Timestamp: "/Timestamp/@value", Fields: map[string]string{ "value": "number(Value)", "active": "Active = 1", @@ -976,7 +984,12 @@ func TestParseMultiNodes(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}} + parser := &Parser{ + DefaultMetricName: "test", + Configs: tt.configs, + DefaultTags: tt.defaultTags, + Log: testutil.Logger{Name: "parsers.xml"}, + } require.NoError(t, parser.Init()) actual, err := parser.Parse([]byte(tt.input)) @@ -1000,9 +1013,8 @@ func TestParseMetricQuery(t *testing.T) { input: metricNameQueryXML, configs: []Config{ { - MetricDefaultName: "test", - MetricQuery: "name(/Device_1/Metric/@*[1])", - Timestamp: "/Device_1/Timestamp_unix", + MetricQuery: "name(/Device_1/Metric/@*[1])", + Timestamp: "/Device_1/Timestamp_unix", Fields: map[string]string{ "value": "/Device_1/Metric/@*[1]", }, @@ -1023,9 +1035,8 @@ func TestParseMetricQuery(t *testing.T) { input: metricNameQueryXML, configs: []Config{ { - MetricDefaultName: "test", - MetricQuery: "'the_metric'", - Timestamp: "/Device_1/Timestamp_unix", + MetricQuery: "'the_metric'", + Timestamp: "/Device_1/Timestamp_unix", Fields: map[string]string{ "value": "/Device_1/Metric/@*[1]", }, @@ -1045,7 +1056,12 @@ func TestParseMetricQuery(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}} + parser := &Parser{ + DefaultMetricName: "test", + Configs: tt.configs, + DefaultTags: tt.defaultTags, + Log: testutil.Logger{Name: "parsers.xml"}, + } require.NoError(t, parser.Init()) actual, err := parser.ParseLine(tt.input) @@ -1068,9 +1084,8 @@ func TestParseErrors(t *testing.T) { input: metricNameQueryXML, configs: []Config{ { - MetricDefaultName: "test", - MetricQuery: "arbitrary", - Timestamp: "/Device_1/Timestamp_unix", + MetricQuery: "arbitrary", + Timestamp: "/Device_1/Timestamp_unix", Fields: map[string]string{ "value": "/Device_1/Metric/@*[1]", }, @@ -1082,7 +1097,12 @@ func TestParseErrors(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parser := &Parser{Configs: tt.configs, DefaultTags: map[string]string{}, Log: testutil.Logger{Name: "parsers.xml"}} + parser := &Parser{ + DefaultMetricName: "test", + Configs: tt.configs, + DefaultTags: map[string]string{}, + Log: testutil.Logger{Name: "parsers.xml"}, + } require.NoError(t, parser.Init()) _, err := parser.ParseLine(tt.input) @@ -1150,12 +1170,17 @@ func TestEmptySelection(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parser := &Parser{Configs: tt.configs, DefaultTags: map[string]string{}, Log: testutil.Logger{Name: "parsers.xml"}} + parser := &Parser{ + DefaultMetricName: "test", + Configs: tt.configs, + DefaultTags: map[string]string{}, + Log: testutil.Logger{Name: "parsers.xml"}, + } require.NoError(t, parser.Init()) _, err := parser.Parse([]byte(tt.input)) require.Error(t, err) - require.Equal(t, "cannot parse with empty selection node", err.Error()) + require.Equal(t, err.Error(), "cannot parse with empty selection node") }) } } @@ -1218,7 +1243,13 @@ func TestEmptySelectionAllowed(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parser := &Parser{Configs: tt.configs, AllowEmptySelection: true, DefaultTags: map[string]string{}, Log: testutil.Logger{Name: "parsers.xml"}} + parser := &Parser{ + DefaultMetricName: "xml", + Configs: tt.configs, + AllowEmptySelection: true, + DefaultTags: map[string]string{}, + Log: testutil.Logger{Name: "parsers.xml"}, + } require.NoError(t, parser.Init()) _, err := parser.Parse([]byte(tt.input)) @@ -1277,7 +1308,6 @@ func TestTestCases(t *testing.T) { filename := filepath.FromSlash(tt.filename) cfg, header, err := loadTestConfiguration(filename) require.NoError(t, err) - cfg.MetricDefaultName = "xml" // Load the xml-content input, err := testutil.ParseRawLinesFrom(header, "File:") @@ -1315,7 +1345,12 @@ func TestTestCases(t *testing.T) { expectedErrors, _ := testutil.ParseRawLinesFrom(header, "Expected Error:") // Setup the parser and run it. + metricName := "xml" + if fileformat != "" { + metricName = fileformat + } parser := &Parser{ + DefaultMetricName: metricName, Format: fileformat, ProtobufMessageDef: pbmsgdef, ProtobufMessageType: pbmsgtype, @@ -1340,6 +1375,7 @@ func TestTestCases(t *testing.T) { func TestProtobufImporting(t *testing.T) { // Setup the parser and run it. parser := &Parser{ + DefaultMetricName: "xpath_protobuf", Format: "xpath_protobuf", ProtobufMessageDef: "person.proto", ProtobufMessageType: "importtest.Person",