/// <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));
 }
Beispiel #7
0
 /// <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;
 }
Beispiel #11
0
 /// <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));
 }
Beispiel #13
0
 /// <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;
 }
Beispiel #15
0
        /// <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));
 }
Beispiel #19
0
        /// <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);
        }
Beispiel #20
0
 /// <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));
 }
Beispiel #21
0
        /// <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&lt;System.Object&gt;.</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;
                }
            }
        }
Beispiel #22
0
 /// <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);
 }
Beispiel #23
0
 /// <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);
 }