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);
        }
示例#2
0
            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);
        }