diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/Serialization/DefaultHttpResponseFormatter.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/Serialization/DefaultHttpResponseFormatter.cs index 3d62656cc26..8c21ac880c9 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/Serialization/DefaultHttpResponseFormatter.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/Serialization/DefaultHttpResponseFormatter.cs @@ -551,7 +551,7 @@ protected virtual void OnWriteResponseHeaders( return _multiPartFormat; } - if (mediaType.Kind is EventStream) + if (mediaType.Kind is EventStream or All) { return _eventStreamFormat; } diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs index c399a72406d..e0258ae2496 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs @@ -41,6 +41,7 @@ protected virtual TestServer CreateStarWarsServer( .AddStarWarsTypes() .AddTypeExtension() .AddTypeExtension() + .AddType() .AddStarWarsRepositories() .AddInMemorySubscriptions() .UseInstrumentation() @@ -165,4 +166,10 @@ protected virtual TestServer CreateServer( .UseRouting() .UseEndpoints(endpoints => configureConventions?.Invoke(endpoints))); } + + [DirectiveType(DirectiveLocation.Subscription)] + public class Foo + { + public required int Bar { get; set; } + } } diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs index 48d927b0afc..88a93611b15 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs @@ -457,7 +457,102 @@ public async Task EventStream_Sends_KeepAlive() Snapshot .Create() .Add(response) - .MatchInline(""" + .MatchInline( + """ + Headers: + Cache-Control: no-cache + Content-Type: text/event-stream; charset=utf-8 + --------------------------> + Status Code: OK + --------------------------> + event: next + data: {"data":{"delay":"next"}} + + : + + event: next + data: {"data":{"delay":"next"}} + + : + + event: complete + + + """); + } + + [Fact] + public async Task EventStream_When_Accept_Is_All() + { + // arrange + var server = CreateStarWarsServer(); + var client = server.CreateClient(); + client.Timeout = TimeSpan.FromSeconds(30); + + // act + using var request = new HttpRequestMessage(HttpMethod.Post, _url); + request.Content = JsonContent.Create( + new ClientQueryRequest + { + Query = "subscription {delay(count: 2, delay:15000)}", + }); + request.Headers.Add("Accept", "*/*"); + + using var response = await client.SendAsync(request, ResponseHeadersRead); + + // assert + Snapshot + .Create() + .Add(response) + .MatchInline( + """ + Headers: + Cache-Control: no-cache + Content-Type: text/event-stream; charset=utf-8 + --------------------------> + Status Code: OK + --------------------------> + event: next + data: {"data":{"delay":"next"}} + + : + + event: next + data: {"data":{"delay":"next"}} + + : + + event: complete + + + """); + } + + [Fact] + public async Task EventStream_When_Accept_Is_All_And_Subscription_Directive() + { + // arrange + var server = CreateStarWarsServer(); + var client = server.CreateClient(); + client.Timeout = TimeSpan.FromSeconds(30); + + // act + using var request = new HttpRequestMessage(HttpMethod.Post, _url); + request.Content = JsonContent.Create( + new ClientQueryRequest + { + Query = "subscription foo @foo(bar: 1) {delay(count: 2, delay:15000)}", + }); + request.Headers.Add("Accept", "*/*"); + + using var response = await client.SendAsync(request, ResponseHeadersRead); + + // assert + Snapshot + .Create() + .Add(response) + .MatchInline( + """ Headers: Cache-Control: no-cache Content-Type: text/event-stream; charset=utf-8 diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/SubscriptionTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/SubscriptionTypeTests.cs index e9f6ba030d0..35f5dd49747 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/SubscriptionTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/SubscriptionTypeTests.cs @@ -6,6 +6,7 @@ #pragma warning disable CS0618 // Type or member is obsolete #nullable enable +using System.Runtime.CompilerServices; using CookieCrumble; using HotChocolate.Execution; using HotChocolate.Subscriptions; @@ -647,6 +648,44 @@ public async Task Arguments_Can_Be_Declared_On_The_Stream() """); } + [Fact] + public async Task Subscription_Directives_Are_Allowed() + { + // arrange + // act + var executor = await new ServiceCollection() + .AddGraphQLServer() + .AddDocumentFromString( + """ + type Subscription { + bookAdded: String! + } + + directive @bug(test: Int!) on SUBSCRIPTION + """) + .BindRuntimeType("Subscription") + .ModifyOptions(o => o.StrictValidation = false) + .BuildRequestExecutorAsync(); + + var result = await executor.ExecuteAsync( + """ + subscription test @bug(test: 123) { + bookAdded + } + """); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "bookAdded": "foo" + } + } + + """); + } + public class TestObservable : IObservable, IDisposable { public bool DisposeRaised { get; private set; } @@ -1047,4 +1086,18 @@ public string OnExplicit( [EventMessage] string message) => message; } + + + public class SubscriptionWithDirective + { + [Subscribe(With = nameof(GetStream))] + public string BookAdded([EventMessage] string foo) => foo; + + private async IAsyncEnumerable GetStream( + [EnumeratorCancellation] CancellationToken ct = default) + { + await Task.Delay(200, ct); + yield return "foo"; + } + } }