/// <summary> /// OnEvent is called whenever the event stream yields a payload and triggers an /// execution of the subscription query. /// </summary> /// <param name="payload"> /// The event stream payload. /// </param> /// <returns> /// Returns a query result which will be enqueued to the response stream. /// </returns> private async Task <IQueryResult> OnEvent(object payload) { using IDisposable es = _diagnosticEvents.OnSubscriptionEvent(new(this, payload)); using IServiceScope serviceScope = _requestContext.Services.CreateScope(); OperationContext operationContext = _operationContextPool.Get(); try { var eventServices = serviceScope.ServiceProvider; var dispatcher = eventServices.GetRequiredService <IBatchDispatcher>(); // we store the event payload on the scoped context so that it is accessible // in the resolvers. ImmutableDictionary <string, object?> scopedContext = ImmutableDictionary <string, object?> .Empty .SetItem(WellKnownContextData.EventMessage, payload); // next we resolve the subscription instance. var rootValue = RootValueResolver.Resolve( _requestContext, eventServices, _subscriptionType, ref _cachedRootValue); // last we initialize a standard operation context to execute // the subscription query with the standard query executor. operationContext.Initialize( _requestContext, eventServices, dispatcher, _requestContext.Operation !, _queryPlan, _requestContext.Variables !, rootValue, _resolveQueryRootValue); operationContext.Result.SetContextData( WellKnownContextData.EventMessage, payload); IQueryResult result = await _queryExecutor .ExecuteAsync(operationContext, scopedContext) .ConfigureAwait(false); _diagnosticEvents.SubscriptionEventResult(new(this, payload), result); return(result); } catch (Exception ex) { _diagnosticEvents.SubscriptionEventError(new(this, payload), ex); throw; } finally { _operationContextPool.Return(operationContext); } }
private async Task <IQueryResult> OnEvent(object payload) { using IServiceScope serviceScope = _requestContext.Services.CreateScope(); IServiceProvider eventServices = serviceScope.ServiceProvider; IBatchDispatcher dispatcher = eventServices.GetRequiredService <IBatchDispatcher>(); OperationContext operationContext = _operationContextPool.Get(); try { ImmutableDictionary <string, object?> scopedContext = ImmutableDictionary <string, object?> .Empty .SetItem(WellKnownContextData.EventMessage, payload); object?rootValue = RootValueResolver.Resolve( _requestContext, eventServices, _subscriptionType, ref _cachedRootValue); operationContext.Initialize( _requestContext, eventServices, dispatcher, _requestContext.Operation !, _requestContext.Variables !, rootValue, _resolveQueryRootValue); return(await _queryExecutor .ExecuteAsync(operationContext, scopedContext) .ConfigureAwait(false)); } finally { _operationContextPool.Return(operationContext); } }
// subscribe will use the subscribe resolver to create a source stream that yields // the event messages from the underlying pub/sub-system. private async ValueTask <ISourceStream> SubscribeAsync() { OperationContext operationContext = _operationContextPool.Get(); try { // first we will create the root value which essentially // is the subscription object. In some cases this object is null. object?rootValue = RootValueResolver.Resolve( _requestContext, _requestContext.Services, _subscriptionType, ref _cachedRootValue); // next we need to initialize our operation context so that we have access to // variables services and other things. // The subscribe resolver will use a noop dispatcher and all DataLoader are // dispatched immediately. operationContext.Initialize( _requestContext, _requestContext.Services, NoopBatchDispatcher.Default, _requestContext.Operation !, _requestContext.Variables !, rootValue, _resolveQueryRootValue); // next we need a result map so that we can store the subscribe temporarily // while executing the subscribe pipeline. ResultMap resultMap = operationContext.Result.RentResultMap(1); ISelection rootSelection = _rootSelections.Selections[0]; // we create a temporary middleware context so that we can use the standard // resolver pipeline. var middlewareContext = new MiddlewareContext(); middlewareContext.Initialize( operationContext, rootSelection, resultMap, 1, rootValue, Path.New(rootSelection.ResponseName), ImmutableDictionary <string, object?> .Empty); // it is important that we correctly coerce the arguments before // invoking subscribe. if (!rootSelection.Arguments.TryCoerceArguments( middlewareContext.Variables, middlewareContext.ReportError, out IReadOnlyDictionary <NameString, ArgumentValue>?coercedArgs)) { // the middleware context reports errors to the operation context, // this means if we failed, we need to grab the coercion errors from there // and just throw a GraphQLException. throw new GraphQLException(operationContext.Result.Errors); } // if everything is fine with the arguments we still need to assign them. middlewareContext.Arguments = coercedArgs; // last but not least we can invoke the subscribe resolver which will subscribe // to the underlying pub/sub-system yielding the source stream. ISourceStream sourceStream = await rootSelection.Field.SubscribeResolver !.Invoke(middlewareContext) .ConfigureAwait(false); if (operationContext.Result.Errors.Count > 0) { // again if we have any errors we will just throw them and not opening // any subscription context. throw new GraphQLException(operationContext.Result.Errors); } return(sourceStream); } finally { operationContext.Result.DropResult(); _operationContextPool.Return(operationContext); } }