Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6d3f5b9
Add graphql command
MichaelJCompton May 28, 2019
72cd15a
Add basic GraphQL schema processing
MichaelJCompton May 28, 2019
feb6c9d
Add basic Dgraph schema save
MichaelJCompton May 28, 2019
319c999
Save and load GraphQL schema first cut
MichaelJCompton May 28, 2019
a4427bb
Serve simple /graphql endpoint
MichaelJCompton May 29, 2019
bd595d5
Change error handling
MichaelJCompton May 29, 2019
e4811d8
Basic GraphQL request setup
MichaelJCompton May 30, 2019
22747e6
Make the go-bot happy
MichaelJCompton May 30, 2019
18f8e7f
goimports graphql code
MichaelJCompton May 31, 2019
a1cb877
review changes
MichaelJCompton May 31, 2019
343b2c3
remove use compression
MichaelJCompton May 31, 2019
2bfea22
Fix up last few errs and exit
MichaelJCompton Jun 3, 2019
1fd914c
start filling out GraphQL response
MichaelJCompton Jun 6, 2019
7482c85
pull out http handling from GraphQL handling
MichaelJCompton Jun 6, 2019
6636b36
refactor to remove dependence on external schema structure
MichaelJCompton Jun 6, 2019
2bf9c1e
Fixup imports
MichaelJCompton Jun 6, 2019
3e6382b
more refactoring
MichaelJCompton Jun 6, 2019
61b9322
wrap the bits of ast.schema we need
MichaelJCompton Jun 12, 2019
70ad9c4
touches on request and response
MichaelJCompton Jun 12, 2019
84b59b6
fill in handling of get and add, split out package structure
MichaelJCompton Jun 12, 2019
f205879
add some TODOs
MichaelJCompton Jun 12, 2019
cfdd9db
http server errors
MichaelJCompton Jun 12, 2019
f654fa9
debugging
MichaelJCompton Jun 12, 2019
95dc81d
Merge branch 'master' into mjc/graphql-init-qm
MichaelJCompton Jun 12, 2019
7f8a7fb
quick cut of buffer instead of raw json
MichaelJCompton Jun 12, 2019
0d3d073
line lengths
MichaelJCompton Jun 13, 2019
9de257f
bug fixes
MichaelJCompton Jun 13, 2019
ca81b8d
touchups
MichaelJCompton Jun 13, 2019
012dd2e
rewrite ID fields properly to uid
MichaelJCompton Jun 13, 2019
801f1bc
review changes
MichaelJCompton Jul 4, 2019
0171bb9
simplify
MichaelJCompton Jul 4, 2019
f9cc743
review changes
MichaelJCompton Jul 12, 2019
9547dc1
fixups
MichaelJCompton Jul 12, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions dgraph/cmd/graphql/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright 2019 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 graphql

import (
"encoding/json"
"mime"
"net/http"

"github.com/golang/glog"

"github.com/dgraph-io/dgo"
"github.com/dgraph-io/dgraph/dgraph/cmd/graphql/resolve"
"github.com/dgraph-io/dgraph/dgraph/cmd/graphql/schema"
"github.com/vektah/gqlparser/ast"
"github.com/vektah/gqlparser/gqlerror"
)

type graphqlHandler struct {
dgraphClient *dgo.Dgraph
schema *ast.Schema
}

// ServeHTTP handles GraphQL queries and mutations that get resolved
// via GraphQL->Dgraph->GraphQL. It writes a valid GraphQL JSON response
// to w.
func (gh *graphqlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)

if !gh.isValid() {
panic("graphqlHandler not initialised")
}

rh := gh.resolverForRequest(r)
res := rh.Resolve(r.Context())
_, err := res.WriteTo(w)
if err != nil {
glog.Error(err)
}
}

func (gh *graphqlHandler) isValid() bool {
return !(gh == nil || gh.schema == nil || gh.dgraphClient == nil)
}

func (gh *graphqlHandler) resolverForRequest(r *http.Request) (rr *resolve.RequestResolver) {
rr = resolve.New(schema.AsSchema(gh.schema), gh.dgraphClient)

switch r.Method {
case http.MethodGet:
// TODO: fill gqlReq in from parameters
case http.MethodPost:
mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
rr.WithErrors(gqlerror.Errorf("Unable to parse media type: %s", err))
return
}

switch mediaType {
case "application/json":
if err = json.NewDecoder(r.Body).Decode(&rr.GqlReq); err != nil {
rr.WithErrors(
gqlerror.Errorf("Not a valid GraphQL request body: %s", err))
return
}
default:
// https://graphql.org/learn/serving-over-http/#post-request says:
// "A standard GraphQL POST request should use the application/json
// content type ..."
rr.WithErrors(gqlerror.Errorf(
"Unrecognised Content-Type. Please use application/json for GraphQL requests"))
return
}
default:
rr.WithErrors(gqlerror.Errorf(
"Unrecognised request method. Please use GET or POST for GraphQL requests"))
return
}
return
}
109 changes: 109 additions & 0 deletions dgraph/cmd/graphql/resolve/dgraph.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright 2019 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 resolve

import (
"context"
"fmt"
"strings"

"github.com/golang/glog"

"github.com/dgraph-io/dgo"
"github.com/dgraph-io/dgraph/gql"
"github.com/pkg/errors"
)

func executeQuery(ctx context.Context, query *gql.GraphQuery, dgraphClient *dgo.Dgraph) ([]byte, error) {

q := asString(query)
if glog.V(3) {
glog.Infof("Executing Dgraph query: \n%s\n", q)
}

resp, err := dgraphClient.NewTxn().Query(ctx, q)
if err != nil {
// should I be inspecting this to work out if it's a bug,
// or a transient error?
return nil, errors.Wrap(err, "while querying Dgraph")
}

return resp.Json, nil
}

// TODO: once there's more below, extract out into gql package??
//
// There's not much of GraphQuery supported so far,
// so just simple writes of what is supported.
func asString(query *gql.GraphQuery) string {
var b strings.Builder

b.WriteString("query {\n")
writeQuery(&b, query, " ")
b.WriteString("}")

return b.String()
}

func writeQuery(b *strings.Builder, query *gql.GraphQuery, prefix string) {
b.WriteString(prefix)
if query.Alias != "" {
b.WriteString(query.Alias)
b.WriteString(" : ")
}
b.WriteString(query.Attr)

writeFunction(b, query.Func)
writeFilter(b, query.Filter)

if len(query.Children) > 0 {
b.WriteString(" { \n")
for _, c := range query.Children {
writeQuery(b, c, prefix+" ")
}
b.WriteString(prefix)
b.WriteString("}")
}
b.WriteString("\n")
}

func writeFunction(b *strings.Builder, f *gql.Function) {
if f != nil {
b.WriteString(fmt.Sprintf("(func: %s(0x%x)) ", f.Name, f.UID[0]))
// there's only uid(...) functions so far
}
}

func writeFilterFunction(b *strings.Builder, f *gql.Function) {
if f != nil {
b.WriteString(fmt.Sprintf("%s(%s) ", f.Name, f.Args[0].Value))
}
}

func writeFilter(b *strings.Builder, f *gql.FilterTree) {
if f == nil {
return
}

if f.Func.Name == "type" {
b.WriteString(fmt.Sprintf("@filter(type(%s)) ", f.Func.Args[0].Value))
} else {
b.WriteString("@filter(")
writeFilterFunction(b, f.Func)
b.WriteString(")")
}
}
Loading