/// <summary> /// Initializes a new instance of the <see cref="CustomHttpProcessor"/> class. /// </summary> /// <param name="schema">The schema instance.</param> /// <param name="graphqlRunTime">The graphql runtime for the schema.</param> /// <param name="writer">The serializer to turn a response into json.</param> /// <param name="logger">The logger instance to use for recording events.</param> public CustomHttpProcessor( GraphSchema schema, IGraphQLRuntime <GraphSchema> graphqlRunTime, IGraphResponseWriter <GraphSchema> writer, IGraphEventLogger logger = null) : base(schema, graphqlRunTime, writer, logger) { }
/// <summary> /// Recorded when a subscription client is no longer connected or otherwise dropped /// by ASP.NET. The server will process no more messages from the client. /// </summary> /// <param name="logger">The logger doing the logging.</param> /// <param name="client">The client that was dropped and is being cleaned up.</param> public static void SubscriptionClientDropped( this IGraphEventLogger logger, ISubscriptionClientProxy client) { logger.Log( LogLevel.Debug, () => new SubscriptionClientDroppedLogEntry(client)); }
/// <summary> /// Initializes a new instance of the <see cref="SecureGraphQLHttpProcessor{TSchema}" /> class. /// </summary> /// <param name="schema">The schema.</param> /// <param name="runtime">The runtime.</param> /// <param name="writer">The writer.</param> /// <param name="logger">The logger.</param> public SecureGraphQLHttpProcessor( TSchema schema, IGraphQLRuntime <TSchema> runtime, IGraphResponseWriter <TSchema> writer, IGraphEventLogger logger = null) : base(schema, runtime, writer, logger) { }
/// <summary> /// Recorded by this instance's <see cref="ISubscriptionEventRouter"/> when it receives a new subscription event from /// an externally connected source such as a message queue or service or bus. For single server configurations this event /// is recorded when an event is passed from the internal publishing queue directly to the <see cref="ISubscriptionEventRouter"/>. /// </summary> /// <param name="logger">The logger doing the logging.</param> /// <param name="eventData">The event data that was received from a data source.</param> public static void SubscriptionEventReceived( this IGraphEventLogger logger, SubscriptionEvent eventData) { logger.Log( LogLevel.Debug, () => new SubscriptionEventReceivedLogEntry(eventData)); }
/// <summary> /// Recorded when the startup services registers a publically available ASP.NET MVC route to which /// end users can intiate a websocket request through which subscriptions can be established. /// </summary> /// <typeparam name="TSchema">The type of the schema the route was registered for.</typeparam> /// <param name="logger">The logger.</param> /// <param name="routePath">The relative route path (e.g. '/graphql').</param> public static void SchemaSubscriptionRouteRegistered <TSchema>( this IGraphEventLogger logger, string routePath) where TSchema : class, ISchema { logger.Log( LogLevel.Debug, () => new SchemaSubscriptionRouteRegisteredLogEntry <TSchema>(routePath)); }
/// <summary> /// Recorded when a subscription client is no longer connected or otherwise dropped /// by ASP.NET. The server will process no more messages from the client. /// </summary> /// <typeparam name="TSchema">The type of the schema the server was created for.</typeparam> /// <param name="logger">The logger doing the logging.</param> /// <param name="server">The server that was created.</param> public static void SubscriptionServerCreated <TSchema>( this IGraphEventLogger logger, ISubscriptionServer <TSchema> server) where TSchema : class, ISchema { logger.Log( LogLevel.Debug, () => new SubscriptionServerCreatedLogEntry <TSchema>(server)); }
/// <summary> /// Initializes a new instance of the <see cref="SecureGraphQLHttpProcessor{TSchema}" /> class. /// </summary> /// <param name="schema">The schema.</param> /// <param name="queryPipeline">The query pipeline.</param> /// <param name="writer">The writer.</param> /// <param name="metricsFactory">The metrics factory.</param> /// <param name="logger">The logger.</param> public SecureGraphQLHttpProcessor( TSchema schema, ISchemaPipeline <TSchema, GraphQueryExecutionContext> queryPipeline, IGraphResponseWriter <TSchema> writer, IGraphQueryExecutionMetricsFactory <TSchema> metricsFactory, IGraphEventLogger logger = null) : base(schema, queryPipeline, writer, metricsFactory, logger) { }
/// <summary> /// Initializes a new instance of the <see cref="DefaultGraphQLRuntime{TSchema}" /> class. /// </summary> /// <param name="pipeline">The top level pipeline to execute any requests on.</param> /// <param name="metricsFactory">The factory to produce metrics packages if and when needed.</param> /// <param name="logger">The logger used to record events during an execution.</param> public DefaultGraphQLRuntime( ISchemaPipeline <TSchema, GraphQueryExecutionContext> pipeline, IGraphQueryExecutionMetricsFactory <TSchema> metricsFactory = null, IGraphEventLogger logger = null) { _pipeline = Validation.ThrowIfNullOrReturn(pipeline, nameof(pipeline)); _logger = logger; _metricsFactory = metricsFactory; }
/// <summary> /// Recorded when a new client is registered against a subscription server and /// the graphql server begins monitoring it for messages. /// </summary> /// <typeparam name="TSchema">The type of the schema the client was registered for.</typeparam> /// <param name="logger">The logger doing the logging.</param> /// <param name="server">The server which created the client.</param> /// <param name="client">The client that was created.</param> public static void SubscriptionClientRegistered <TSchema>( this IGraphEventLogger logger, ISubscriptionServer <TSchema> server, ISubscriptionClientProxy client) where TSchema : class, ISchema { logger.Log( LogLevel.Debug, () => new SubscriptionClientRegisteredLogEntry <TSchema>(server, client)); }
/// <summary> /// Initializes a new instance of the <see cref="DefaultGraphQLHttpProcessor{TSchema}" /> class. /// </summary> /// <param name="schema">The singleton instance of <typeparamref name="TSchema"/> representing this processor works against.</param> /// <param name="runtime">The primary runtime instance in which GraphQL requests are processed for <typeparamref name="TSchema"/>.</param> /// <param name="writer">The result writer capable of converting a <see cref="IGraphOperationResult"/> into a serialized payload /// for the given <typeparamref name="TSchema"/>.</param> /// <param name="logger">A logger instance where this object can write and record log entries.</param> public DefaultGraphQLHttpProcessor( TSchema schema, IGraphQLRuntime <TSchema> runtime, IGraphResponseWriter <TSchema> writer, IGraphEventLogger logger = null) { _schema = Validation.ThrowIfNullOrReturn(schema, nameof(schema)); _runtime = Validation.ThrowIfNullOrReturn(runtime, nameof(runtime)); _writer = Validation.ThrowIfNullOrReturn(writer, nameof(writer)); _logger = logger; }
/// <summary> /// Initializes a new instance of the <see cref="GraphQueryExecutionContext" /> class. /// </summary> /// <param name="request">The request to be processed through the query pipeline.</param> /// <param name="serviceProvider">The service provider passed on the HttpContext.</param> /// <param name="user">The user authenticated by the Asp.net runtime.</param> /// <param name="metrics">The metrics package to profile this request, if any.</param> /// <param name="logger">The logger instance to record events related to this context.</param> /// <param name="items">A key/value pair collection for random access data.</param> public GraphQueryExecutionContext( IGraphOperationRequest request, IServiceProvider serviceProvider, ClaimsPrincipal user = null, IGraphQueryExecutionMetrics metrics = null, IGraphEventLogger logger = null, MetaDataCollection items = null) : base(serviceProvider, user, metrics, logger, items) { this.Request = Validation.ThrowIfNullOrReturn(request, nameof(request)); this.FieldResults = new List <GraphDataItem>(); }
/// <summary> /// Initializes a new instance of the <see cref="SubcriptionExecutionContext" /> class. /// </summary> /// <param name="client">The client.</param> /// <param name="request">The request to be processed through the query pipeline.</param> /// <param name="subscriptionId">The unique id to assign to the created subscription, when one is made.</param> /// <param name="metrics">The metrics package to profile this request, if any.</param> /// <param name="logger">The logger instance to record events related to this context.</param> /// <param name="items">A key/value pair collection for random access data.</param> public SubcriptionExecutionContext( ISubscriptionClientProxy client, IGraphOperationRequest request, string subscriptionId, IGraphQueryExecutionMetrics metrics = null, IGraphEventLogger logger = null, MetaDataCollection items = null) : base(request, client?.ServiceProvider, client?.User, metrics, logger, items) { this.Client = Validation.ThrowIfNullOrReturn(client, nameof(client)); this.SubscriptionId = Validation.ThrowIfNullWhiteSpaceOrReturn(subscriptionId, nameof(subscriptionId)); }
/// <summary> /// Initializes a new instance of the <see cref="DefaultGraphQLHttpProcessor{TSchema}" /> class. /// </summary> /// <param name="schema">The schema.</param> /// <param name="queryPipeline">The query pipeline.</param> /// <param name="writer">The writer.</param> /// <param name="metricsFactory">The metrics factory.</param> /// <param name="logger">The logger.</param> public DefaultGraphQLHttpProcessor( TSchema schema, ISchemaPipeline <TSchema, GraphQueryExecutionContext> queryPipeline, IGraphResponseWriter <TSchema> writer, IGraphQueryExecutionMetricsFactory <TSchema> metricsFactory, IGraphEventLogger logger = null) { _schema = Validation.ThrowIfNullOrReturn(schema, nameof(schema)); _queryPipeline = Validation.ThrowIfNullOrReturn(queryPipeline, nameof(queryPipeline)); _writer = Validation.ThrowIfNullOrReturn(writer, nameof(writer)); _metricsFactory = Validation.ThrowIfNullOrReturn(metricsFactory, nameof(metricsFactory)); _logger = logger; }
/// <summary> /// Initializes a new instance of the <see cref="BaseGraphMiddlewareContext" /> class. /// </summary> /// <param name="serviceProvider">The service provider passed on the HttpContext.</param> /// <param name="user">The user authenticated by the Asp.net runtime.</param> /// <param name="metrics">The metrics package to profile this request, if any.</param> /// <param name="logger">The logger instance to record events related to this context.</param> /// <param name="items">A key/value pair collection for random access data.</param> protected BaseGraphMiddlewareContext( IServiceProvider serviceProvider, ClaimsPrincipal user = null, IGraphQueryExecutionMetrics metrics = null, IGraphEventLogger logger = null, MetaDataCollection items = null) { this.ServiceProvider = Validation.ThrowIfNullOrReturn(serviceProvider, nameof(serviceProvider)); this.User = user; this.Metrics = metrics; this.Items = items ?? new MetaDataCollection(); this.Messages = new GraphMessageCollection(); this.Logger = logger; }
/// <summary> /// Initializes a new instance of the <see cref="ApolloClientProxy{TSchema}" /> class. /// </summary> /// <param name="clientConnection">The underlying client connection for apollo to manage.</param> /// <param name="options">The options used to configure the registration.</param> /// <param name="messageConverter">The message converter factory that will generate /// json converters for the various <see cref="ApolloMessage" /> the proxy shuttles to the client.</param> /// <param name="logger">The logger to record client level events to, if any.</param> /// <param name="enableMetrics">if set to <c>true</c> any queries this client /// executes will have metrics attached.</param> public ApolloClientProxy( IClientConnection clientConnection, SubscriptionServerOptions <TSchema> options, ApolloMessageConverterFactory messageConverter, IGraphEventLogger logger = null, bool enableMetrics = false) { _connection = Validation.ThrowIfNullOrReturn(clientConnection, nameof(clientConnection)); _options = Validation.ThrowIfNullOrReturn(options, nameof(options)); _messageConverter = Validation.ThrowIfNullOrReturn(messageConverter, nameof(messageConverter)); _reservedMessageIds = new ClientTrackedMessageIdSet(); _subscriptions = new ApolloSubscriptionCollection <TSchema>(); _enableKeepAlive = options.KeepAliveInterval != TimeSpan.Zero; _logger = logger != null ? new ApolloClientEventLogger <TSchema>(this, logger) : null; _enableMetrics = enableMetrics; }
/// <summary> /// Initializes a new instance of the <see cref="ApolloSubscriptionServer{TSchema}" /> class. /// </summary> /// <param name="schema">The schema instance this sever will use for various comparisons.</param> /// <param name="options">The user configured options for this server.</param> /// <param name="eventRouter">The listener watching for new events that need to be communicated /// to clients managed by this server.</param> /// <param name="logger">The logger to record server events to, if any.</param> public ApolloSubscriptionServer( TSchema schema, SubscriptionServerOptions <TSchema> options, ISubscriptionEventRouter eventRouter, IGraphEventLogger logger = null) { _schema = Validation.ThrowIfNullOrReturn(schema, nameof(schema)); _serverOptions = Validation.ThrowIfNullOrReturn(options, nameof(options)); _eventRouter = Validation.ThrowIfNullOrReturn(eventRouter, nameof(eventRouter)); _clients = new HashSet <ApolloClientProxy <TSchema> >(); _eventSendSemaphore = new SemaphoreSlim(_serverOptions.MaxConcurrentClientNotifications); _logger = logger != null ? new ApolloServerEventLogger <TSchema>(this, logger) : null; _subCountByName = new Dictionary <SubscriptionEventName, HashSet <ApolloClientProxy <TSchema> > >( SubscriptionEventNameEqualityComparer.Instance); this.Id = Guid.NewGuid().ToString(); }
/// <summary> /// Adds the given logger instance to the contexts that this builder creates. /// </summary> /// <param name="eventLogger">The event logger.</param> /// <returns>SubscriptionContextBuilder.</returns> public SubscriptionContextBuilder AddLogger(IGraphEventLogger eventLogger) { _eventLogger = eventLogger; return(this); }
/// <summary> /// Initializes a new instance of the <see cref="ApolloServerEventLogger{TSchema}" /> class. /// </summary> /// <param name="server">The server being logged.</param> /// <param name="logger">The root graph logger to send apollo events to.</param> public ApolloServerEventLogger(ApolloSubscriptionServer <TSchema> server, IGraphEventLogger logger) { _server = Validation.ThrowIfNullOrReturn(server, nameof(server)); _logger = Validation.ThrowIfNullOrReturn(logger, nameof(logger)); }
/// <summary> /// Instructs the client to process the new event. If this is an event the client subscribes /// to it should process the data appropriately and send down any data to its underlying connection /// as necessary. /// </summary> /// <param name="field">The unique field corrisponding to the event that was raised /// by the publisher.</param> /// <param name="sourceData">The source data sent from the publisher when the event was raised.</param> /// <param name="cancelToken">A cancellation token.</param> /// <returns>Task.</returns> public async Task ReceiveEvent(GraphFieldPath field, object sourceData, CancellationToken cancelToken = default) { await Task.Yield(); if (field == null) { return; } var subscriptions = _subscriptions.RetreiveByRoute(field); _logger?.SubscriptionEventReceived(field, subscriptions); if (subscriptions.Count == 0) { return; } var runtime = this.ServiceProvider.GetRequiredService <IGraphQLRuntime <TSchema> >(); var schema = this.ServiceProvider.GetRequiredService <TSchema>(); var tasks = new List <Task>(); foreach (var subscription in subscriptions) { IGraphQueryExecutionMetrics metricsPackage = null; IGraphEventLogger logger = this.ServiceProvider.GetService <IGraphEventLogger>(); if (schema.Configuration.ExecutionOptions.EnableMetrics) { var factory = this.ServiceProvider.GetRequiredService <IGraphQueryExecutionMetricsFactory <TSchema> >(); metricsPackage = factory.CreateMetricsPackage(); } var context = new GraphQueryExecutionContext( runtime.CreateRequest(subscription.QueryData), this.ServiceProvider, this.User, metricsPackage, logger); // register the event data as a source input for the target subscription field context.DefaultFieldSources.AddSource(subscription.Field, sourceData); context.QueryPlan = subscription.QueryPlan; context.QueryOperation = subscription.QueryOperation; tasks.Add(runtime.ExecuteRequest(context, cancelToken) .ContinueWith( task => { if (task.IsFaulted) { return(task); } // send the message with the resultant data package var message = new ApolloServerDataMessage(subscription.Id, task.Result); return(this.SendMessage(message)); }, cancelToken)); } await Task.WhenAll(tasks).ConfigureAwait(false); }
/// <summary> /// Initializes a new instance of the <see cref="ApolloClientEventLogger{TSchema}" /> class. /// </summary> /// <param name="client">The client being logged.</param> /// <param name="logger">The root graph logger to send apollo events to.</param> public ApolloClientEventLogger(ApolloClientProxy <TSchema> client, IGraphEventLogger logger) { _client = Validation.ThrowIfNullOrReturn(client, nameof(client)); _logger = Validation.ThrowIfNullOrReturn(logger, nameof(logger)); }
/// <summary> /// Invoke the specified action method as an asynchronous operation. /// </summary> /// <param name="actionToInvoke">The action to invoke.</param> /// <param name="context">The context.</param> /// <returns>Task<System.Object>.</returns> public async Task <object> InvokeActionAsync( IGraphMethod actionToInvoke, BaseResolutionContext <TRequest> context) { // deconstruct the context for processing _action = actionToInvoke; var fieldRequest = context.Request; this.Request = Validation.ThrowIfNullOrReturn(fieldRequest, nameof(fieldRequest)); _context = context; _logger = context?.Logger; _logger?.ActionMethodInvocationRequestStarted(_action, this.Request); if (_action?.Method == null) { return(new InternalServerErrorGraphActionResult( $"The definition for field '{this.GetType().Name}' defined no graph action to execute. Operation failed.")); } try { var modelGenerator = new ModelStateGenerator(context.ServiceProvider); this.ModelState = modelGenerator.CreateStateDictionary(context.Arguments); _logger?.ActionMethodModelStateValidated(_action, this.Request, this.ModelState); if (_action.Method.DeclaringType != this.GetType()) { throw new TargetException($"Unable to invoke action '{_action.Route.Path}' on controller '{this.GetType().FriendlyName()}'. The controller " + "does not own the method."); } var invoker = InstanceFactory.CreateInstanceMethodInvoker(_action.Method); var invocationParameters = context.Arguments.PrepareArguments(_action); var invokeReturn = invoker(this, invocationParameters); if (_action.IsAsyncField) { if (invokeReturn is Task task) { await task.ConfigureAwait(false); if (task.IsFaulted) { throw task.UnwrapException(); } invokeReturn = task.ResultOrDefault(); } else { // given all the checking and parsing this should be imnpossible, but just in case invokeReturn = new InternalServerErrorGraphActionResult($"The action '{_action.Route.Path}' is defined " + $"as asyncronous but it did not return a {typeof(Task)}."); } } _logger?.ActionMethodInvocationCompleted(_action, this.Request, invokeReturn); return(invokeReturn); } catch (TargetInvocationException ti) { var innerException = ti.InnerException ?? ti; _logger?.ActionMethodInvocationException(_action, this.Request, innerException); return(new InternalServerErrorGraphActionResult(_action, innerException)); } catch (Exception ex) { switch (ex) { // catch any other invocation exceptions and treat them as an invalid route // might happen if a method was declared differently than the actual call signature case TargetException _: case TargetParameterCountException _: _logger?.ActionMethodInvocationException(_action, this.Request, ex); return(new RouteNotFoundGraphActionResult(_action, ex)); default: // total failure by the user's action code. // record and bubble _logger?.ActionMethodUnhandledException(_action, this.Request, ex); throw; } } }
/// <summary> /// Initializes a new instance of the <see cref="DefaultSubscriptionEventRouter" /> class. /// </summary> /// <param name="logger">The logger.</param> public DefaultSubscriptionEventRouter(IGraphEventLogger logger = null) { _logger = logger; _receivers = new SubscribedEventRecievers(SubscriptionEventNameEqualityComparer.Instance); }
/// <summary> /// Adds the given logger instance to the contexts that this builder creates. /// </summary> /// <param name="eventLogger">The event logger.</param> /// <returns>QueryContextBuilder.</returns> public QueryContextBuilder AddLogger(IGraphEventLogger eventLogger) { _eventLogger = eventLogger; return(this); }