Skip to content

Commit

Permalink
fix(predicate): add string parser
Browse files Browse the repository at this point in the history
  • Loading branch information
kelwang committed Oct 18, 2019
1 parent fcc06d4 commit 2837d4e
Show file tree
Hide file tree
Showing 12 changed files with 642 additions and 365 deletions.
16 changes: 7 additions & 9 deletions http/delete_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,15 +162,14 @@ type deleteRequest struct {
}

type deleteRequestDecode struct {
Start string `json:"start"`
Stop string `json:"stop"`
Predicate json.RawMessage `json:"predicate"`
Start string `json:"start"`
Stop string `json:"stop"`
Predicate string `json:"predicate"`
}

func (dr *deleteRequest) UnmarshalJSON(b []byte) error {
var drd deleteRequestDecode
err := json.Unmarshal(b, &drd)
if err != nil {
if err := json.Unmarshal(b, &drd); err != nil {
return &influxdb.Error{
Code: influxdb.EInvalid,
Msg: "Invalid delete predicate node request",
Expand All @@ -183,7 +182,7 @@ func (dr *deleteRequest) UnmarshalJSON(b []byte) error {
return &influxdb.Error{
Code: influxdb.EInvalid,
Op: "http/Delete",
Msg: "invalid RFC3339Nano for field start",
Msg: "invalid RFC3339Nano for field start, please format your time with RFC3339Nano format, example: 2009-01-02T23:00:00Z",
}
}
dr.Start = start.UnixNano()
Expand All @@ -193,15 +192,14 @@ func (dr *deleteRequest) UnmarshalJSON(b []byte) error {
return &influxdb.Error{
Code: influxdb.EInvalid,
Op: "http/Delete",
Msg: "invalid RFC3339Nano for field stop",
Msg: "invalid RFC3339Nano for field stop, please format your time with RFC3339Nano format, example: 2009-01-01T23:00:00Z",
}
}
dr.Stop = stop.UnixNano()
node, err := predicate.UnmarshalJSON(drd.Predicate)
node, err := predicate.Parse(drd.Predicate)
if err != nil {
return err
}
dr.Predicate, err = predicate.New(node)

return err
}
87 changes: 57 additions & 30 deletions http/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func TestDelete(t *testing.T) {
contentType: "application/json; charset=utf-8",
body: fmt.Sprintf(`{
"code": "invalid",
"message": "invalid request; error parsing request json: invalid RFC3339Nano for field start"
"message": "invalid request; error parsing request json: invalid RFC3339Nano for field start, please format your time with RFC3339Nano format, example: 2009-01-02T23:00:00Z"
}`),
},
},
Expand All @@ -82,7 +82,7 @@ func TestDelete(t *testing.T) {
contentType: "application/json; charset=utf-8",
body: fmt.Sprintf(`{
"code": "invalid",
"message": "invalid request; error parsing request json: invalid RFC3339Nano for field stop"
"message": "invalid request; error parsing request json: invalid RFC3339Nano for field stop, please format your time with RFC3339Nano format, example: 2009-01-01T23:00:00Z"
}`),
},
},
Expand Down Expand Up @@ -231,43 +231,70 @@ func TestDelete(t *testing.T) {
},
},
{
name: "complex delete",
name: "unsupported delete",
args: args{
queryParams: map[string][]string{
"org": []string{"org1"},
"bucket": []string{"buck1"},
},
body: []byte(`{
"start":"2009-01-01T23:00:00Z",
"stop":"2019-11-10T01:00:00Z",
"nodeType": "logical",
"operator":"and",
"children":[
"stop":"2019-11-10T01:00:00Z",
"predicate": "tag1=\"v1\" and (tag2=\"v2\" or tag3=\"v3\")"
}`),
authorizer: &influxdb.Authorization{
UserID: user1ID,
Status: influxdb.Active,
Permissions: []influxdb.Permission{
{
"nodeType":"tagRule",
"operator":"equal",
"key":"tag1",
"value":"v1"
Action: influxdb.WriteAction,
Resource: influxdb.Resource{
Type: influxdb.BucketsResourceType,
ID: influxtesting.IDPtr(influxdb.ID(2)),
OrgID: influxtesting.IDPtr(influxdb.ID(1)),
},
},
{
"nodeType":"logical",
"operator":"or",
"children":[
{
"nodeType":"tagRule",
"operator":"notequal",
"key":"tag2",
"value":"v2"
},
{
"nodeType":"tagRule",
"operator":"regexequal",
"key":"tag3",
"value":"/v3/"
}
]
}
]
},
},
},
fields: fields{
DeleteService: mock.NewDeleteService(),
BucketService: &mock.BucketService{
FindBucketFn: func(ctx context.Context, f influxdb.BucketFilter) (*influxdb.Bucket, error) {
return &influxdb.Bucket{
ID: influxdb.ID(2),
Name: "bucket1",
}, nil
},
},
OrganizationService: &mock.OrganizationService{
FindOrganizationF: func(ctx context.Context, f influxdb.OrganizationFilter) (*influxdb.Organization, error) {
return &influxdb.Organization{
ID: influxdb.ID(1),
Name: "org1",
}, nil
},
},
},
wants: wants{
statusCode: http.StatusBadRequest,
body: fmt.Sprintf(`{
"code": "invalid",
"message": "invalid request; error parsing request json: Err in Child 1, err: the logical operator OR is not supported for delete predicate yet"
}`),
},
},
{
name: "complex delete",
args: args{
queryParams: map[string][]string{
"org": []string{"org1"},
"bucket": []string{"buck1"},
},
body: []byte(`{
"start":"2009-01-01T23:00:00Z",
"stop":"2019-11-10T01:00:00Z",
"predicate": "tag1=\"v1\" and (tag2=\"v2\" and tag3=\"v3\")"
}`),
authorizer: &influxdb.Authorization{
UserID: user1ID,
Expand Down
35 changes: 5 additions & 30 deletions http/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6223,9 +6223,7 @@ components:
path:
$ref: "#/components/schemas/StringLiteral"
DeletePredicateRequest:
oneOf:
- $ref: "#/components/schemas/PredicateNode"
description: A set of statements
description: the delete predicate request
type: object
required: [start, stop]
properties:
Expand All @@ -6235,10 +6233,10 @@ components:
stop:
description: RFC3339Nano.
type: string
PredicateNode:
oneOf:
- $ref: "#/components/schemas/LogicalNode"
- $ref: "#/components/schemas/TagRuleNode"
predicate:
description: sql where like delete statement
example: tag1="value1" and (tag2="value2" and tag3="value3")
type: string
Node:
oneOf:
- $ref: "#/components/schemas/Expression"
Expand Down Expand Up @@ -9704,29 +9702,6 @@ components:
owners:
description: URL to retrieve owners for this notification rule.
$ref: "#/components/schemas/Link"
LogicalNode:
type: object
required: [nodeType,operator,children]
properties:
nodeType:
type: string
enum: [logical]
operator:
type: string
enum: ["and"]
children:
type: array
items:
$ref: "#/components/schemas/PredicateNode"
TagRuleNode:
type: object
required: [nodeType]
allOf:
- $ref: "#/components/schemas/TagRule"
properties:
nodeType:
type: string
enum: [tagRule]
TagRule:
type: object
properties:
Expand Down
4 changes: 2 additions & 2 deletions notification/rule/rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,15 @@ func TestValidRule(t *testing.T) {
Key: "k1",
Value: "v1",
},
Operator: influxdb.Operator("bad"),
Operator: -5,
},
},
},
MessageTemplate: "body {var2}",
},
err: &influxdb.Error{
Code: influxdb.EInvalid,
Msg: `Operator "bad" is invalid`,
Msg: `Operator is invalid`,
},
},
{
Expand Down
59 changes: 3 additions & 56 deletions predicate/logical.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
package predicate

import (
"encoding/json"
"fmt"
"strings"

"github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/storage/reads/datatypes"
)

// LogicalOperator is a string type of logical operator.
type LogicalOperator string
type LogicalOperator int

// LogicalOperators
var (
LogicalAnd LogicalOperator = "and"
LogicalOr LogicalOperator = "or"
LogicalAnd LogicalOperator = 1
LogicalOr LogicalOperator = 2
)

// Value returns the node logical type.
func (op LogicalOperator) Value() (datatypes.Node_Logical, error) {
op = LogicalOperator(strings.ToLower(string(op)))
switch op {
case LogicalAnd:
return datatypes.LogicalAnd, nil
Expand All @@ -43,56 +40,6 @@ type LogicalNode struct {
Children []Node `json:"children"`
}

// NodeType returns value "logical".
func (n LogicalNode) NodeType() string {
return logicalNodeType
}

type logicalNodeAlias LogicalNode

// MarshalJSON implement json.Marshaler interface.
func (n LogicalNode) MarshalJSON() ([]byte, error) {
return json.Marshal(
struct {
logicalNodeAlias
NodeType string `json:"nodeType"`
}{
logicalNodeAlias: logicalNodeAlias(n),
NodeType: n.NodeType(),
})
}

type logicalNodeDecode struct {
Operator LogicalOperator `json:"operator"`
Children []json.RawMessage `json:"children"`
}

// UnmarshalJSON implement json unmarshal interface.
func (n *LogicalNode) UnmarshalJSON(b []byte) error {
nn := new(logicalNodeDecode)
err := json.Unmarshal(b, nn)
if err != nil {
return &influxdb.Error{
Code: influxdb.EInvalid,
Err: err,
}
}
*n = LogicalNode{
Operator: nn.Operator,
Children: make([]Node, len(nn.Children)),
}
for k, child := range nn.Children {
n.Children[k], err = UnmarshalJSON(child)
if err != nil {
return &influxdb.Error{
Code: influxdb.EInvalid,
Msg: fmt.Sprintf("Err in Child %d, err: %s", k, err.Error()),
}
}
}
return nil
}

// ToDataType convert a LogicalNode to datatypes.Node.
func (n LogicalNode) ToDataType() (*datatypes.Node, error) {
logicalOp, err := n.Operator.Value()
Expand Down
Loading

0 comments on commit 2837d4e

Please sign in to comment.