github.com/attic-labs/noms@v0.0.0-20210827224422-e5fa29d95e8b/go/ngql/query.go (about)

     1  // Copyright 2017 Attic Labs, Inc. All rights reserved.
     2  // Licensed under the Apache License, version 2.0:
     3  // http://www.apache.org/licenses/LICENSE-2.0
     4  
     5  package ngql
     6  
     7  import (
     8  	"context"
     9  	"encoding/json"
    10  	"io"
    11  
    12  	"github.com/attic-labs/graphql"
    13  	"github.com/attic-labs/graphql/gqlerrors"
    14  	"github.com/attic-labs/noms/go/d"
    15  	"github.com/attic-labs/noms/go/types"
    16  )
    17  
    18  const (
    19  	atKey          = "at"
    20  	countKey       = "count"
    21  	elementsKey    = "elements"
    22  	entriesKey     = "entries"
    23  	keyKey         = "key"
    24  	keysKey        = "keys"
    25  	rootKey        = "root"
    26  	rootQueryKey   = "Root"
    27  	scalarValue    = "scalarValue"
    28  	sizeKey        = "size"
    29  	targetHashKey  = "targetHash"
    30  	targetValueKey = "targetValue"
    31  	throughKey     = "through"
    32  	valueKey       = "value"
    33  	valuesKey      = "values"
    34  	vrwKey         = "vrw"
    35  )
    36  
    37  // NewRootQueryObject creates a "root" query object that can be used to
    38  // traverse the value tree of rootValue.
    39  func NewRootQueryObject(rootValue types.Value, tm *TypeMap) *graphql.Object {
    40  	tc := TypeConverter{*tm, DefaultNameFunc}
    41  	return tc.NewRootQueryObject(rootValue)
    42  }
    43  
    44  // NewRootQueryObject creates a "root" query object that can be used to
    45  // traverse the value tree of rootValue.
    46  func (tc *TypeConverter) NewRootQueryObject(rootValue types.Value) *graphql.Object {
    47  	rootNomsType := types.TypeOf(rootValue)
    48  	rootType := tc.NomsTypeToGraphQLType(rootNomsType)
    49  
    50  	return graphql.NewObject(graphql.ObjectConfig{
    51  		Name: rootQueryKey,
    52  		Fields: graphql.Fields{
    53  			rootKey: &graphql.Field{
    54  				Type: rootType,
    55  				Resolve: func(p graphql.ResolveParams) (interface{}, error) {
    56  					return MaybeGetScalar(rootValue), nil
    57  				},
    58  			},
    59  		}})
    60  }
    61  
    62  // NewContext creates a new context.Context with the extra data added to it
    63  // that is required by ngql.
    64  func NewContext(vrw types.ValueReader) context.Context {
    65  	return context.WithValue(context.Background(), vrwKey, vrw)
    66  }
    67  
    68  // Query takes |rootValue|, builds a GraphQL scheme from rootValue.Type() and
    69  // executes |query| against it, encoding the result to |w|.
    70  func Query(rootValue types.Value, query string, vrw types.ValueReadWriter, w io.Writer) {
    71  	schemaConfig := graphql.SchemaConfig{}
    72  	tc := NewTypeConverter()
    73  	queryWithSchemaConfig(rootValue, query, schemaConfig, vrw, tc, w)
    74  }
    75  
    76  func queryWithSchemaConfig(rootValue types.Value, query string, schemaConfig graphql.SchemaConfig, vrw types.ValueReadWriter, tc *TypeConverter, w io.Writer) {
    77  	schemaConfig.Query = tc.NewRootQueryObject(rootValue)
    78  	schema, _ := graphql.NewSchema(schemaConfig)
    79  	ctx := NewContext(vrw)
    80  
    81  	r := graphql.Do(graphql.Params{
    82  		Schema:        schema,
    83  		RequestString: query,
    84  		Context:       ctx,
    85  	})
    86  
    87  	err := json.NewEncoder(w).Encode(r)
    88  	d.PanicIfError(err)
    89  }
    90  
    91  // Error writes an error as a GraphQL error to a writer.
    92  func Error(err error, w io.Writer) {
    93  	r := graphql.Result{
    94  		Errors: []gqlerrors.FormattedError{
    95  			{Message: err.Error()},
    96  		},
    97  	}
    98  
    99  	jsonErr := json.NewEncoder(w).Encode(r)
   100  	d.PanicIfError(jsonErr)
   101  }