Skip to content

Commit

Permalink
saving state
Browse files Browse the repository at this point in the history
  • Loading branch information
mangalaman93 committed May 20, 2019
1 parent 742a948 commit 378caa0
Show file tree
Hide file tree
Showing 3 changed files with 474 additions and 29 deletions.
77 changes: 67 additions & 10 deletions gql/parser_mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,46 +17,103 @@
package gql

import (
"errors"

"github.com/dgraph-io/dgo/protos/api"
"github.com/dgraph-io/dgraph/lex"
"github.com/dgraph-io/dgraph/x"
)

func ParseMutation(mutation string) (*api.Mutation, error) {
// ParseMutationBlock parses a block of text into a mutation.
// Returns an object with a mutation or a transaction query
// with mutation, otherwise returns nil with an error.
func ParseMutationBlock(mutation string) (*api.Mutation, error) {
lexer := lex.NewLexer(mutation)
lexer.Run(lexInsideMutation)
lexer.Run(lexIdentifyMutationBlock)
it := lexer.NewIterator()
var mu api.Mutation

if !it.Next() {
return nil, errors.New("Invalid mutation")
return nil, x.Errorf("Invalid mutation")
}

item := it.Item()
if item.Typ != itemLeftCurl {
switch item.Typ {
// mutation upsert block
case itemMutationUpsert:
// parse query block
upsertQuery, err := parseMutationUpsertQuery(it)
if err != nil {
return nil, err
}
// parse mutation block

// mutation block
case itemLeftCurl:

default:
return nil, x.Errorf("Expected { at the start of block. Got: [%s]", item.Val)
}
}

func ParseMutation(it *lex.ItemIterator) (string, error) {
for it.Next() {
item := it.Item()
if item.Typ == itemText {
continue
}
if item.Typ == itemRightCurl {
// mutations must be enclosed in a single block.
if it.Next() && it.Item().Typ != lex.ItemEOF {
if !inTxn && it.Next() && it.Item().Typ != lex.ItemEOF {
return nil, x.Errorf("Unexpected %s after the end of the block.", it.Item().Val)
}

return &mu, nil
}

if item.Typ == itemMutationOp {
if err := parseMutationOp(it, item.Val, &mu); err != nil {
return nil, err
}
}
}
return nil, x.Errorf("Invalid mutation.")
return nil, x.Errorf("Invalid mutation")
}

// parseMutationUpsertQuery gets the text inside a txn query block. It is possible that there's
// no query to be found, in that case it's the caller's responsibility to fail.
// Returns the query text if any is found, otherwise an empty string with error.
func parseMutationUpsertQuery(it *lex.ItemIterator) (string, error) {
var query string
var parse bool
for it.Next() {
item := it.Item()
switch item.Typ {
case itemLeftCurl:
continue
case itemMutationOpContent:
if !parse {
return "", x.Errorf("Invalid query block.")
}
query = item.Val
case itemMutationUpsertOp:
if item.Val == "query" {
if parse {
return "", x.Errorf("Too many query blocks in txn")
}
parse = true
continue
}
// TODO: mutation conditionals
if item.Val != "mutation" {
return "", x.Errorf("Invalid txn operator %q.", item.Val)
}
if !it.Next() {
return "", x.Errorf("Invalid mutation block")
}
return query, nil
default:
return "", x.Errorf("Unexpected %q inside of txn block.", item.Val)
}
}
return query, nil
}

// parseMutationOp parses and stores set or delete operation string in Mutation.
Expand All @@ -73,7 +130,7 @@ func parseMutationOp(it *lex.ItemIterator, op string, mu *api.Mutation) error {
}
parse = true
}
if item.Typ == itemMutationContent {
if item.Typ == itemMutationOpContent {
if !parse {
return x.Errorf("Mutation syntax invalid.")
}
Expand Down
260 changes: 260 additions & 0 deletions gql/parser_upsert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
/*
* Copyright 2017-2018 Dgraph Labs, Inc. and Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package gql

import (
"testing"

"github.com/dgraph-io/dgo/protos/api"
"github.com/stretchr/testify/require"
)

func TestParseMutationTxn1(t *testing.T) {
m := `
txn {
query {
me(func: eq(email, "[email protected]")) {
v as uid
}
}
mutation {
set {
uid(v) <name> "Some One" .
uid(v) <email> "[email protected]" .
}
}
}`
mu, err := ParseMutation(m)
require.NoError(t, err)
require.NotNil(t, mu)
query := `{
me(func: eq(email, "[email protected]")) {
v as uid
}
}`
require.EqualValues(t, query, mu.CondQuery)
sets, err := parseNquads(mu.SetNquads)
require.NoError(t, err)
require.EqualValues(t,
&api.NQuad{
SubjectVar: "v",
Predicate: "name",
ObjectValue: &api.Value{Val: &api.Value_DefaultVal{DefaultVal: "Some One"}},
},
sets[0])
require.EqualValues(t,
&api.NQuad{
SubjectVar: "v",
Predicate: "email",
ObjectValue: &api.Value{Val: &api.Value_DefaultVal{DefaultVal: "[email protected]"}},
},
sets[1])
}

func TestParseMutationTxn2(t *testing.T) {
m := `
txn {
mutation {
set {
uid(v) <name> "Some One" .
uid(v) <email> "[email protected]" .
}
}
}`
mu, err := ParseMutation(m)
require.NoError(t, err)
require.NotNil(t, mu)
sets, err := parseNquads(mu.SetNquads)
require.NoError(t, err)
require.EqualValues(t,
&api.NQuad{
SubjectVar: "v",
Predicate: "name",
ObjectValue: &api.Value{Val: &api.Value_DefaultVal{DefaultVal: "Some One"}},
},
sets[0])
require.EqualValues(t,
&api.NQuad{
SubjectVar: "v",
Predicate: "email",
ObjectValue: &api.Value{Val: &api.Value_DefaultVal{DefaultVal: "[email protected]"}},
},
sets[1])
}

func TestParseMutationTxn3(t *testing.T) {
m := `
txn {
mutation {
set {
<0x1> <name> "Some One" .
<0x1> <email> "[email protected]" .
<0x1> <friend> uid(v) .
}
}
}`
mu, err := ParseMutation(m)
require.NoError(t, err)
require.NotNil(t, mu)
sets, err := parseNquads(mu.SetNquads)
require.NoError(t, err)
require.EqualValues(t,
&api.NQuad{
Subject: "0x1",
Predicate: "name",
ObjectValue: &api.Value{Val: &api.Value_DefaultVal{DefaultVal: "Some One"}},
},
sets[0])
require.EqualValues(t,
&api.NQuad{
Subject: "0x1",
Predicate: "email",
ObjectValue: &api.Value{Val: &api.Value_DefaultVal{DefaultVal: "[email protected]"}},
},
sets[1])
require.EqualValues(t,
&api.NQuad{
Subject: "0x1",
Predicate: "friend",
ObjectVar: "v",
},
sets[2])
}

func TestParseMutationTxn4(t *testing.T) {
m := `
txn {
mutation {
set {
uid(v) <name> "Some One" .
uid(v) <friend> uid(w) .
}
}
}`
mu, err := ParseMutation(m)
require.NoError(t, err)
require.NotNil(t, mu)
sets, err := parseNquads(mu.SetNquads)
require.NoError(t, err)
require.EqualValues(t,
&api.NQuad{
SubjectVar: "v",
Predicate: "name",
ObjectValue: &api.Value{Val: &api.Value_DefaultVal{DefaultVal: "Some One"}},
},
sets[0])
require.EqualValues(t,
&api.NQuad{
SubjectVar: "v",
Predicate: "friend",
ObjectVar: "w",
},
sets[1])
}

func TestParseMutationErr1(t *testing.T) {
m := `
txn {
{
set {
<_:taco> <name> "Some One" .
<_:taco> <email> "[email protected]" .
}
}
}`
_, err := ParseMutation(m)
require.Error(t, err)
require.Contains(t, err.Error(), `Invalid operation type: set`)
}

func TestParseMutationErr2(t *testing.T) {
m := `
txn {
query {}
}`
_, err := ParseMutation(m)
require.Error(t, err)
require.Contains(t, err.Error(), `Unexpected "}" inside of txn block.`)
}

func TestParseMutationErr3(t *testing.T) {
m := `
txn {
query {}
query {}
}`
_, err := ParseMutation(m)
require.Error(t, err)
require.Contains(t, err.Error(), `Too many query blocks in txn`)
}

func TestParseMutationErr4(t *testing.T) {
m := `
txn {
query {}
mutation {
set {
<name> <is> <something> .
<hometown> <is> <san/francisco> .
}
delete {
<name> <is> <something-else> .
}
`
_, err := ParseMutation(m)
require.Error(t, err)
require.Contains(t, err.Error(), `Invalid mutation.`)
}

func TestParseMutationErr5(t *testing.T) {
m := `
txn {
query {
mutation {
set {
<name> <is> <something> .
<hometown> <is> <san/francisco> .
}
delete {
<name> <is> <something-else> .
}
`
_, err := ParseMutation(m)
require.Error(t, err)
require.Contains(t, err.Error(), `Unbalanced '}' found inside query text`)
}

func TestParseMutationErr6(t *testing.T) {
m := `
txn{
query {}
mutation {
set {
<name> <is> <something> .
<hometown> <is> <san/francisco> .
}
delete {
<name> <is> <something-else> .
}
}
}
`
_, err := ParseMutation(m)
require.Error(t, err)
require.Contains(t, err.Error(), `Unbalanced '}' found inside query text`)
}
Loading

0 comments on commit 378caa0

Please sign in to comment.