From b47914d80f6845f651fa051b8f4d35ac6f11bf17 Mon Sep 17 00:00:00 2001 From: andig Date: Sat, 9 Oct 2021 10:36:52 +0200 Subject: [PATCH] Move optional provider params to separate option functions --- meter/discovergy.go | 4 +- provider/http.go | 81 +++++++++++++++++++------------- provider/mqtt.go | 6 +-- provider/script.go | 110 +++++++++++++++++++++++++------------------- 4 files changed, 115 insertions(+), 86 deletions(-) diff --git a/meter/discovergy.go b/meter/discovergy.go index f38c4321c..52417eead 100644 --- a/meter/discovergy.go +++ b/meter/discovergy.go @@ -67,9 +67,9 @@ func NewDiscovergyFromConfig(other map[string]interface{}) (api.Meter, error) { } uri := fmt.Sprintf("%s/last_reading?meterId=%s", discovergyAPI, meterID) - power, err := provider.NewHTTP(log, http.MethodGet, uri, nil, "", false, "", ".values.power", 0.001*cc.Scale, 0) + power, err := provider.NewHTTP(log, http.MethodGet, uri, false, 0.001*cc.Scale, 0).WithAuth("basic", cc.User, cc.Password) if err == nil { - _, err = power.WithAuth("basic", cc.User, cc.Password) + _, err = power.WithJq(".values.power") } if err != nil { return nil, err diff --git a/provider/http.go b/provider/http.go index d7f67b144..6446eb303 100644 --- a/provider/http.go +++ b/provider/http.go @@ -64,46 +64,45 @@ func NewHTTPProviderFromConfig(other map[string]interface{}) (IntProvider, error return nil, err } - log := util.NewLogger("http") - - http, err := NewHTTP(log, + http := NewHTTP( + util.NewLogger("http"), cc.Method, cc.URI, - cc.Headers, - cc.Body, cc.Insecure, - cc.Regex, - cc.Jq, cc.Scale, cc.Cache, - ) + ).WithHeaders(cc.Headers).WithBody(cc.Body) + http.Client.Timeout = cc.Timeout + + var err error + if err == nil && cc.Regex != "" { + _, err = http.WithRegex(cc.Regex) + } + + if err == nil && cc.Jq != "" { + _, err = http.WithJq(cc.Jq) + } if err == nil && cc.Auth.Type != "" { _, err = http.WithAuth(cc.Auth.Type, cc.Auth.User, cc.Auth.Password) } - if err == nil { - http.Client.Timeout = cc.Timeout - } - return http, err } // NewHTTP create HTTP provider -func NewHTTP(log *util.Logger, method, uri string, headers map[string]string, body string, insecure bool, regex, jq string, scale float64, cache time.Duration) (*HTTP, error) { +func NewHTTP(log *util.Logger, method, uri string, insecure bool, scale float64, cache time.Duration) *HTTP { url := util.DefaultScheme(uri, "http") if url != uri { log.WARN.Printf("missing scheme for %s, assuming http", uri) } p := &HTTP{ - Helper: request.NewHelper(log), - url: url, - method: method, - headers: headers, - body: body, - scale: scale, - cache: cache, + Helper: request.NewHelper(log), + url: url, + method: method, + scale: scale, + cache: cache, } // ignore the self signed certificate @@ -111,24 +110,42 @@ func NewHTTP(log *util.Logger, method, uri string, headers map[string]string, bo p.Client.Transport = request.NewTripper(log, request.InsecureTransport()) } - if regex != "" { - re, err := regexp.Compile(regex) - if err != nil { - return nil, fmt.Errorf("invalid regex '%s': %w", re, err) - } + return p +} - p.re = re +// WithBody adds request body +func (p *HTTP) WithBody(body string) *HTTP { + p.body = body + return p +} + +// WithHeaders adds request headers +func (p *HTTP) WithHeaders(headers map[string]string) *HTTP { + p.headers = headers + return p +} + +// WithRegex adds a regex query applied to the mqtt listener payload +func (p *HTTP) WithRegex(regex string) (*HTTP, error) { + re, err := regexp.Compile(regex) + if err != nil { + return nil, fmt.Errorf("invalid regex '%s': %w", re, err) } - if jq != "" { - op, err := gojq.Parse(jq) - if err != nil { - return nil, fmt.Errorf("invalid jq query '%s': %w", jq, err) - } + p.re = re - p.jq = op + return p, nil +} + +// WithJq adds a jq query applied to the mqtt listener payload +func (p *HTTP) WithJq(jq string) (*HTTP, error) { + op, err := gojq.Parse(jq) + if err != nil { + return nil, fmt.Errorf("invalid jq query '%s': %w", jq, err) } + p.jq = op + return p, nil } diff --git a/provider/mqtt.go b/provider/mqtt.go index 216fd5328..e35ebba06 100644 --- a/provider/mqtt.go +++ b/provider/mqtt.go @@ -53,16 +53,14 @@ func NewMqttFromConfig(other map[string]interface{}) (IntProvider, error) { return nil, err } - m := NewMqtt(log, client, cc.Topic, cc.Scale, cc.Timeout) + m := NewMqtt(log, client, cc.Topic, cc.Scale, cc.Timeout).WithPayload(cc.Payload) - if cc.Payload != "" { - m = m.WithPayload(cc.Payload) - } if cc.Regex != "" { if m, err = m.WithRegex(cc.Regex); err != nil { return nil, err } } + if cc.Jq != "" { if m, err = m.WithJq(cc.Jq); err != nil { return nil, err diff --git a/provider/script.go b/provider/script.go index 3a0777941..e84caa8dd 100644 --- a/provider/script.go +++ b/provider/script.go @@ -54,12 +54,22 @@ func NewScriptProviderFromConfig(other map[string]interface{}) (IntProvider, err return nil, err } - return NewScriptProvider(cc.Cmd, cc.Timeout, cc.Regex, cc.Jq, cc.Scale, cc.Cache) + p, err := NewScriptProvider(cc.Cmd, cc.Timeout, cc.Scale, cc.Cache) + + if err == nil && cc.Regex != "" { + _, err = p.WithRegex(cc.Regex) + } + + if err == nil && cc.Jq != "" { + _, err = p.WithJq(cc.Jq) + } + + return p, err } // NewScriptProvider creates a script provider. // Script execution is aborted after given timeout. -func NewScriptProvider(script string, timeout time.Duration, regex, jq string, scale float64, cache time.Duration) (*Script, error) { +func NewScriptProvider(script string, timeout time.Duration, scale float64, cache time.Duration) (*Script, error) { s := &Script{ log: util.NewLogger("script"), script: script, @@ -68,34 +78,38 @@ func NewScriptProvider(script string, timeout time.Duration, regex, jq string, s cache: cache, } - if regex != "" { - re, err := regexp.Compile(regex) - if err != nil { - return nil, fmt.Errorf("invalid regex '%s': %w", re, err) - } - - s.re = re - } - - if jq != "" { - op, err := gojq.Parse(jq) - if err != nil { - return nil, fmt.Errorf("invalid jq query '%s': %w", jq, err) - } - - s.jq = op - } - return s, nil } -func (e *Script) exec(script string) (string, error) { +func (p *Script) WithRegex(regex string) (*Script, error) { + re, err := regexp.Compile(regex) + if err != nil { + return nil, fmt.Errorf("invalid regex '%s': %w", re, err) + } + + p.re = re + + return p, nil +} + +func (p *Script) WithJq(jq string) (*Script, error) { + op, err := gojq.Parse(jq) + if err != nil { + return nil, fmt.Errorf("invalid jq query '%s': %w", jq, err) + } + + p.jq = op + + return p, nil +} + +func (p *Script) exec(script string) (string, error) { args, err := shellquote.Split(script) if err != nil { return "", err } - ctx, cancel := context.WithTimeout(context.Background(), e.timeout) + ctx, cancel := context.WithTimeout(context.Background(), p.timeout) defer cancel() cmd := exec.CommandContext(ctx, args[0], args[1:]...) @@ -110,44 +124,44 @@ func (e *Script) exec(script string) (string, error) { s = strings.TrimSpace(string(ee.Stderr)) } - e.log.ERROR.Printf("%s: %s", strings.Join(args, " "), s) + p.log.ERROR.Printf("%s: %s", strings.Join(args, " "), s) return "", err } - e.log.DEBUG.Printf("%s: %s", strings.Join(args, " "), s) + p.log.DEBUG.Printf("%s: %s", strings.Join(args, " "), s) return s, nil } // StringGetter returns string from exec result. Only STDOUT is considered. -func (e *Script) StringGetter() func() (string, error) { +func (p *Script) StringGetter() func() (string, error) { return func() (string, error) { - if time.Since(e.updated) > e.cache { - e.val, e.err = e.exec(e.script) - e.updated = time.Now() + if time.Since(p.updated) > p.cache { + p.val, p.err = p.exec(p.script) + p.updated = time.Now() - if e.err == nil && e.re != nil { - m := e.re.FindStringSubmatch(e.val) + if p.err == nil && p.re != nil { + m := p.re.FindStringSubmatch(p.val) if len(m) > 1 { - e.val = m[1] // first submatch + p.val = m[1] // first submatch } } - if e.err == nil && e.jq != nil { + if p.err == nil && p.jq != nil { var v interface{} - if v, e.err = jq.Query(e.jq, []byte(e.val)); e.err == nil { - e.val = fmt.Sprintf("%v", v) + if v, p.err = jq.Query(p.jq, []byte(p.val)); p.err == nil { + p.val = fmt.Sprintf("%v", v) } } } - return e.val, e.err + return p.val, p.err } } // FloatGetter parses float from exec result -func (e *Script) FloatGetter() func() (float64, error) { - g := e.StringGetter() +func (p *Script) FloatGetter() func() (float64, error) { + g := p.StringGetter() return func() (float64, error) { s, err := g() @@ -157,7 +171,7 @@ func (e *Script) FloatGetter() func() (float64, error) { f, err := strconv.ParseFloat(s, 64) if err == nil { - f *= e.scale + f *= p.scale } return f, err @@ -165,8 +179,8 @@ func (e *Script) FloatGetter() func() (float64, error) { } // IntGetter parses int64 from exec result -func (e *Script) IntGetter() func() (int64, error) { - g := e.FloatGetter() +func (p *Script) IntGetter() func() (int64, error) { + g := p.FloatGetter() return func() (int64, error) { f, err := g() @@ -175,8 +189,8 @@ func (e *Script) IntGetter() func() (int64, error) { } // BoolGetter parses bool from exec result. "on", "true" and 1 are considered truish. -func (e *Script) BoolGetter() func() (bool, error) { - g := e.StringGetter() +func (p *Script) BoolGetter() func() (bool, error) { + g := p.StringGetter() return func() (bool, error) { s, err := g() @@ -189,15 +203,15 @@ func (e *Script) BoolGetter() func() (bool, error) { } // IntSetter invokes script with parameter replaced by int value -func (e *Script) IntSetter(param string) func(int64) error { +func (p *Script) IntSetter(param string) func(int64) error { // return func to access cached value return func(i int64) error { - cmd, err := util.ReplaceFormatted(e.script, map[string]interface{}{ + cmd, err := util.ReplaceFormatted(p.script, map[string]interface{}{ param: i, }) if err == nil { - _, err = e.exec(cmd) + _, err = p.exec(cmd) } return err @@ -205,15 +219,15 @@ func (e *Script) IntSetter(param string) func(int64) error { } // BoolSetter invokes script with parameter replaced by bool value -func (e *Script) BoolSetter(param string) func(bool) error { +func (p *Script) BoolSetter(param string) func(bool) error { // return func to access cached value return func(b bool) error { - cmd, err := util.ReplaceFormatted(e.script, map[string]interface{}{ + cmd, err := util.ReplaceFormatted(p.script, map[string]interface{}{ param: b, }) if err == nil { - _, err = e.exec(cmd) + _, err = p.exec(cmd) } return err