Move optional provider params to separate option functions

This commit is contained in:
andig 2021-10-09 10:36:52 +02:00
parent e019112489
commit b47914d80f
4 changed files with 115 additions and 86 deletions

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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