private async Task CreateFactoryOptionsAsync( RemoteSchemaDefinition schemaDefinition, IList <IConfigureRequestExecutorSetup> factoryOptions, CancellationToken cancellationToken) { await using ServiceProvider services = new ServiceCollection() .AddGraphQL(_schemaName) .AddRemoteSchema( schemaDefinition.Name, (sp, ct) => new ValueTask <RemoteSchemaDefinition>(schemaDefinition)) .Services .BuildServiceProvider(); IRequestExecutorOptionsMonitor optionsMonitor = services.GetRequiredService <IRequestExecutorOptionsMonitor>(); RequestExecutorSetup options = await optionsMonitor.GetAsync(schemaDefinition.Name, cancellationToken) .ConfigureAwait(false); factoryOptions.Add(new ConfigureRequestExecutorSetup(schemaDefinition.Name, options)); options = await optionsMonitor.GetAsync(_schemaName, cancellationToken) .ConfigureAwait(false); factoryOptions.Add(new ConfigureRequestExecutorSetup(_schemaName, options)); }
private string SerializeSchemaDefinition(RemoteSchemaDefinition schemaDefinition) { var dto = new SchemaDefinitionDto { Name = schemaDefinition.Name.Value, Document = schemaDefinition.Document.ToString(false), }; dto.ExtensionDocuments.AddRange( schemaDefinition.ExtensionDocuments.Select(t => t.ToString()).ToList()); return(JsonSerializer.Serialize(dto)); }
public async ValueTask PublishAsync( RemoteSchemaDefinition schemaDefinition, CancellationToken cancellationToken = default) { string key = $"{_configurationName}.{schemaDefinition.Name}"; string json = SerializeSchemaDefinition(schemaDefinition); IDatabase database = _connection.GetDatabase(); await database.StringSetAsync(key, json).ConfigureAwait(false); await database.SetAddAsync(_configurationName.Value, schemaDefinition.Name.Value) .ConfigureAwait(false); ISubscriber subscriber = _connection.GetSubscriber(); await subscriber.PublishAsync(_configurationName.Value, schemaDefinition.Name.Value) .ConfigureAwait(false); }
private async ValueTask <IEnumerable <RemoteSchemaDefinition> > GetSchemaDefinitionsAsync( CancellationToken cancellationToken) { RedisValue[] items = await _database.SetMembersAsync(_configurationName.Value) .ConfigureAwait(false); var schemaDefinitions = new List <RemoteSchemaDefinition>(); foreach (var schemaName in items.Select(t => (string)t)) { cancellationToken.ThrowIfCancellationRequested(); RemoteSchemaDefinition schemaDefinition = await GetRemoteSchemaDefinitionAsync(schemaName) .ConfigureAwait(false); schemaDefinitions.Add(schemaDefinition); } return(schemaDefinitions); }
private async Task OnChangeMessageAsync(ChannelMessage message) { string schemaName = message.Message; RemoteSchemaDefinition schemaDefinition = await GetRemoteSchemaDefinitionAsync(schemaName) .ConfigureAwait(false); var factoryOptions = new List <IConfigureRequestExecutorSetup>(); await CreateFactoryOptionsAsync(schemaDefinition, factoryOptions, default) .ConfigureAwait(false); lock (_listeners) { foreach (OnChangeListener listener in _listeners) { foreach (IConfigureRequestExecutorSetup options in factoryOptions) { listener.OnChange(options); } } } }
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; } } });