diff --git a/src/HotChocolate/Stitching/src/Stitching/DependencyInjection/HotChocolateStitchingRequestExecutorExtensions.cs b/src/HotChocolate/Stitching/src/Stitching/DependencyInjection/HotChocolateStitchingRequestExecutorExtensions.cs
index e905b522838..8d96df9e0cf 100644
--- a/src/HotChocolate/Stitching/src/Stitching/DependencyInjection/HotChocolateStitchingRequestExecutorExtensions.cs
+++ b/src/HotChocolate/Stitching/src/Stitching/DependencyInjection/HotChocolateStitchingRequestExecutorExtensions.cs
@@ -2,6 +2,7 @@
using System.IO;
using System.Linq;
using System.Net.Http;
+using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -20,6 +21,7 @@
using HotChocolate.Stitching.Utilities;
using HotChocolate.Utilities;
using HotChocolate.Utilities.Introspection;
+using ThrowHelper = HotChocolate.Stitching.ThrowHelper;
namespace Microsoft.Extensions.DependencyInjection
{
@@ -571,6 +573,71 @@ await loadSchema(services, cancellationToken)
});
}
+ ///
+ /// Adds a schema SDL that contains type extensions.
+ /// Extension documents can be used to extend merged types
+ /// or even replace them.
+ ///
+ ///
+ /// The .
+ ///
+ ///
+ /// The assembly from which the type extension file shall be resolved.
+ ///
+ ///
+ /// The resource key of the type extension file
+ ///
+ ///
+ /// Returns the .
+ ///
+ ///
+ /// is null, or
+ /// is null.
+ /// is null.
+ ///
+ public static IRequestExecutorBuilder AddTypeExtensionsFromResource(
+ this IRequestExecutorBuilder builder,
+ Assembly assembly,
+ string key)
+ {
+ if (builder is null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ if (assembly is null)
+ {
+ throw new ArgumentNullException(nameof(assembly));
+ }
+
+ if (key is null)
+ {
+ throw new ArgumentNullException(nameof(key));
+ }
+
+ return builder.ConfigureSchemaAsync(
+ async (s, ct) =>
+ {
+ Stream? stream = assembly.GetManifestResourceStream(key);
+
+ if (stream is null)
+ {
+ throw ThrowHelper.RequestExecutorBuilder_ResourceNotFound(key);
+ }
+
+#if NET5_0
+ await using (stream)
+#else
+ using (stream)
+#endif
+ {
+ var buffer = new byte[stream.Length];
+ await stream.ReadAsync(buffer, 0, buffer.Length, ct).ConfigureAwait(false);
+ s.AddTypeExtensions(Utf8GraphQLParser.Parse(buffer));
+ }
+ });
+ }
+
///
/// Add a document rewriter that is executed on
/// the merged schema document.
diff --git a/src/HotChocolate/Stitching/src/Stitching/ThrowHelper.cs b/src/HotChocolate/Stitching/src/Stitching/ThrowHelper.cs
index 6b27b2f043b..74839b69838 100644
--- a/src/HotChocolate/Stitching/src/Stitching/ThrowHelper.cs
+++ b/src/HotChocolate/Stitching/src/Stitching/ThrowHelper.cs
@@ -10,13 +10,13 @@ internal static class ThrowHelper
{
public static InvalidOperationException BufferedRequest_VariableDoesNotExist(
string name) =>
- new InvalidOperationException(string.Format(
+ new(string.Format(
ThrowHelper_BufferedRequest_VariableDoesNotExist,
name));
public static InvalidOperationException BufferedRequest_OperationNotFound(
DocumentNode document) =>
- new InvalidOperationException(string.Format(
+ new(string.Format(
ThrowHelper_BufferedRequest_OperationNotFound,
document));
@@ -24,7 +24,7 @@ internal static class ThrowHelper
string variableName,
FieldNode fieldSelection,
Path path) =>
- new GraphQLException(ErrorBuilder.New()
+ new(ErrorBuilder.New()
.SetMessage(
StitchingResources.ArgumentScopedVariableResolver_InvalidArgumentName,
variableName)
@@ -37,7 +37,7 @@ internal static class ThrowHelper
string variableName,
FieldNode fieldSelection,
Path path) =>
- new GraphQLException(ErrorBuilder.New()
+ new(ErrorBuilder.New()
.SetMessage(
StitchingResources.FieldScopedVariableResolver_InvalidFieldName,
variableName)
@@ -50,7 +50,7 @@ internal static class ThrowHelper
string scopeName,
FieldNode fieldSelection,
Path path) =>
- new GraphQLException(ErrorBuilder.New()
+ new(ErrorBuilder.New()
.SetMessage(
StitchingResources.RootScopedVariableResolver_ScopeNotSupported,
scopeName)
@@ -61,7 +61,7 @@ internal static class ThrowHelper
public static SchemaException PublishSchemaDefinitionDescriptor_ResourceNotFound(
string key) =>
- new SchemaException(
+ new(
SchemaErrorBuilder.New()
.SetMessage(
"The resource `{0}` was not found!",
@@ -70,10 +70,19 @@ internal static class ThrowHelper
public static SchemaException IntrospectionHelper_UnableToFetchSchemaDefinition(
IReadOnlyList errors) =>
- new SchemaException(
+ new(
SchemaErrorBuilder.New()
.SetMessage("Unable to fetch schema definition.")
.SetExtension("errors", errors)
.Build());
+
+ public static SchemaException RequestExecutorBuilder_ResourceNotFound(
+ string key) =>
+ new(
+ SchemaErrorBuilder.New()
+ .SetMessage(
+ "The resource `{0}` was not found!",
+ key)
+ .Build());
}
}
diff --git a/src/HotChocolate/Stitching/test/Stitching.Tests/Configuration/HotChocolateStitchingRequestExecutorExtensionsTests.cs b/src/HotChocolate/Stitching/test/Stitching.Tests/Configuration/HotChocolateStitchingRequestExecutorExtensionsTests.cs
index 9c65c103f26..2c21faa0a2b 100644
--- a/src/HotChocolate/Stitching/test/Stitching.Tests/Configuration/HotChocolateStitchingRequestExecutorExtensionsTests.cs
+++ b/src/HotChocolate/Stitching/test/Stitching.Tests/Configuration/HotChocolateStitchingRequestExecutorExtensionsTests.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
using HotChocolate.Configuration;
using HotChocolate.Execution;
@@ -34,6 +35,45 @@ public async Task RewriteType()
Assert.Equal("OriginalType1", lookup[("NewType1", "Schema1")]);
Assert.Equal("OriginalType2", lookup[("NewType2", "Schema2")]);
}
+
+ [Fact]
+ public void AddTypeExtensionsFromResource_Builder_Is_Null()
+ {
+ // arrange
+ // act
+ void Configure() =>
+ HotChocolateStitchingRequestExecutorExtensions
+ .AddTypeExtensionsFromResource(null!, GetType().Assembly, "abc");
+
+ // assert
+ Assert.Throws(Configure);
+ }
+
+ [Fact]
+ public void AddTypeExtensionsFromResource_Assembly_Is_Null()
+ {
+ // arrange
+ // act
+ void Configure() =>
+ new ServiceCollection().AddGraphQL()
+ .AddTypeExtensionsFromResource(null!, "abc");
+
+ // assert
+ Assert.Throws(Configure);
+ }
+
+ [Fact]
+ public void AddTypeExtensionsFromResource_Key_Is_Null()
+ {
+ // arrange
+ // act
+ void Configure() =>
+ new ServiceCollection().AddGraphQL()
+ .AddTypeExtensionsFromResource(GetType().Assembly, null!);
+
+ // assert
+ Assert.Throws(Configure);
+ }
}
public class CustomQueryType : ObjectType
diff --git a/src/HotChocolate/Stitching/test/Stitching.Tests/HotChocolate.Stitching.Tests.csproj b/src/HotChocolate/Stitching/test/Stitching.Tests/HotChocolate.Stitching.Tests.csproj
index b6852bda4ec..2dbc0a4eae7 100644
--- a/src/HotChocolate/Stitching/test/Stitching.Tests/HotChocolate.Stitching.Tests.csproj
+++ b/src/HotChocolate/Stitching/test/Stitching.Tests/HotChocolate.Stitching.Tests.csproj
@@ -28,4 +28,11 @@
+
+
+
+ Always
+
+
+
diff --git a/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/BaseTests.cs b/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/BaseTests.cs
index f92a4cdef67..c27ea80779a 100644
--- a/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/BaseTests.cs
+++ b/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/BaseTests.cs
@@ -693,5 +693,56 @@ public async Task Add_Dummy_Directive()
// assert
Assert.NotNull(schema.GetDirectiveType("foo"));
}
+
+ [Fact]
+ public async Task Add_Dummy_Directive_From_Resource()
+ {
+ // arrange
+ IHttpClientFactory httpClientFactory =
+ Context.CreateDefaultRemoteSchemas();
+
+ // act
+ ISchema schema =
+ await new ServiceCollection()
+ .AddSingleton(httpClientFactory)
+ .AddGraphQL()
+ .AddRemoteSchema(Context.ContractSchema)
+ .AddRemoteSchema(Context.CustomerSchema)
+ .AddTypeExtensionsFromResource(
+ GetType().Assembly,
+ "HotChocolate.Stitching.__resources__.DummyDirective.graphql")
+ .ModifyRequestOptions(o => o.IncludeExceptionDetails = true)
+ .BuildSchemaAsync();
+
+ // assert
+ Assert.NotNull(schema.GetDirectiveType("foo"));
+ }
+
+ [Fact]
+ public async Task Add_Dummy_Directive_From_Resource_Key_Does_Not_Exist()
+ {
+ // arrange
+ IHttpClientFactory httpClientFactory =
+ Context.CreateDefaultRemoteSchemas();
+
+ // act
+ async Task Configure() =>
+ await new ServiceCollection()
+ .AddSingleton(httpClientFactory)
+ .AddGraphQL()
+ .AddRemoteSchema(Context.ContractSchema)
+ .AddRemoteSchema(Context.CustomerSchema)
+ .AddTypeExtensionsFromResource(
+ GetType().Assembly,
+ "HotChocolate.Stitching.__resources__.abc")
+ .ModifyRequestOptions(o => o.IncludeExceptionDetails = true)
+ .BuildSchemaAsync();
+
+ // assert
+ SchemaException exception = await Assert.ThrowsAsync(Configure);
+ Assert.Contains(
+ "The resource `HotChocolate.Stitching.__resources__.abc` was not found!",
+ exception.Message);
+ }
}
}
diff --git a/src/HotChocolate/Stitching/test/Stitching.Tests/__resources__/DummyDirective.graphql b/src/HotChocolate/Stitching/test/Stitching.Tests/__resources__/DummyDirective.graphql
new file mode 100644
index 00000000000..11be1da2a6a
--- /dev/null
+++ b/src/HotChocolate/Stitching/test/Stitching.Tests/__resources__/DummyDirective.graphql
@@ -0,0 +1 @@
+directive @foo on FIELD_DEFINITION