refactor(http/query): update http query logic for new parser API

pull/10616/head
Nathaniel Cook 2019-01-04 11:08:07 -07:00
parent 3660cfc34a
commit 4918b15f35
6 changed files with 36 additions and 56 deletions

2
go.mod
View File

@ -79,7 +79,7 @@ require (
github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20181106190520-2236f141171e // indirect
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/influxdata/flux v0.12.0
github.com/influxdata/flux v0.12.1-0.20190104180637-9c4371ac8c93
github.com/influxdata/influxql v0.0.0-20180925231337-1cbfca8e56b6
github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee // indirect

4
go.sum
View File

@ -240,8 +240,8 @@ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/flux v0.12.0 h1:mI91GHgqb5sbsz9fesvKwbZrh3wpxLKIEeCYswRBh2A=
github.com/influxdata/flux v0.12.0/go.mod h1:81jeDcHVn1rN5uj9aQ81S72Q8ol8If7N0zM0G8TnxTE=
github.com/influxdata/flux v0.12.1-0.20190104180637-9c4371ac8c93 h1:HYb4I2jH8OrHAZj4bjPLNz9rSSHnWUdphCUNlD75wqo=
github.com/influxdata/flux v0.12.1-0.20190104180637-9c4371ac8c93/go.mod h1:81jeDcHVn1rN5uj9aQ81S72Q8ol8If7N0zM0G8TnxTE=
github.com/influxdata/goreleaser v0.86.2-0.20181010170531-0fd209ba67f5/go.mod h1:aVuBpDAT5VtjtUxzvBt8HOd0buzvvk7OX3H2iaviixg=
github.com/influxdata/influxql v0.0.0-20180925231337-1cbfca8e56b6 h1:CFx+pP90q/qg3spoiZjf8donE4WpAdjeJfPOcoNqkWo=
github.com/influxdata/influxql v0.0.0-20180925231337-1cbfca8e56b6/go.mod h1:KpVI7okXjK6PRi3Z5B+mtKZli+R1DnZgb3N+tzevNgo=

View File

@ -26,7 +26,7 @@ import (
// QueryRequest is a flux query request.
type QueryRequest struct {
Spec *flux.Spec `json:"spec,omitempty"`
AST *ast.Program `json:"ast,omitempty"`
AST *ast.Package `json:"ast,omitempty"`
Query string `json:"query"`
Type string `json:"type"`
Dialect QueryDialect `json:"dialect"`
@ -128,49 +128,26 @@ func (r QueryRequest) Analyze() (*QueryAnalysis, error) {
func (r QueryRequest) analyzeFluxQuery() (*QueryAnalysis, error) {
a := &QueryAnalysis{}
_, err := parser.NewAST(r.Query)
if err == nil {
pkg := parser.ParseSource(r.Query)
errCount := ast.Check(pkg)
if errCount == 0 {
a.Errors = []queryParseError{}
return a, nil
}
ms := fluxParseErrorRE.FindAllStringSubmatch(err.Error(), -1)
a.Errors = make([]queryParseError, 0, len(ms))
for _, m := range ms {
if len(m) != 5 {
return nil, fmt.Errorf("flux query error is not formatted as expected: got %d matches expected 5", len(m))
a.Errors = make([]queryParseError, 0, errCount)
ast.Walk(ast.CreateVisitor(func(node ast.Node) {
loc := node.Location()
for _, err := range node.Errs() {
a.Errors = append(a.Errors, queryParseError{
Line: loc.Start.Line,
Column: loc.Start.Column,
Message: err.Msg,
})
}
lineStr := m[1]
line, err := strconv.Atoi(lineStr)
if err != nil {
return nil, fmt.Errorf("failed to parse line number from error mesage: %s -> %v", lineStr, err)
}
colStr := m[2]
col, err := strconv.Atoi(colStr)
if err != nil {
return nil, fmt.Errorf("failed to parse column number from error mesage: %s -> %v", colStr, err)
}
charStr := m[3]
char, err := strconv.Atoi(charStr)
if err != nil {
return nil, fmt.Errorf("failed to parse character number from error mesage: %s -> %v", charStr, err)
}
msg := m[4]
a.Errors = append(a.Errors, queryParseError{
Line: line,
Column: col,
Character: char,
Message: msg,
})
}
}), pkg)
return a, nil
}
var fluxParseErrorRE = regexp.MustCompile(`(\d+):(\d+) \((\d+)\): ([[:graph:] ]+)`)
func (r QueryRequest) analyzeInfluxQLQuery() (*QueryAnalysis, error) {
a := &QueryAnalysis{}
_, err := influxql.ParseQuery(r.Query)
@ -238,7 +215,7 @@ func nowFunc(now time.Time) values.Function {
return values.NewFunction("now", ftype, call, sideEffect)
}
func toSpec(p *ast.Program, now func() time.Time) (*flux.Spec, error) {
func toSpec(p *ast.Package, now func() time.Time) (*flux.Spec, error) {
itrp := flux.NewInterpreter()
itrp.SetOption("now", nowFunc(now()))
semProg, err := semantic.New(p)

View File

@ -98,7 +98,7 @@ type langRequest struct {
}
type postFluxASTResponse struct {
AST *ast.Program `json:"ast"`
AST *ast.Package `json:"ast"`
}
// postFluxAST returns a flux AST for provided flux string
@ -112,14 +112,15 @@ func (h *FluxHandler) postFluxAST(w http.ResponseWriter, r *http.Request) {
return
}
ast, err := parser.NewAST(request.Query)
if err != nil {
pkg := parser.ParseSource(request.Query)
if ast.Check(pkg) > 0 {
err := ast.GetError(pkg)
EncodeError(ctx, errors.InvalidDataf("invalid AST: %v", err), w)
return
}
res := postFluxASTResponse{
AST: ast,
AST: pkg,
}
if err := encodeResponse(ctx, w, http.StatusOK, res); err != nil {

View File

@ -202,7 +202,7 @@ func TestFluxHandler_postFluxAST(t *testing.T) {
name: "get ast from()",
w: httptest.NewRecorder(),
r: httptest.NewRequest("POST", "/api/v2/query/ast", bytes.NewBufferString(`{"query": "from()"}`)),
want: `{"ast":{"type":"Program","location":{"start":{"line":1,"column":1},"end":{"line":1,"column":7},"source":"from()"},"package":null,"imports":null,"body":[{"type":"ExpressionStatement","location":{"start":{"line":1,"column":1},"end":{"line":1,"column":7},"source":"from()"},"expression":{"type":"CallExpression","location":{"start":{"line":1,"column":1},"end":{"line":1,"column":7},"source":"from()"},"callee":{"type":"Identifier","location":{"start":{"line":1,"column":1},"end":{"line":1,"column":5},"source":"from"},"name":"from"}}}]}}
want: `{"ast":{"type":"Package","package":"main","files":[{"type":"File","location":{"start":{"line":1,"column":1},"end":{"line":1,"column":7},"source":"from()"},"package":null,"imports":null,"body":[{"type":"ExpressionStatement","location":{"start":{"line":1,"column":1},"end":{"line":1,"column":7},"source":"from()"},"expression":{"type":"CallExpression","location":{"start":{"line":1,"column":1},"end":{"line":1,"column":7},"source":"from()"},"callee":{"type":"Identifier","location":{"start":{"line":1,"column":1},"end":{"line":1,"column":5},"source":"from"},"name":"from"}}}]}]}}
`,
status: http.StatusOK,
},

View File

@ -24,7 +24,7 @@ import (
func TestQueryRequest_WithDefaults(t *testing.T) {
type fields struct {
Spec *flux.Spec
AST *ast.Program
AST *ast.Package
Query string
Type string
Dialect QueryDialect
@ -67,7 +67,7 @@ func TestQueryRequest_WithDefaults(t *testing.T) {
func TestQueryRequest_Validate(t *testing.T) {
type fields struct {
Spec *flux.Spec
AST *ast.Program
AST *ast.Package
Query string
Type string
Dialect QueryDialect
@ -181,7 +181,7 @@ func TestQueryRequest_Validate(t *testing.T) {
func Test_toSpec(t *testing.T) {
type args struct {
p *ast.Program
p *ast.Package
now func() time.Time
}
tests := []struct {
@ -193,7 +193,7 @@ func Test_toSpec(t *testing.T) {
{
name: "ast converts to spec",
args: args{
p: &ast.Program{},
p: &ast.Package{},
now: func() time.Time { return time.Unix(0, 0) },
},
want: &flux.Spec{
@ -203,10 +203,12 @@ func Test_toSpec(t *testing.T) {
{
name: "bad semantics error",
args: args{
p: &ast.Program{
Body: []ast.Statement{
&ast.ReturnStatement{},
},
p: &ast.Package{
Files: []*ast.File{{
Body: []ast.Statement{
&ast.ReturnStatement{},
},
}},
},
now: func() time.Time { return time.Unix(0, 0) },
},
@ -231,7 +233,7 @@ func Test_toSpec(t *testing.T) {
func TestQueryRequest_proxyRequest(t *testing.T) {
type fields struct {
Spec *flux.Spec
AST *ast.Program
AST *ast.Package
Query string
Type string
Dialect QueryDialect
@ -279,7 +281,7 @@ func TestQueryRequest_proxyRequest(t *testing.T) {
{
name: "valid AST",
fields: fields{
AST: &ast.Program{},
AST: &ast.Package{},
Type: "flux",
Dialect: QueryDialect{
Delimiter: ",",