github.com/dgraph-io/dgraph@v1.2.8/graphql/resolve/resolver.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  	"bytes"
    21  	"context"
    22  	"encoding/json"
    23  	"sync"
    24  
    25  	"github.com/dgraph-io/dgraph/graphql/dgraph"
    26  
    27  	dgoapi "github.com/dgraph-io/dgo/v2/protos/api"
    28  	"github.com/dgraph-io/dgraph/gql"
    29  	"github.com/dgraph-io/dgraph/graphql/api"
    30  	"github.com/dgraph-io/dgraph/x"
    31  	"github.com/pkg/errors"
    32  	"go.opencensus.io/trace"
    33  	otrace "go.opencensus.io/trace"
    34  
    35  	"github.com/golang/glog"
    36  
    37  	"github.com/dgraph-io/dgraph/graphql/schema"
    38  )
    39  
    40  const (
    41  	methodResolve = "RequestResolver.Resolve"
    42  
    43  	resolverFailed    = false
    44  	resolverSucceeded = true
    45  )
    46  
    47  // A ResolverFactory finds the right resolver for a query/mutation.
    48  type ResolverFactory interface {
    49  	queryResolverFor(query schema.Query) QueryResolver
    50  	mutationResolverFor(mutation schema.Mutation) MutationResolver
    51  
    52  	// WithQueryResolver adds a new query resolver.  Each time query name is resolved
    53  	// resolver is called to create a new instane of a QueryResolver to resolve the
    54  	// query.
    55  	WithQueryResolver(name string, resolver func(schema.Query) QueryResolver) ResolverFactory
    56  
    57  	// WithMutationResolver adds a new query resolver.  Each time mutation name is resolved
    58  	// resolver is called to create a new instane of a MutationResolver to resolve the
    59  	// mutation.
    60  	WithMutationResolver(
    61  		name string, resolver func(schema.Mutation) MutationResolver) ResolverFactory
    62  
    63  	// WithConventionResolvers adds a set of our convention based resolvers to the
    64  	// factory.  The registration happens only once.
    65  	WithConventionResolvers(s schema.Schema, fns *ResolverFns) ResolverFactory
    66  
    67  	// WithSchemaIntrospection adds schema introspection capabilities to the factory.
    68  	// So __schema and __type queries can be resolved.
    69  	WithSchemaIntrospection() ResolverFactory
    70  }
    71  
    72  // A ResultCompleter can take a []byte slice representing an intermediate result
    73  // in resolving field and applies a completion step - for example, apply GraphQL
    74  // error propagation or massaging error paths.
    75  type ResultCompleter interface {
    76  	Complete(ctx context.Context, field schema.Field, result []byte, err error) ([]byte, error)
    77  }
    78  
    79  // RequestResolver can process GraphQL requests and write GraphQL JSON responses.
    80  type RequestResolver struct {
    81  	schema    schema.Schema
    82  	resolvers ResolverFactory
    83  }
    84  
    85  // A resolverFactory is the main implementation of ResolverFactory.  It stores a
    86  // map of all the resolvers that have been registered and returns a resolver that
    87  // just returns errors if it's asked for a resolver for a field that it doesn't
    88  // know about.
    89  type resolverFactory struct {
    90  	queryResolvers    map[string]func(schema.Query) QueryResolver
    91  	mutationResolvers map[string]func(schema.Mutation) MutationResolver
    92  
    93  	// returned if the factory gets asked for resolver for a field that it doesn't
    94  	// know about.
    95  	queryError    QueryResolverFunc
    96  	mutationError MutationResolverFunc
    97  }
    98  
    99  // ResolverFns is a convenience struct for passing blocks of rewriters and executors.
   100  type ResolverFns struct {
   101  	Qrw QueryRewriter
   102  	Arw func() MutationRewriter
   103  	Urw func() MutationRewriter
   104  	Drw MutationRewriter
   105  	Qe  QueryExecutor
   106  	Me  MutationExecutor
   107  }
   108  
   109  // dgraphExecutor is an implementation of both QueryExecutor and MutationExecutor
   110  // that proxies query/mutation resolution through Query method in dgraph server.
   111  type dgraphExecutor struct {
   112  }
   113  
   114  // A Resolved is the result of resolving a single query or mutation.
   115  // A schema.Request may contain any number of queries or mutations (never both).
   116  // RequestResolver.Resolve() resolves all of them by finding the resolved answers
   117  // of the component queries/mutations and joining into a single schema.Response.
   118  type Resolved struct {
   119  	Data []byte
   120  	Err  error
   121  }
   122  
   123  // CompletionFunc is an adapter that allows us to compose completions and build a
   124  // ResultCompleter from a function.  Based on the http.HandlerFunc pattern.
   125  type CompletionFunc func(
   126  	ctx context.Context, field schema.Field, result []byte, err error) ([]byte, error)
   127  
   128  // Complete calls cf(ctx, field, result, err)
   129  func (cf CompletionFunc) Complete(
   130  	ctx context.Context,
   131  	field schema.Field,
   132  	result []byte,
   133  	err error) ([]byte, error) {
   134  
   135  	return cf(ctx, field, result, err)
   136  }
   137  
   138  // DgraphAsQueryExecutor builds a QueryExecutor for proxying requests through dgraph.
   139  func DgraphAsQueryExecutor() QueryExecutor {
   140  	return &dgraphExecutor{}
   141  }
   142  
   143  // DgraphAsMutationExecutor builds a MutationExecutor for dog.
   144  func DgraphAsMutationExecutor() MutationExecutor {
   145  	return &dgraphExecutor{}
   146  }
   147  
   148  func (de *dgraphExecutor) Query(ctx context.Context, query *gql.GraphQuery) ([]byte, error) {
   149  	return dgraph.Query(ctx, query)
   150  }
   151  
   152  func (de *dgraphExecutor) Mutate(
   153  	ctx context.Context,
   154  	query *gql.GraphQuery,
   155  	mutations []*dgoapi.Mutation) (map[string]string, map[string]interface{}, error) {
   156  	return dgraph.Mutate(ctx, query, mutations)
   157  }
   158  
   159  func (rf *resolverFactory) WithQueryResolver(
   160  	name string, resolver func(schema.Query) QueryResolver) ResolverFactory {
   161  	rf.queryResolvers[name] = resolver
   162  	return rf
   163  }
   164  
   165  func (rf *resolverFactory) WithMutationResolver(
   166  	name string, resolver func(schema.Mutation) MutationResolver) ResolverFactory {
   167  	rf.mutationResolvers[name] = resolver
   168  	return rf
   169  }
   170  
   171  func (rf *resolverFactory) WithSchemaIntrospection() ResolverFactory {
   172  	introspect := func(q schema.Query) QueryResolver {
   173  		return &queryResolver{
   174  			queryRewriter:   NoOpQueryRewrite(),
   175  			queryExecutor:   introspectionExecution(q),
   176  			resultCompleter: removeObjectCompletion(noopCompletion),
   177  		}
   178  	}
   179  
   180  	rf.WithQueryResolver("__schema", introspect)
   181  	rf.WithQueryResolver("__type", introspect)
   182  
   183  	return rf
   184  }
   185  
   186  func (rf *resolverFactory) WithConventionResolvers(
   187  	s schema.Schema, fns *ResolverFns) ResolverFactory {
   188  
   189  	queries := append(s.Queries(schema.GetQuery), s.Queries(schema.FilterQuery)...)
   190  	for _, q := range queries {
   191  		rf.WithQueryResolver(q, func(q schema.Query) QueryResolver {
   192  			return NewQueryResolver(fns.Qrw, fns.Qe, StdQueryCompletion())
   193  		})
   194  	}
   195  
   196  	for _, m := range s.Mutations(schema.AddMutation) {
   197  		rf.WithMutationResolver(m, func(m schema.Mutation) MutationResolver {
   198  			return NewMutationResolver(
   199  				fns.Arw(), fns.Qe, fns.Me, StdMutationCompletion(m.ResponseName()))
   200  		})
   201  	}
   202  
   203  	for _, m := range s.Mutations(schema.UpdateMutation) {
   204  		rf.WithMutationResolver(m, func(m schema.Mutation) MutationResolver {
   205  			return NewMutationResolver(
   206  				fns.Urw(), fns.Qe, fns.Me, StdMutationCompletion(m.ResponseName()))
   207  		})
   208  	}
   209  
   210  	for _, m := range s.Mutations(schema.DeleteMutation) {
   211  		rf.WithMutationResolver(m, func(m schema.Mutation) MutationResolver {
   212  			return NewMutationResolver(
   213  				fns.Drw, NoOpQueryExecution(), fns.Me, StdDeleteCompletion(m.ResponseName()))
   214  		})
   215  	}
   216  
   217  	return rf
   218  }
   219  
   220  // NewResolverFactory returns a ResolverFactory that resolves requests via
   221  // query/mutation rewriting and execution through Dgraph.  If the factory gets asked
   222  // to resolve a query/mutation it doesn't know how to rewrite, it uses
   223  // the queryError/mutationError to build an error result.
   224  func NewResolverFactory(
   225  	queryError QueryResolverFunc, mutationError MutationResolverFunc) ResolverFactory {
   226  
   227  	return &resolverFactory{
   228  		queryResolvers:    make(map[string]func(schema.Query) QueryResolver),
   229  		mutationResolvers: make(map[string]func(schema.Mutation) MutationResolver),
   230  
   231  		queryError:    queryError,
   232  		mutationError: mutationError,
   233  	}
   234  }
   235  
   236  // StdQueryCompletion is the completion steps that get run for queries
   237  func StdQueryCompletion() CompletionFunc {
   238  	return removeObjectCompletion(completeDgraphResult)
   239  }
   240  
   241  // StdMutationCompletion is the completion steps that get run for add and update mutations
   242  func StdMutationCompletion(name string) CompletionFunc {
   243  	return addPathCompletion(name, addRootFieldCompletion(name, completeDgraphResult))
   244  }
   245  
   246  // StdDeleteCompletion is the completion steps that get run for add and update mutations
   247  func StdDeleteCompletion(name string) CompletionFunc {
   248  	return addPathCompletion(name, addRootFieldCompletion(name, deleteCompletion()))
   249  }
   250  
   251  func (rf *resolverFactory) queryResolverFor(query schema.Query) QueryResolver {
   252  	if resolver, ok := rf.queryResolvers[query.Name()]; ok {
   253  		return resolver(query)
   254  	}
   255  
   256  	return rf.queryError
   257  }
   258  
   259  func (rf *resolverFactory) mutationResolverFor(mutation schema.Mutation) MutationResolver {
   260  	if resolver, ok := rf.mutationResolvers[mutation.Name()]; ok {
   261  		return resolver(mutation)
   262  	}
   263  
   264  	return rf.mutationError
   265  }
   266  
   267  // New creates a new RequestResolver.
   268  func New(s schema.Schema, resolverFactory ResolverFactory) *RequestResolver {
   269  	return &RequestResolver{
   270  		schema:    s,
   271  		resolvers: resolverFactory,
   272  	}
   273  }
   274  
   275  // Resolve processes r.GqlReq and returns a GraphQL response.
   276  // r.GqlReq should be set with a request before Resolve is called
   277  // and a schema and backend Dgraph should have been added.
   278  // Resolve records any errors in the response's error field.
   279  func (r *RequestResolver) Resolve(ctx context.Context, gqlReq *schema.Request) *schema.Response {
   280  	span := otrace.FromContext(ctx)
   281  	stop := x.SpanTimer(span, methodResolve)
   282  	defer stop()
   283  
   284  	reqID := api.RequestID(ctx)
   285  
   286  	if r == nil {
   287  		glog.Errorf("[%s] Call to Resolve with nil RequestResolver", reqID)
   288  		return schema.ErrorResponse(errors.New("Internal error"), reqID)
   289  	}
   290  
   291  	if r.schema == nil {
   292  		glog.Errorf("[%s] Call to Resolve with no schema", reqID)
   293  		return schema.ErrorResponse(errors.New("Internal error"), reqID)
   294  	}
   295  
   296  	op, err := r.schema.Operation(gqlReq)
   297  	if err != nil {
   298  		return schema.ErrorResponse(err, reqID)
   299  	}
   300  
   301  	resp := &schema.Response{
   302  		Extensions: &schema.Extensions{
   303  			RequestID: reqID,
   304  		},
   305  	}
   306  
   307  	if glog.V(3) {
   308  		b, err := json.Marshal(gqlReq.Variables)
   309  		if err != nil {
   310  			glog.Infof("Failed to marshal variables for logging : %s", err)
   311  		}
   312  		glog.Infof("[%s] Resolving GQL request: \n%s\nWith Variables: \n%s\n",
   313  			reqID, gqlReq.Query, string(b))
   314  	}
   315  
   316  	// A single request can contain either queries or mutations - not both.
   317  	// GraphQL validation on the request would have caught that error case
   318  	// before we get here.  At this point, we know it's valid, it's passed
   319  	// GraphQL validation and any additional validation we've added.  So here,
   320  	// we can just execute it.
   321  	switch {
   322  	case op.IsQuery():
   323  		// Queries run in parallel and are independent of each other: e.g.
   324  		// an error in one query, doesn't affect the others.
   325  
   326  		var wg sync.WaitGroup
   327  		allResolved := make([]*Resolved, len(op.Queries()))
   328  
   329  		for i, q := range op.Queries() {
   330  			wg.Add(1)
   331  
   332  			go func(q schema.Query, storeAt int) {
   333  				defer wg.Done()
   334  				defer api.PanicHandler(api.RequestID(ctx),
   335  					func(err error) {
   336  						allResolved[storeAt] = &Resolved{Err: err}
   337  					})
   338  
   339  				allResolved[storeAt] = r.resolvers.queryResolverFor(q).Resolve(ctx, q)
   340  			}(q, i)
   341  		}
   342  		wg.Wait()
   343  
   344  		// The GraphQL data response needs to be written in the same order as the
   345  		// queries in the request.
   346  		for _, res := range allResolved {
   347  			// Errors and data in the same response is valid.  Both WithError and
   348  			// AddData handle nil cases.
   349  			resp.WithError(res.Err)
   350  			resp.AddData(res.Data)
   351  		}
   352  	case op.IsMutation():
   353  		// A mutation operation can contain any number of mutation fields.  Those should be executed
   354  		// serially.
   355  		// (spec https://graphql.github.io/graphql-spec/June2018/#sec-Normal-and-Serial-Execution)
   356  		//
   357  		// The spec is ambiguous about what to do in the case of errors during that serial execution
   358  		// - apparently deliberately so; see this comment from Lee Byron:
   359  		// https://github.com/graphql/graphql-spec/issues/277#issuecomment-385588590
   360  		// and clarification
   361  		// https://github.com/graphql/graphql-spec/pull/438
   362  		//
   363  		// A reasonable interpretation of that is to stop a list of mutations after the first error -
   364  		// which seems like the natural semantics and is what we enforce here.
   365  		allSuccessful := true
   366  
   367  		for _, m := range op.Mutations() {
   368  			if !allSuccessful {
   369  				resp.WithError(x.GqlErrorf(
   370  					"Mutation %s was not executed because of a previous error.",
   371  					m.ResponseName()).
   372  					WithLocations(m.Location()))
   373  
   374  				continue
   375  			}
   376  
   377  			var res *Resolved
   378  			res, allSuccessful = r.resolvers.mutationResolverFor(m).Resolve(ctx, m)
   379  			resp.WithError(res.Err)
   380  			resp.AddData(res.Data)
   381  		}
   382  	case op.IsSubscription():
   383  		resp.WithError(errors.Errorf("Subscriptions not yet supported."))
   384  	}
   385  
   386  	return resp
   387  }
   388  
   389  // noopCompletion just passes back it's result and err arguments
   390  func noopCompletion(
   391  	ctx context.Context, field schema.Field, result []byte, err error) ([]byte, error) {
   392  	return result, err
   393  }
   394  
   395  // removeObjectCompletion chops leading '{' and trailing '}' from a JSON object
   396  //
   397  // The final GraphQL result gets built like
   398  // { data:
   399  //    {
   400  //      q1: {...},
   401  //      q2: [ {...}, {...} ],
   402  //      ...
   403  //    }
   404  // }
   405  //
   406  // When we are building a single one of the q's, the result is built initially as
   407  // { q1: {...} }
   408  // so the completed result should be
   409  // q1: {...}
   410  func removeObjectCompletion(cf CompletionFunc) CompletionFunc {
   411  	return CompletionFunc(
   412  		func(ctx context.Context, field schema.Field, result []byte, err error) ([]byte, error) {
   413  			res, err := cf(ctx, field, result, err)
   414  			if len(res) >= 2 {
   415  				res = res[1 : len(res)-1]
   416  			}
   417  			return res, err
   418  		})
   419  }
   420  
   421  // addRootFieldCompletion adds an extra object name to the start of a result.
   422  //
   423  // A mutation always looks like
   424  //   `addFoo(...) { foo { ... } }`
   425  // What's resolved initially is
   426  //   `foo { ... }`
   427  // So `addFoo: ...` is added.
   428  func addRootFieldCompletion(name string, cf CompletionFunc) CompletionFunc {
   429  	return CompletionFunc(func(
   430  		ctx context.Context, field schema.Field, result []byte, err error) ([]byte, error) {
   431  
   432  		res, err := cf(ctx, field, result, err)
   433  
   434  		var b bytes.Buffer
   435  		x.Check2(b.WriteString("\""))
   436  		x.Check2(b.WriteString(name))
   437  		x.Check2(b.WriteString(`": `))
   438  		if len(res) > 0 {
   439  			x.Check2(b.Write(res))
   440  		} else {
   441  			x.Check2(b.WriteString("null"))
   442  		}
   443  
   444  		return b.Bytes(), err
   445  	})
   446  }
   447  
   448  // addPathCompletion adds an extra object name to the start of every error path
   449  // arrising from applying cf.
   450  //
   451  // A mutation always looks like
   452  //   `addFoo(...) { foo { ... } }`
   453  // But cf's error paths begin at `foo`, so `addFoo` needs to be added to all.
   454  func addPathCompletion(name string, cf CompletionFunc) CompletionFunc {
   455  	return CompletionFunc(func(
   456  		ctx context.Context, field schema.Field, result []byte, err error) ([]byte, error) {
   457  
   458  		res, err := cf(ctx, field, result, err)
   459  
   460  		resErrs := schema.AsGQLErrors(err)
   461  		for _, err := range resErrs {
   462  			if len(err.Path) > 0 {
   463  				err.Path = append([]interface{}{name}, err.Path...)
   464  			}
   465  		}
   466  
   467  		return res, resErrs
   468  	})
   469  }
   470  
   471  // Once a result has been returned from Dgraph, that result needs to be worked
   472  // through for two main reasons:
   473  //
   474  // 1) (null insertion)
   475  //    Where an edge was requested in a query, but isn't in the store, Dgraph just
   476  //    won't return an edge for that in the results.  But GraphQL wants those as
   477  //    "null" in the result.  And then we need to inspect those nulls via pt (2)
   478  //
   479  // 2) (error propagation)
   480  //    The schema is a contract with consumers.  So if there's an `f: T!` in the
   481  //    schema, that says: "this API never returns a null f".  If f turned out null
   482  //    in the results, then returning null would break the contract.  GraphQL specifies
   483  //    a set of rules about how to propagate and record those errors.
   484  //
   485  //    The basic intuition is that if we asked for something that's nullable and we
   486  //    got back a null/error, then that's fine, just set it to null.  But if we asked
   487  //    for something non-nullable and got a null/error, then the object we are building
   488  //    is in an error state, and we should propagate that up to it's parent, and so
   489  //    on, until we reach a nullable field, or the top level.
   490  //
   491  // The completeXYZ() functions below essentially covers the value completion alg from
   492  // https://graphql.github.io/graphql-spec/June2018/#sec-Value-Completion.
   493  // see also: error propagation
   494  // https://graphql.github.io/graphql-spec/June2018/#sec-Errors-and-Non-Nullability
   495  // and the spec requirements for response
   496  // https://graphql.github.io/graphql-spec/June2018/#sec-Response.
   497  //
   498  // There's three basic types to consider here: GraphQL object types (equals json
   499  // objects in the result), list types (equals lists of objects or scalars), and
   500  // values (either scalar values, lists or objects).
   501  //
   502  // So the algorithm is a three way mutual recursion between those types.
   503  //
   504  // That works like this... if part of the json result from Dgraph
   505  // looked like:
   506  //
   507  // {
   508  //   "name": "A name"
   509  //   "friends": [
   510  //     { "name": "Friend 1"},
   511  //     { "name": "Friend 2", "friends": [...] }
   512  //   ]
   513  // }
   514  //
   515  // Then, schematically, the recursion tree would look like:
   516  //
   517  // completeObject ( {
   518  //   "name": completeValue("A name")
   519  //   "friends": completeValue( completeList([
   520  //     completeValue (completeObject ({ "name": completeValue ("Friend 1")} )),
   521  //     completeValue (completeObject ({
   522  //                           "name": completeValue("Friend 2"),
   523  //                           "friends": completeValue ( completeList([ completeObject(..), ..]) } )
   524  //
   525  
   526  // completeDgraphResult starts the recursion with field as the top level GraphQL
   527  // query and dgResult as the matching full Dgraph result.  Always returns a valid
   528  // JSON []byte of the form
   529  //   { "query-name": null }
   530  // if there's no result, or
   531  //   { "query-name": ... }
   532  // if there is a result.
   533  //
   534  // Returned errors are generally lists of errors resulting from the value completion
   535  // algorithm that may emit multiple errors
   536  func completeDgraphResult(ctx context.Context, field schema.Field, dgResult []byte, e error) (
   537  	[]byte, error) {
   538  	span := trace.FromContext(ctx)
   539  	stop := x.SpanTimer(span, "completeDgraphResult")
   540  	defer stop()
   541  
   542  	// We need an initial case in the alg because Dgraph always returns a list
   543  	// result no matter what.
   544  	//
   545  	// If the query was for a non-list type, that needs to be corrected:
   546  	//
   547  	//   { "q":[{ ... }] }  --->  { "q":{ ... } }
   548  	//
   549  	// Also, if the query found nothing at all, that needs correcting too:
   550  	//
   551  	//    { }  --->  { "q": null }
   552  
   553  	errs := schema.AsGQLErrors(e)
   554  	if len(dgResult) == 0 {
   555  		return nil, errs
   556  	}
   557  
   558  	nullResponse := func() []byte {
   559  		var buf bytes.Buffer
   560  		x.Check2(buf.WriteString(`{ "`))
   561  		x.Check2(buf.WriteString(field.ResponseName()))
   562  		x.Check2(buf.WriteString(`": null }`))
   563  		return buf.Bytes()
   564  	}
   565  
   566  	dgraphError := func() ([]byte, error) {
   567  		glog.Errorf("[%s] Could not process Dgraph result : \n%s",
   568  			api.RequestID(ctx), string(dgResult))
   569  		return nullResponse(),
   570  			x.GqlErrorf("Couldn't process the result from Dgraph.  " +
   571  				"This probably indicates a bug in the Dgraph GraphQL layer.  " +
   572  				"Please let us know : https://github.com/dgraph-io/dgraph/issues.").
   573  				WithLocations(field.Location())
   574  	}
   575  
   576  	// Dgraph should only return {} or a JSON object.  Also,
   577  	// GQL type checking should ensure query results are only object types
   578  	// https://graphql.github.io/graphql-spec/June2018/#sec-Query
   579  	// So we are only building object results.
   580  	var valToComplete map[string]interface{}
   581  	err := json.Unmarshal(dgResult, &valToComplete)
   582  	if err != nil {
   583  		glog.Errorf("[%s] %+v \n Dgraph result :\n%s\n",
   584  			api.RequestID(ctx),
   585  			errors.Wrap(err, "failed to unmarshal Dgraph query result"),
   586  			string(dgResult))
   587  		return nullResponse(),
   588  			schema.GQLWrapLocationf(err, field.Location(), "couldn't unmarshal Dgraph result")
   589  	}
   590  
   591  	switch val := valToComplete[field.ResponseName()].(type) {
   592  	case []interface{}:
   593  		if field.Type().ListType() == nil {
   594  			// Turn Dgraph list result to single object
   595  			// "q":[{ ... }] ---> "q":{ ... }
   596  
   597  			var internalVal interface{}
   598  
   599  			if len(val) > 0 {
   600  				var ok bool
   601  				if internalVal, ok = val[0].(map[string]interface{}); !ok {
   602  					// This really shouldn't happen. Dgraph only returns arrays
   603  					// of json objects.
   604  					return dgraphError()
   605  				}
   606  			}
   607  
   608  			if len(val) > 1 {
   609  				// If we get here, then we got a list result for a query that expected
   610  				// a single item.  That probably indicates a schema error, or maybe
   611  				// a bug in GraphQL processing or some data corruption.
   612  				//
   613  				// We'll continue and just try the first item to return some data.
   614  
   615  				glog.Errorf("[%s] Got a list of length %v from Dgraph when expecting a "+
   616  					"one-item list.\n"+
   617  					"GraphQL query was : %s\n",
   618  					api.RequestID(ctx), len(val), api.QueryString(ctx))
   619  
   620  				errs = append(errs,
   621  					x.GqlErrorf(
   622  						"Dgraph returned a list, but %s (type %s) was expecting just one item.  "+
   623  							"The first item in the list was used to produce the result. "+
   624  							"Logged as a potential bug; see the API log for more details.",
   625  						field.Name(), field.Type().String()).WithLocations(field.Location()))
   626  			}
   627  
   628  			valToComplete[field.ResponseName()] = internalVal
   629  		}
   630  	default:
   631  		if val != nil {
   632  			return dgraphError()
   633  		}
   634  
   635  		// valToComplete[field.ResponseName()] is nil, so resolving for the
   636  		// { } ---> "q": null
   637  		// case
   638  	}
   639  
   640  	// Errors should report the "path" into the result where the error was found.
   641  	//
   642  	// The definition of a path in a GraphQL error is here:
   643  	// https://graphql.github.io/graphql-spec/June2018/#sec-Errors
   644  	// For a query like (assuming field f is of a list type and g is a scalar type):
   645  	// - q { f { g } }
   646  	// a path to the 2nd item in the f list would look like:
   647  	// - [ "q", "f", 2, "g" ]
   648  	path := make([]interface{}, 0, maxPathLength(field))
   649  
   650  	completed, gqlErrs := completeObject(
   651  		path, field.Type(), []schema.Field{field}, valToComplete)
   652  
   653  	if len(completed) < 2 {
   654  		// This could only occur completeObject crushed the whole query, but
   655  		// that should never happen because the result type shouldn't be '!'.
   656  		// We should wrap enough testing around the schema generation that this
   657  		// just can't happen.
   658  		//
   659  		// This isn't really an observable GraphQL error, so no need to add anything
   660  		// to the payload of errors for the result.
   661  		glog.Errorf("[%s] Top level completeObject didn't return a result.  "+
   662  			"That's only possible if the query result is non-nullable.  "+
   663  			"There's something wrong in the GraphQL schema.  \n"+
   664  			"GraphQL query was : %s\n",
   665  			api.RequestID(ctx), api.QueryString(ctx))
   666  		return nullResponse(), append(errs, gqlErrs...)
   667  	}
   668  
   669  	return completed, append(errs, gqlErrs...)
   670  }
   671  
   672  // completeObject builds a json GraphQL result object for the current query level.
   673  // It returns a bracketed json object like { f1:..., f2:..., ... }.
   674  //
   675  // fields are all the fields from this bracketed level in the GraphQL  query, e.g:
   676  // {
   677  //   name
   678  //   dob
   679  //   friends {...}
   680  // }
   681  // If it's the top level of a query then it'll be the top level query name.
   682  //
   683  // typ is the expected type matching those fields, e.g. above that'd be something
   684  // like the `Person` type that has fields name, dob and friends.
   685  //
   686  // res is the results map from Dgraph for this level of the query.  This map needn't
   687  // contain values for all the requested fields, e.g. if there's no corresponding
   688  // values in the store or if the query contained a filter that excluded a value.
   689  // So res might be the map : name->"A Name", friends -> []interface{}
   690  //
   691  // completeObject fills out this result putting in null for any missing values
   692  // (dob above) and applying GraphQL error propagation for any null fields that the
   693  // schema says can't be null.
   694  //
   695  // Example:
   696  //
   697  // if the map is name->"A Name", friends -> []interface{}
   698  //
   699  // and "dob" is nullable then the result should be json object
   700  // {"name": "A Name", "dob": null, "friends": ABC}
   701  // where ABC is the result of applying completeValue to res["friends"]
   702  //
   703  // if "dob" were non-nullable (maybe it's type is DateTime!), then the result is
   704  // nil and the error propagates to the enclosing level.
   705  func completeObject(
   706  	path []interface{},
   707  	typ schema.Type,
   708  	fields []schema.Field,
   709  	res map[string]interface{}) ([]byte, x.GqlErrorList) {
   710  
   711  	var errs x.GqlErrorList
   712  	var buf bytes.Buffer
   713  	comma := ""
   714  
   715  	x.Check2(buf.WriteRune('{'))
   716  
   717  	dgraphTypes, ok := res["dgraph.type"].([]interface{})
   718  	for _, f := range fields {
   719  		if f.Skip() || !f.Include() {
   720  			continue
   721  		}
   722  
   723  		includeField := true
   724  		// If typ is an interface, and dgraphTypes contains another type, then we ignore
   725  		// fields which don't start with that type. This would happen when multiple
   726  		// fragments (belonging to different types) are requested within a query for an interface.
   727  
   728  		// If the dgraphPredicate doesn't start with the typ.Name(), then this field belongs to
   729  		// a concrete type, lets check that it has inputType as the prefix, otherwise skip it.
   730  		if len(dgraphTypes) > 0 {
   731  			includeField = f.IncludeInterfaceField(dgraphTypes)
   732  		}
   733  		if !includeField {
   734  			continue
   735  		}
   736  
   737  		x.Check2(buf.WriteString(comma))
   738  		x.Check2(buf.WriteRune('"'))
   739  		x.Check2(buf.WriteString(f.ResponseName()))
   740  		x.Check2(buf.WriteString(`": `))
   741  
   742  		val := res[f.ResponseName()]
   743  		if f.Name() == schema.Typename {
   744  			// From GraphQL spec:
   745  			// https://graphql.github.io/graphql-spec/June2018/#sec-Type-Name-Introspection
   746  			// "GraphQL supports type name introspection at any point within a query by the
   747  			// meta‐field  __typename: String! when querying against any Object, Interface,
   748  			// or Union. It returns the name of the object type currently being queried."
   749  
   750  			// If we have dgraph.type information, we will use that to figure out the type
   751  			// otherwise we will get it from the schema.
   752  			if ok {
   753  				val = f.TypeName(dgraphTypes)
   754  			} else {
   755  				val = f.GetObjectName()
   756  			}
   757  		}
   758  
   759  		completed, err := completeValue(append(path, f.ResponseName()), f, val)
   760  		errs = append(errs, err...)
   761  		if completed == nil {
   762  			if !f.Type().Nullable() {
   763  				return nil, errs
   764  			}
   765  			completed = []byte(`null`)
   766  		}
   767  		x.Check2(buf.Write(completed))
   768  		comma = ", "
   769  	}
   770  	x.Check2(buf.WriteRune('}'))
   771  
   772  	return buf.Bytes(), errs
   773  }
   774  
   775  // completeValue applies the value completion algorithm to a single value, which
   776  // could turn out to be a list or object or scalar value.
   777  func completeValue(
   778  	path []interface{},
   779  	field schema.Field,
   780  	val interface{}) ([]byte, x.GqlErrorList) {
   781  
   782  	switch val := val.(type) {
   783  	case map[string]interface{}:
   784  		return completeObject(path, field.Type(), field.SelectionSet(), val)
   785  	case []interface{}:
   786  		return completeList(path, field, val)
   787  	default:
   788  		if val == nil {
   789  			if field.Type().ListType() != nil {
   790  				// We could choose to set this to null.  This is our decision, not
   791  				// anything required by the GraphQL spec.
   792  				//
   793  				// However, if we query, for example, for a persons's friends with
   794  				// some restrictions, and there aren't any, is that really a case to
   795  				// set this at null and error if the list is required?  What
   796  				// about if an person has just been added and doesn't have any friends?
   797  				// Doesn't seem right to add null and cause error propagation.
   798  				//
   799  				// Seems best if we pick [], rather than null, as the list value if
   800  				// there's nothing in the Dgraph result.
   801  				return []byte("[]"), nil
   802  			}
   803  
   804  			if field.Type().Nullable() {
   805  				return []byte("null"), nil
   806  			}
   807  
   808  			gqlErr := x.GqlErrorf(
   809  				"Non-nullable field '%s' (type %s) was not present in result from Dgraph.  "+
   810  					"GraphQL error propagation triggered.", field.Name(), field.Type()).
   811  				WithLocations(field.Location())
   812  			gqlErr.Path = copyPath(path)
   813  
   814  			return nil, x.GqlErrorList{gqlErr}
   815  		}
   816  
   817  		// val is a scalar
   818  
   819  		// Can this ever error?  We can't have an unsupported type or value because
   820  		// we just unmarshaled this val.
   821  		json, err := json.Marshal(val)
   822  		if err != nil {
   823  			gqlErr := x.GqlErrorf(
   824  				"Error marshalling value for field '%s' (type %s).  "+
   825  					"Resolved as null (which may trigger GraphQL error propagation) ",
   826  				field.Name(), field.Type()).
   827  				WithLocations(field.Location())
   828  			gqlErr.Path = copyPath(path)
   829  
   830  			if field.Type().Nullable() {
   831  				return []byte("null"), x.GqlErrorList{gqlErr}
   832  			}
   833  
   834  			return nil, x.GqlErrorList{gqlErr}
   835  		}
   836  
   837  		return json, nil
   838  	}
   839  }
   840  
   841  // completeList applies the completion algorithm to a list field and result.
   842  //
   843  // field is one field from the query - which should have a list type in the
   844  // GraphQL schema.
   845  //
   846  // values is the list of values found by the query for this field.
   847  //
   848  // completeValue() is applied to every list element, but
   849  // the type of field can only be a scalar list like [String], or an object
   850  // list like [Person], so schematically the final result is either
   851  // [ completValue("..."), completValue("..."), ... ]
   852  // or
   853  // [ completeObject({...}), completeObject({...}), ... ]
   854  // depending on the type of list.
   855  //
   856  // If the list has non-nullable elements (a type like [T!]) and any of those
   857  // elements resolve to null, then the whole list is crushed to null.
   858  func completeList(
   859  	path []interface{},
   860  	field schema.Field,
   861  	values []interface{}) ([]byte, x.GqlErrorList) {
   862  
   863  	var buf bytes.Buffer
   864  	var errs x.GqlErrorList
   865  	comma := ""
   866  
   867  	if field.Type().ListType() == nil {
   868  		// This means a bug on our part - in rewriting, schema generation,
   869  		// or Dgraph returned something unexpected.
   870  		//
   871  		// Let's crush it to null so we still get something from the rest of the
   872  		// query and log the error.
   873  		return mismatched(path, field, values)
   874  	}
   875  
   876  	x.Check2(buf.WriteRune('['))
   877  	for i, b := range values {
   878  		r, err := completeValue(append(path, i), field, b)
   879  		errs = append(errs, err...)
   880  		x.Check2(buf.WriteString(comma))
   881  		if r == nil {
   882  			if !field.Type().ListType().Nullable() {
   883  				// Unlike the choice in completeValue() above, where we turn missing
   884  				// lists into [], the spec explicitly calls out:
   885  				//  "If a List type wraps a Non-Null type, and one of the
   886  				//  elements of that list resolves to null, then the entire list
   887  				//  must resolve to null."
   888  				//
   889  				// The list gets reduced to nil, but an error recording that must
   890  				// already be in errs.  See
   891  				// https://graphql.github.io/graphql-spec/June2018/#sec-Errors-and-Non-Nullability
   892  				// "If the field returns null because of an error which has already
   893  				// been added to the "errors" list in the response, the "errors"
   894  				// list must not be further affected."
   895  				// The behavior is also in the examples in here:
   896  				// https://graphql.github.io/graphql-spec/June2018/#sec-Errors
   897  				return nil, errs
   898  			}
   899  			x.Check2(buf.WriteString("null"))
   900  		} else {
   901  			x.Check2(buf.Write(r))
   902  		}
   903  		comma = ", "
   904  	}
   905  	x.Check2(buf.WriteRune(']'))
   906  
   907  	return buf.Bytes(), errs
   908  }
   909  
   910  func mismatched(
   911  	path []interface{},
   912  	field schema.Field,
   913  	values []interface{}) ([]byte, x.GqlErrorList) {
   914  
   915  	glog.Errorf("completeList() called in resolving %s (Line: %v, Column: %v), "+
   916  		"but its type is %s.\n"+
   917  		"That could indicate the Dgraph schema doesn't match the GraphQL schema.",
   918  		field.Name(), field.Location().Line, field.Location().Column, field.Type().Name())
   919  
   920  	gqlErr := &x.GqlError{
   921  		Message: "Dgraph returned a list, but GraphQL was expecting just one item.  " +
   922  			"This indicates an internal error - " +
   923  			"probably a mismatch between GraphQL and Dgraph schemas.  " +
   924  			"The value was resolved as null (which may trigger GraphQL error propagation) " +
   925  			"and as much other data as possible returned.",
   926  		Locations: []x.Location{field.Location()},
   927  		Path:      copyPath(path),
   928  	}
   929  
   930  	val, errs := completeValue(path, field, nil)
   931  	return val, append(errs, gqlErr)
   932  }
   933  
   934  func copyPath(path []interface{}) []interface{} {
   935  	result := make([]interface{}, len(path))
   936  	copy(result, path)
   937  	return result
   938  }
   939  
   940  // maxPathLength finds the max length (including list indexes) of any path in the 'query' f.
   941  // Used to pre-allocate a path buffer of the correct size before running completeObject on
   942  // the top level query - means that we aren't reallocating slices multiple times
   943  // during the complete* functions.
   944  func maxPathLength(f schema.Field) int {
   945  	childMax := 0
   946  	for _, chld := range f.SelectionSet() {
   947  		d := maxPathLength(chld)
   948  		if d > childMax {
   949  			childMax = d
   950  		}
   951  	}
   952  	if f.Type().ListType() != nil {
   953  		// It's f: [...], so add a space for field name and
   954  		// a space for the index into the list
   955  		return 2 + childMax
   956  	}
   957  
   958  	return 1 + childMax
   959  }