Example #1
0
        public static IRequestExecutorBuilder AddRemoteSchema(
            this IRequestExecutorBuilder builder,
            NameString schemaName,
            Func <IServiceProvider, CancellationToken, ValueTask <RemoteSchemaDefinition> > 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.
                RemoteSchemaDefinition schemaDef =
                    await loadSchema(services, cancellationToken)
                    .ConfigureAwait(false);

                DocumentNode document = schemaDef.Document.RemoveBuiltInTypes();

                // We store the schema definition on the schema building context
                // and copy it to the schema once that is built.
                schemaBuilder
                .SetContextData(typeof(RemoteSchemaDefinition).FullName !, schemaDef)
                .TryAddTypeInterceptor <CopySchemaDefinitionTypeInterceptor>();

                // The document is used to create a SDL-first schema ...
                schemaBuilder.AddDocument(document);

                // ... which will fail if any resolver is actually used ...
                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>();

                var schemaDefinition =
                    (RemoteSchemaDefinition)autoProxy.Schema
                    .ContextData[typeof(RemoteSchemaDefinition).FullName !] !;

                var extensionsRewriter = new SchemaExtensionsRewriter();

                foreach (var extensionDocument in schemaDefinition.ExtensionDocuments)
                {
                    var doc = (DocumentNode)extensionsRewriter
                              .Rewrite(extensionDocument, schemaName.Value);

                    SchemaExtensionNode?schemaExtension =
                        doc.Definitions.OfType <SchemaExtensionNode>().FirstOrDefault();

                    if (schemaExtension is not null &&
                        schemaExtension.Directives.Count == 0 &&
                        schemaExtension.OperationTypes.Count == 0)
                    {
                        var definitions = doc.Definitions.ToList();
                        definitions.Remove(schemaExtension);
                        doc = doc.WithDefinitions(definitions);
                    }

                    schemaBuilder.AddTypeExtensions(doc);
                }

                schemaBuilder.AddTypeRewriter(
                    new RemoveFieldRewriter(
                        new FieldReference(
                            autoProxy.Schema.QueryType.Name,
                            SchemaDefinitionFieldNames.SchemaDefinitionField),
                        schemaName));

                schemaBuilder.AddDocumentRewriter(
                    new RemoveTypeRewriter(
                        SchemaDefinitionType.Names.SchemaDefinition,
                        schemaName));

                foreach (var schemaAction in extensionsRewriter.SchemaActions)
                {
                    switch (schemaAction.Name.Value)
                    {
                    case DirectiveNames.RemoveRootTypes:
                        schemaBuilder.AddDocumentRewriter(
                            new RemoveRootTypeRewriter(schemaName));
                        break;

                    case DirectiveNames.RemoveType:
                        schemaBuilder.AddDocumentRewriter(
                            new RemoveTypeRewriter(
                                GetArgumentValue(
                                    schemaAction,
                                    DirectiveFieldNames.RemoveType_TypeName),
                                schemaName));
                        break;

                    case DirectiveNames.RenameType:
                        schemaBuilder.AddTypeRewriter(
                            new RenameTypeRewriter(
                                GetArgumentValue(
                                    schemaAction,
                                    DirectiveFieldNames.RenameType_TypeName),
                                GetArgumentValue(
                                    schemaAction,
                                    DirectiveFieldNames.RenameType_NewTypeName),
                                schemaName));
                        break;

                    case DirectiveNames.RenameField:
                        schemaBuilder.AddTypeRewriter(
                            new RenameFieldRewriter(
                                new FieldReference(
                                    GetArgumentValue(
                                        schemaAction,
                                        DirectiveFieldNames.RenameField_TypeName),
                                    GetArgumentValue(
                                        schemaAction,
                                        DirectiveFieldNames.RenameField_FieldName)),
                                GetArgumentValue(
                                    schemaAction,
                                    DirectiveFieldNames.RenameField_NewFieldName),
                                schemaName));
                        break;
                    }
                }
            });
        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);
        }