github.com/dgraph-io/dgraph@v1.2.8/graphql/resolve/query.go (about) 1 /* 2 * Copyright 2019 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package resolve 18 19 import ( 20 "context" 21 22 "github.com/golang/glog" 23 otrace "go.opencensus.io/trace" 24 25 "github.com/dgraph-io/dgraph/gql" 26 "github.com/dgraph-io/dgraph/graphql/api" 27 "github.com/dgraph-io/dgraph/graphql/schema" 28 "github.com/dgraph-io/dgraph/x" 29 ) 30 31 // A QueryResolver can resolve a single query. 32 type QueryResolver interface { 33 Resolve(ctx context.Context, query schema.Query) *Resolved 34 } 35 36 // A QueryRewriter can build a Dgraph gql.GraphQuery from a GraphQL query, 37 type QueryRewriter interface { 38 Rewrite(q schema.Query) (*gql.GraphQuery, error) 39 } 40 41 // A QueryExecutor can execute a gql.GraphQuery and return a result. The result of 42 // a QueryExecutor doesn't need to be valid GraphQL results. 43 type QueryExecutor interface { 44 Query(ctx context.Context, query *gql.GraphQuery) ([]byte, error) 45 } 46 47 // QueryResolverFunc is an adapter that allows to build a QueryResolver from 48 // a function. Based on the http.HandlerFunc pattern. 49 type QueryResolverFunc func(ctx context.Context, query schema.Query) *Resolved 50 51 // QueryRewritingFunc is an adapter that allows us to build a QueryRewriter from 52 // a function. Based on the http.HandlerFunc pattern. 53 type QueryRewritingFunc func(q schema.Query) (*gql.GraphQuery, error) 54 55 // QueryExecutionFunc is an adapter that allows us to compose query execution and 56 // build a QueryExecuter from a function. Based on the http.HandlerFunc pattern. 57 type QueryExecutionFunc func(ctx context.Context, query *gql.GraphQuery) ([]byte, error) 58 59 // Resolve calls qr(ctx, query) 60 func (qr QueryResolverFunc) Resolve(ctx context.Context, query schema.Query) *Resolved { 61 return qr(ctx, query) 62 } 63 64 // Rewrite calls qr(q) 65 func (qr QueryRewritingFunc) Rewrite(q schema.Query) (*gql.GraphQuery, error) { 66 return qr(q) 67 } 68 69 // Query calls qe(ctx, query) 70 func (qe QueryExecutionFunc) Query(ctx context.Context, query *gql.GraphQuery) ([]byte, error) { 71 return qe(ctx, query) 72 } 73 74 // NewQueryResolver creates a new query resolver. The resolver runs the pipeline: 75 // 1) rewrite the query using qr (return error if failed) 76 // 2) execute the rewritten query with qe (return error if failed) 77 // 3) process the result with rc 78 func NewQueryResolver(qr QueryRewriter, qe QueryExecutor, rc ResultCompleter) QueryResolver { 79 return &queryResolver{queryRewriter: qr, queryExecutor: qe, resultCompleter: rc} 80 } 81 82 // NoOpQueryExecution does nothing and returns nil. 83 func NoOpQueryExecution() QueryExecutionFunc { 84 return QueryExecutionFunc(func(ctx context.Context, query *gql.GraphQuery) ([]byte, error) { 85 return nil, nil 86 }) 87 } 88 89 // NoOpQueryRewrite does nothing and returns a nil rewriting. 90 func NoOpQueryRewrite() QueryRewritingFunc { 91 return QueryRewritingFunc(func(q schema.Query) (*gql.GraphQuery, error) { 92 return nil, nil 93 }) 94 } 95 96 // a queryResolver can resolve a single GraphQL query field. 97 type queryResolver struct { 98 queryRewriter QueryRewriter 99 queryExecutor QueryExecutor 100 resultCompleter ResultCompleter 101 } 102 103 func (qr *queryResolver) Resolve(ctx context.Context, query schema.Query) *Resolved { 104 span := otrace.FromContext(ctx) 105 stop := x.SpanTimer(span, "resolveQuery") 106 defer stop() 107 108 res, err := qr.rewriteAndExecute(ctx, query) 109 110 completed, err := qr.resultCompleter.Complete(ctx, query, res, err) 111 return &Resolved{Data: completed, Err: err} 112 } 113 114 func (qr *queryResolver) rewriteAndExecute( 115 ctx context.Context, query schema.Query) ([]byte, error) { 116 117 dgQuery, err := qr.queryRewriter.Rewrite(query) 118 if err != nil { 119 return nil, schema.GQLWrapf(err, "couldn't rewrite query %s", query.ResponseName()) 120 } 121 122 resp, err := qr.queryExecutor.Query(ctx, dgQuery) 123 if err != nil { 124 glog.Infof("[%s] query execution failed : %s", api.RequestID(ctx), err) 125 return nil, schema.GQLWrapf(err, "[%s] failed to resolve query", api.RequestID(ctx)) 126 } 127 128 return resp, nil 129 } 130 131 func introspectionExecution(q schema.Query) QueryExecutionFunc { 132 return QueryExecutionFunc(func(ctx context.Context, query *gql.GraphQuery) ([]byte, error) { 133 return schema.Introspect(q) 134 }) 135 }