github.com/weaviate/weaviate@v1.24.6/adapters/handlers/graphql/schema.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 // Package graphql provides the graphql endpoint for Weaviate 13 package graphql 14 15 import ( 16 "context" 17 "fmt" 18 "runtime/debug" 19 20 "github.com/sirupsen/logrus" 21 "github.com/tailor-inc/graphql" 22 "github.com/weaviate/weaviate/adapters/handlers/graphql/local" 23 "github.com/weaviate/weaviate/adapters/handlers/graphql/local/get" 24 "github.com/weaviate/weaviate/entities/schema" 25 "github.com/weaviate/weaviate/usecases/config" 26 "github.com/weaviate/weaviate/usecases/modules" 27 ) 28 29 type Traverser interface { 30 local.Resolver 31 } 32 33 type RequestsLogger interface { 34 get.RequestsLog 35 } 36 37 // The communication interface between the REST API and the GraphQL API. 38 type GraphQL interface { 39 // Resolve the GraphQL query in 'query'. 40 Resolve(context context.Context, query string, operationName string, variables map[string]interface{}) *graphql.Result 41 } 42 43 type graphQL struct { 44 schema graphql.Schema 45 traverser Traverser 46 config config.Config 47 } 48 49 // Construct a GraphQL API from the database schema, and resolver interface. 50 func Build(schema *schema.Schema, traverser Traverser, 51 logger logrus.FieldLogger, config config.Config, modulesProvider *modules.Provider, 52 ) (GraphQL, error) { 53 logger.WithField("action", "graphql_rebuild"). 54 WithField("schema", schema). 55 Debug("rebuilding the graphql schema") 56 57 graphqlSchema, err := buildGraphqlSchema(schema, logger, config, modulesProvider) 58 if err != nil { 59 return nil, err 60 } 61 62 return &graphQL{ 63 schema: graphqlSchema, 64 traverser: traverser, 65 config: config, 66 }, nil 67 } 68 69 // Resolve at query time 70 func (g *graphQL) Resolve(context context.Context, query string, operationName string, variables map[string]interface{}) *graphql.Result { 71 return graphql.Do(graphql.Params{ 72 Schema: g.schema, 73 RootObject: map[string]interface{}{ 74 "Resolver": g.traverser, 75 "Config": g.config, 76 }, 77 RequestString: query, 78 OperationName: operationName, 79 VariableValues: variables, 80 Context: context, 81 }) 82 } 83 84 func buildGraphqlSchema(dbSchema *schema.Schema, logger logrus.FieldLogger, 85 config config.Config, modulesProvider *modules.Provider, 86 ) (graphql.Schema, error) { 87 localSchema, err := local.Build(dbSchema, logger, config, modulesProvider) 88 if err != nil { 89 return graphql.Schema{}, err 90 } 91 92 schemaObject := graphql.ObjectConfig{ 93 Name: "WeaviateObj", 94 Description: "Location of the root query", 95 Fields: localSchema, 96 } 97 98 // Run graphql.NewSchema in a sub-closure, so that we can recover from panics. 99 // We need to use panics to return errors deep inside the dynamic generation of the GraphQL schema, 100 // inside the FieldThunks. There is _no_ way to bubble up an error besides panicking. 101 var result graphql.Schema 102 func() { 103 defer func() { 104 if r := recover(); r != nil { 105 err = fmt.Errorf("%v at %s", r, debug.Stack()) 106 } 107 }() 108 109 result, err = graphql.NewSchema(graphql.SchemaConfig{ 110 Query: graphql.NewObject(schemaObject), 111 }) 112 }() 113 114 if err != nil { 115 return graphql.Schema{}, fmt.Errorf("Could not build GraphQL schema, because: %v", err) 116 } 117 118 return result, nil 119 }