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  }