Skip to content

Commit

Permalink
Fix unhelpful error message for node evaluators
Browse files Browse the repository at this point in the history
  • Loading branch information
desa committed Aug 1, 2017
1 parent 97cc629 commit c588b48
Show file tree
Hide file tree
Showing 16 changed files with 143 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- [#1400](https://github.com/influxdata/kapacitor/issues/1400): Allow for `.yml` file extensions in `define-topic-handler`
- [#1402](https://github.com/influxdata/kapacitor/pull/1402): Fix http server error logging.
- [#1500](https://github.com/influxdata/kapacitor/pull/1500): Fix bugs with stopping running UDF agent.
- [#1470](https://github.com/influxdata/kapacitor/pull/1470): Fix error messages for missing fields which are arguments to functions are not clear

## v1.3.1 [2017-06-02]

Expand Down
1 change: 1 addition & 0 deletions tick/ast/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ func newBool(p position, text string, c *CommentNode) (*BoolNode, error) {
func (n *BoolNode) String() string {
return fmt.Sprintf("BoolNode@%v{%v}%v", n.position, n.Bool, n.Comment)
}

func (n *BoolNode) Format(buf *bytes.Buffer, indent string, onNewLine bool) {
if n.Comment != nil {
n.Comment.Format(buf, indent, onNewLine)
Expand Down
5 changes: 5 additions & 0 deletions tick/stateful/eval_binary_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ func NewEvalBinaryNode(node *ast.BinaryNode) (*EvalBinaryNode, error) {
return b, nil
}

func (n *EvalBinaryNode) String() string {

return fmt.Sprintf("%s %s %s", n.leftEvaluator, n.operator, n.rightEvaluator)
}

func (n *EvalBinaryNode) Type(scope ReadOnlyScope) (ast.ValueType, error) {
if n.constReturnType == ast.InvalidType {
var err error
Expand Down
5 changes: 5 additions & 0 deletions tick/stateful/eval_bool_node.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package stateful

import (
"fmt"
"regexp"
"time"

Expand All @@ -11,6 +12,10 @@ type EvalBoolNode struct {
Node *ast.BoolNode
}

func (n *EvalBoolNode) String() string {
return fmt.Sprintf("%v", n.Node.Bool)
}

func (n *EvalBoolNode) Type(scope ReadOnlyScope) (ast.ValueType, error) {
return ast.TBool, nil
}
Expand Down
5 changes: 5 additions & 0 deletions tick/stateful/eval_duration_node.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package stateful

import (
"fmt"
"regexp"
"time"

Expand All @@ -11,6 +12,10 @@ type EvalDurationNode struct {
Duration time.Duration
}

func (n *EvalDurationNode) String() string {
return fmt.Sprintf("%v", n.Duration)
}

func (n *EvalDurationNode) Type(scope ReadOnlyScope) (ast.ValueType, error) {
return ast.TDuration, nil
}
Expand Down
5 changes: 5 additions & 0 deletions tick/stateful/eval_float_node.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package stateful

import (
"fmt"
"regexp"
"time"

Expand All @@ -11,6 +12,10 @@ type EvalFloatNode struct {
Float64 float64
}

func (n *EvalFloatNode) String() string {
return fmt.Sprintf("%v", n.Float64)
}

func (n *EvalFloatNode) Type(scope ReadOnlyScope) (ast.ValueType, error) {
return ast.TFloat, nil
}
Expand Down
35 changes: 33 additions & 2 deletions tick/stateful/eval_function_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/influxdata/kapacitor/tick/ast"
"github.com/pkg/errors"
)

type EvalFunctionNode struct {
Expand All @@ -32,6 +33,15 @@ func NewEvalFunctionNode(funcNode *ast.FunctionNode) (*EvalFunctionNode, error)
return evalFuncNode, nil
}

func (n *EvalFunctionNode) String() string {
args := []string{}
for _, argEvaluator := range n.argsEvaluators {
args = append(args, fmt.Sprintf("%s", argEvaluator))
}

return n.funcName + "(" + strings.Join(args, ", ") + ")"
}

func (n *EvalFunctionNode) Type(scope ReadOnlyScope) (ast.ValueType, error) {
f := lookupFunc(n.funcName, builtinFuncs, scope)
if f == nil {
Expand All @@ -49,12 +59,33 @@ func (n *EvalFunctionNode) Type(scope ReadOnlyScope) (ast.ValueType, error) {
}

if gotLen, expLen := len(n.argsEvaluators), len(domain); gotLen > expLen {
return ast.InvalidType, ErrWrongFuncSignature{Name: n.funcName, DomainProvided: domain, Func: f}
err := ErrWrongFuncSignature{Name: n.funcName, DomainProvided: domain, Func: f}
return ast.InvalidType, errors.Wrapf(err, "too many arguments provided")
}

retType, ok := signature[domain]
if !ok {
return ast.InvalidType, ErrWrongFuncSignature{Name: n.funcName, DomainProvided: domain, Func: f}
args := []string{}
missing := []string{}
for i, argEvaluator := range n.argsEvaluators {
t, err := argEvaluator.Type(scope)
if err != nil {
// This should never happen
return ast.InvalidType, fmt.Errorf("Failed to handle %v argument: %v", i+1, err)
}

if t == ast.TMissing {
missing = append(missing, fmt.Sprintf("%s", argEvaluator))
}

args = append(args, fmt.Sprintf("%s", argEvaluator))
}

if len(missing) > 0 {
return ast.InvalidType, ErrMissingType{Name: n.funcName, Args: missing, Scope: scope.References()}
}

return ast.InvalidType, ErrWrongFuncSignature{Name: n.funcName, ArgLiterals: args, DomainProvided: domain, Func: f}
}

return retType, nil
Expand Down
5 changes: 5 additions & 0 deletions tick/stateful/eval_int_node.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package stateful

import (
"fmt"
"regexp"
"time"

Expand All @@ -11,6 +12,10 @@ type EvalIntNode struct {
Int64 int64
}

func (n *EvalIntNode) String() string {
return fmt.Sprintf("%v", n.Int64)
}

func (n *EvalIntNode) Type(scope ReadOnlyScope) (ast.ValueType, error) {
return ast.TInt, nil
}
Expand Down
4 changes: 4 additions & 0 deletions tick/stateful/eval_lambda_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ func NewEvalLambdaNode(lambda *ast.LambdaNode) (*EvalLambdaNode, error) {
}, nil
}

func (n *EvalLambdaNode) String() string {
return fmt.Sprintf("%s", n.nodeEvaluator)
}

func (n *EvalLambdaNode) Type(scope ReadOnlyScope) (ast.ValueType, error) {
if n.constReturnType == ast.InvalidType {
// We are dynamic and we need to figure out our type
Expand Down
4 changes: 4 additions & 0 deletions tick/stateful/eval_reference_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ func (n *EvalReferenceNode) getReferenceValue(scope *Scope) (interface{}, error)
return value, nil
}

func (n *EvalReferenceNode) String() string {
return "\"" + n.Node.Reference + "\""
}

func (n *EvalReferenceNode) Type(scope ReadOnlyScope) (ast.ValueType, error) {
value, err := n.getReferenceValue(scope.(*Scope))
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions tick/stateful/eval_regex_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ type EvalRegexNode struct {
Node *ast.RegexNode
}

func (n *EvalRegexNode) String() string {
return n.Node.Literal
}

func (n *EvalRegexNode) Type(scope ReadOnlyScope) (ast.ValueType, error) {
return ast.TRegex, nil
}
Expand Down
4 changes: 4 additions & 0 deletions tick/stateful/eval_string_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ type EvalStringNode struct {
Node *ast.StringNode
}

func (n *EvalStringNode) String() string {
return n.Node.Literal
}

func (n *EvalStringNode) Type(scope ReadOnlyScope) (ast.ValueType, error) {
return ast.TString, nil
}
Expand Down
6 changes: 6 additions & 0 deletions tick/stateful/eval_unary_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
)

type EvalUnaryNode struct {
operator ast.TokenType
nodeEvaluator NodeEvaluator
constReturnType ast.ValueType
}
Expand All @@ -24,6 +25,7 @@ func NewEvalUnaryNode(unaryNode *ast.UnaryNode) (*EvalUnaryNode, error) {
}

return &EvalUnaryNode{
operator: unaryNode.Operator,
nodeEvaluator: nodeEvaluator,
constReturnType: getConstantNodeType(unaryNode),
}, nil
Expand All @@ -33,6 +35,10 @@ func isValidUnaryOperator(operator ast.TokenType) bool {
return operator == ast.TokenNot || operator == ast.TokenMinus
}

func (n *EvalUnaryNode) String() string {
return fmt.Sprintf("%s%s", n.operator, n.nodeEvaluator)
}

func (n *EvalUnaryNode) Type(scope ReadOnlyScope) (ast.ValueType, error) {
if n.constReturnType == ast.InvalidType {
// We are dynamic and we need to figure out our type
Expand Down
54 changes: 52 additions & 2 deletions tick/stateful/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,65 @@ const (
maxArgs = 4
)

type ErrMissingType struct {
Name string
Args []string
Scope []string
}

func (e ErrMissingType) Error() string {
s := "Cannot call function \"%s\" argument %s is missing, values in scope are [%s]"
if len(e.Args) > 1 {
s = "Cannot call function \"%s\" arguments %s are missing, values in scope are [%s]"
}

// remove missing values from scope
for _, a := range e.Args {
e.Scope = removeElement(e.Scope, a)
}

return fmt.Sprintf(s, e.Name, strings.Join(e.Args, ", "), strings.Join(e.Scope, ", "))
}

func removeElement(xs []string, el string) []string {
for i, x := range xs {
if x == el {
xs = append(xs[:i], xs[i+1:]...)
break
}
}
return xs
}

type ErrWrongFuncSignature struct {
Name string
DomainProvided Domain
ArgLiterals []string
Func Func
}

func (e ErrWrongFuncSignature) Error() string {
return fmt.Sprintf("Cannot call function \"%s\" with args signature %s, available signatures are %s.",
e.Name, e.DomainProvided, FuncDomains(e.Func))
var argStringer fmt.Stringer = &argDomain{args: e.ArgLiterals, domain: e.DomainProvided}
if e.ArgLiterals == nil {
argStringer = e.DomainProvided
}
return fmt.Sprintf("Cannot call function \"%s\" with args %s, available signatures are %s.",
e.Name, argStringer, FuncDomains(e.Func))
}

type argDomain struct {
args []string
domain Domain
}

func (a *argDomain) String() string {
input := []string{}
for j, el := range a.args {
t := a.domain[j]
input = append(input, fmt.Sprintf("%s: %s", el, t))
}

return "(" + strings.Join(input, ",") + ")"
}

var ErrNotFloat = errors.New("value is not a float")
Expand Down
1 change: 1 addition & 0 deletions tick/stateful/node_evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func (e ErrTypeGuardFailed) Error() string {

type ReadOnlyScope interface {
Get(name string) (interface{}, error)
References() []string
DynamicFunc(name string) *DynamicFunc
}

Expand Down
8 changes: 8 additions & 0 deletions tick/stateful/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ func NewScope() *Scope {
}
}

func (s *Scope) References() []string {
ks := []string{}
for k := range s.variables {
ks = append(ks, "\""+k+"\"")
}
return ks
}

// Set defines a name -> value pairing in the scope.
func (s *Scope) Set(name string, value interface{}) {
s.variables[name] = value
Expand Down

0 comments on commit c588b48

Please sign in to comment.