public void ClientMessageSent_PropertyCheck()
        {
            var client = new Mock <ISubscriptionClientProxy>();
            var result = new Mock <IGraphOperationResult>();

            client.Setup(x => x.Id).Returns("client1");

            var message = new ApolloServerDataMessage("123", result.Object);

            var entry = new ApolloClientMessageSentLogEntry(client.Object, message);

            Assert.AreEqual("client1", entry.ClientId);
            Assert.AreEqual(message.Type.ToString(), entry.MessageType);
            Assert.AreEqual(message.Id, entry.MessageId);
            Assert.AreNotEqual(entry.ToString(), entry.GetType().Name);
        }
예제 #2
0
        public async Task DataMessage_WithData_SerializesCorrectly()
        {
            var server = new TestServerBuilder()
                         .AddGraphController <ApolloDataMessageController>()
                         .AddSubscriptionServer()
                         .Build();

            var client = server.CreateSubscriptionClient();

            var context = server.CreateQueryContextBuilder()
                          .AddQueryText("query { getValue { property1 property2 } }")
                          .Build();
            await server.ExecuteQuery(context);

            var message = new ApolloServerDataMessage("abc111", context.Result);

            var converter = new ApolloServerDataMessageConverter(
                server.Schema,
                server.ServiceProvider.GetRequiredService <IGraphResponseWriter <GraphSchema> >());

            var options = new JsonSerializerOptions();

            options.Converters.Add(converter);
            var response = JsonSerializer.Serialize(message, message.GetType(), options);

            var expected = @"
            {
                ""type"" : ""data"",
                ""id"" : ""abc111"",
                ""payload"": {
                    ""data"": {
                        ""getValue"" : {
                            ""property1"": ""abc123"",
                            ""property2"" : 15
                        }
                    }
                }
            }";

            CommonAssertions.AreEqualJsonStrings(expected, response);
        }
예제 #3
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);
        }
예제 #4
0
        /// <summary>
        /// Parses the message contents to generate a valid client subscription and adds it to the watched
        /// set for this instance.
        /// </summary>
        /// <param name="message">The message with the subscription details.</param>
        private async Task ExecuteStartRequest(ApolloClientStartMessage message)
        {
            // ensure the id isnt already in use
            if (!_reservedMessageIds.ReserveMessageId(message.Id))
            {
                await this
                .SendMessage(new ApolloServerErrorMessage(
                                 $"The message id {message.Id} is already reserved for an outstanding request and cannot " +
                                 "be processed against. Allow the in-progress request to complete or stop the associated subscription.",
                                 SubscriptionConstants.ErrorCodes.DUPLICATE_MESSAGE_ID,
                                 lastMessage : message,
                                 clientProvidedId : message.Id))
                .ConfigureAwait(false);

                return;
            }

            var retainMessageId = false;
            var runtime         = this.ServiceProvider.GetRequiredService(typeof(IGraphQLRuntime <TSchema>)) as IGraphQLRuntime <TSchema>;
            var request         = runtime.CreateRequest(message.Payload);
            var metricsPackage  = _enableMetrics ? runtime.CreateMetricsPackage() : null;
            var context         = new SubcriptionExecutionContext(
                this,
                request,
                message.Id,
                metricsPackage);

            var result = await runtime
                         .ExecuteRequest(context)
                         .ConfigureAwait(false);

            if (context.IsSubscriptionOperation)
            {
                retainMessageId = await this
                                  .RegisterSubscriptionOrRespond(context.Subscription as ISubscription <TSchema>)
                                  .ConfigureAwait(false);
            }
            else
            {
                // not a subscription, just send back the generated response and close out the id
                ApolloMessage responseMessage;

                // report syntax errors as error messages
                // allow others to bubble into a fully reslt (per apollo spec)
                if (result.Messages.Count == 1 &&
                    result.Messages[0].Code == Constants.ErrorCodes.SYNTAX_ERROR)
                {
                    responseMessage = new ApolloServerErrorMessage(
                        result.Messages[0],
                        message,
                        message.Id);
                }
                else
                {
                    responseMessage = new ApolloServerDataMessage(message.Id, result);
                }

                await this
                .SendMessage(responseMessage)
                .ConfigureAwait(false);

                await this
                .SendMessage(new ApolloServerCompleteMessage(message.Id))
                .ConfigureAwait(false);
            }

            if (!retainMessageId)
            {
                _reservedMessageIds.ReleaseMessageId(message.Id);
            }
        }