public override void OnBeforeCreate( IDescriptorContext context, ISchemaBuilder schemaBuilder) { var allSchemas = new OrderedDictionary <NameString, DocumentNode>(); foreach (KeyValuePair <NameString, IRequestExecutor> executor in context.GetRemoteExecutors()) { allSchemas.Add( executor.Key, Utf8GraphQLParser.Parse(executor.Value.Schema.Print())); } IReadOnlyList <DocumentNode> typeExtensions = context.GetTypeExtensions(); // merge schemas DocumentNode mergedSchema = MergeSchemas(context, allSchemas); mergedSchema = AddExtensions(mergedSchema, typeExtensions); mergedSchema = RewriteMerged(context, mergedSchema); mergedSchema = mergedSchema.RemoveBuiltInTypes(); VisitMerged(context, mergedSchema); MarkExternalFields(schemaBuilder, mergedSchema); BuildNameLookup(context, schemaBuilder, mergedSchema, allSchemas.Keys); schemaBuilder .AddDocument(mergedSchema) .AddDirectiveType <DelegateDirectiveType>() .AddDirectiveType <ComputedDirectiveType>() .AddDirectiveType <SourceDirectiveType>() .SetTypeResolver(IsOfTypeFallback); }
private static IEnumerable <ITypeReference> ParseDocuments( SchemaBuilder builder, IServiceProvider services, IReadOnlySchemaOptions options, IBindingLookup bindingLookup) { var types = new List <ITypeReference>(); foreach (LoadSchemaDocument fetchSchema in builder._documents) { DocumentNode schemaDocument = fetchSchema(services); schemaDocument = schemaDocument.RemoveBuiltInTypes(); var visitorContext = new SchemaSyntaxVisitorContext(bindingLookup, options); var visitor = new SchemaSyntaxVisitor(); visitor.Visit(schemaDocument, visitorContext); types.AddRange(visitorContext.Types); RegisterOperationName( builder, OperationType.Query, visitorContext.QueryTypeName); RegisterOperationName( builder, OperationType.Mutation, visitorContext.MutationTypeName); RegisterOperationName( builder, OperationType.Subscription, visitorContext.SubscriptionTypeName); IReadOnlyCollection <DirectiveNode> directives = visitorContext.Directives ?? Array.Empty <DirectiveNode>(); if (builder._schema is null && (directives.Count > 0 || visitorContext.Description != null)) { builder.SetSchema(new Schema(d => { d.Description(visitorContext.Description); foreach (DirectiveNode directive in directives) { d.Directive(directive); } })); } } return(types); }
public static IRequestExecutorBuilder AddRemoteSchema( this IRequestExecutorBuilder builder, NameString schemaName, Func <IServiceProvider, CancellationToken, ValueTask <DocumentNode> > loadSchema, bool ignoreRootTypes = false) { if (builder is null) { throw new ArgumentNullException(nameof(builder)); } if (loadSchema is null) { throw new ArgumentNullException(nameof(loadSchema)); } schemaName.EnsureNotEmpty(nameof(schemaName)); // first we add a full GraphQL schema and executor that represents the remote schema. // This remote schema will be used by the stitching engine to execute queries against // this schema and also to lookup types in order correctly convert between scalars. builder .AddGraphQL(schemaName) .ConfigureSchemaServices(services => { services.TryAddSingleton( sp => new HttpRequestClient( sp.GetApplicationService <IHttpClientFactory>(), sp.GetRequiredService <IErrorHandler>(), sp.GetRequiredService <IHttpStitchingRequestInterceptor>())); services.TryAddSingleton < IHttpStitchingRequestInterceptor, HttpStitchingRequestInterceptor>(); }) .ConfigureSchemaAsync( async(services, schemaBuilder, cancellationToken) => { // No we need to load the schema document. DocumentNode document = await loadSchema(services, cancellationToken) .ConfigureAwait(false); document = document.RemoveBuiltInTypes(); // The document is used to create a SDL-first schema ... schemaBuilder.AddDocument(document); // ... which will fail if any resolver is actually used ... // todo : how to bind resolvers schemaBuilder.Use(next => context => throw new NotSupportedException()); }) // ... instead we are using a special request pipeline that does everything like // the standard pipeline except the last middleware will not start the execution // algorithms but delegate the request via HTTP to the downstream schema. .UseHttpRequestPipeline(); // Next, we will register a request executor proxy with the stitched schema, // that the stitching runtime will use to send requests to the schema representing // the downstream service. builder .ConfigureSchemaAsync(async(services, schemaBuilder, cancellationToken) => { IInternalRequestExecutorResolver noLockExecutorResolver = services.GetRequiredService <IInternalRequestExecutorResolver>(); IRequestExecutor executor = await noLockExecutorResolver .GetRequestExecutorNoLockAsync(schemaName, cancellationToken) .ConfigureAwait(false); var autoProxy = AutoUpdateRequestExecutorProxy.Create( new RequestExecutorProxy( services.GetRequiredService <IRequestExecutorResolver>(), schemaName), executor); schemaBuilder .AddRemoteExecutor(schemaName, autoProxy) .TryAddSchemaInterceptor <StitchingSchemaInterceptor>() .TryAddTypeInterceptor <StitchingTypeInterceptor>(); }); // Last but not least, we will setup the stitching context which will // provide access to the remote executors which in turn use the just configured // request executor proxies to send requests to the downstream services. builder.Services.TryAddScoped <IStitchingContext, StitchingContext>(); if (ignoreRootTypes) { builder.AddDocumentRewriter(new RemoveRootTypeRewriter(schemaName)); } return(builder); }