Skip to content

Commit 97a2601

Browse files
[Fusion] Add AddErrorFilter & DisableIntrospection (#8736)
1 parent 205dec8 commit 97a2601

14 files changed

+848
-60
lines changed

src/HotChocolate/Core/test/Execution.Tests/DependencyInjection/RequestExecutorBuilderExtensions_Validation.Tests.cs

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public void AddValidationRule_2_Factory_Is_Null()
6969
}
7070

7171
[Fact]
72-
public async Task AddIntrospectionAllowedRule_IntegrationTest_NotAllowed()
72+
public async Task DisableIntrospection_NotAllowed()
7373
{
7474
await new ServiceCollection()
7575
.AddGraphQLServer()
@@ -84,69 +84,53 @@ public async Task AddIntrospectionAllowedRule_IntegrationTest_NotAllowed()
8484
}
8585

8686
[Fact]
87-
public async Task AllowIntrospection_IntegrationTest_NotAllowed()
87+
public async Task DisableIntrospection_NotAllowed_CustomMessage()
8888
{
8989
await new ServiceCollection()
9090
.AddGraphQLServer()
9191
.AddQueryType(d => d.Name("Query").Field("foo").Resolve("bar"))
92-
.DisableIntrospection(disable: true)
93-
.ExecuteRequestAsync(
94-
OperationRequestBuilder
95-
.New()
96-
.SetDocument("{ __schema { description } }")
97-
.Build())
98-
.MatchSnapshotAsync();
99-
}
100-
101-
[Fact]
102-
public async Task AllowIntrospection_IntegrationTest_Allowed()
103-
{
104-
await new ServiceCollection()
105-
.AddGraphQLServer()
106-
.AddQueryType(d => d.Name("Query").Field("foo").Resolve("bar"))
107-
.DisableIntrospection(disable: false)
92+
.DisableIntrospection()
10893
.ExecuteRequestAsync(
10994
OperationRequestBuilder
11095
.New()
11196
.SetDocument("{ __schema { description } }")
97+
.SetIntrospectionNotAllowedMessage("Bar")
11298
.Build())
11399
.MatchSnapshotAsync();
114100
}
115101

116102
[Fact]
117-
public async Task AllowIntrospection_IntegrationTest_NotAllowed_CustomMessage()
103+
public async Task DisableIntrospection_True_NotAllowed()
118104
{
119105
await new ServiceCollection()
120106
.AddGraphQLServer()
121107
.AddQueryType(d => d.Name("Query").Field("foo").Resolve("bar"))
122-
.DisableIntrospection()
108+
.DisableIntrospection(disable: true)
123109
.ExecuteRequestAsync(
124110
OperationRequestBuilder
125111
.New()
126112
.SetDocument("{ __schema { description } }")
127-
.SetIntrospectionNotAllowedMessage("Bar")
128113
.Build())
129114
.MatchSnapshotAsync();
130115
}
131116

132117
[Fact]
133-
public async Task AddIntrospectionAllowedRule_IntegrationTest_NotAllowed_CustomMessage()
118+
public async Task DisableIntrospection_False_Allowed()
134119
{
135120
await new ServiceCollection()
136121
.AddGraphQLServer()
137122
.AddQueryType(d => d.Name("Query").Field("foo").Resolve("bar"))
138-
.DisableIntrospection()
123+
.DisableIntrospection(disable: false)
139124
.ExecuteRequestAsync(
140125
OperationRequestBuilder
141126
.New()
142127
.SetDocument("{ __schema { description } }")
143-
.SetIntrospectionNotAllowedMessage("Baz")
144128
.Build())
145129
.MatchSnapshotAsync();
146130
}
147131

148132
[Fact]
149-
public async Task AddIntrospectionAllowedRule_IntegrationTest_Allowed()
133+
public async Task DisableIntrospection_Request_AllowIntrospection_Is_Allowed()
150134
{
151135
var executor =
152136
await new ServiceCollection()

src/HotChocolate/Core/test/Execution.Tests/DependencyInjection/__snapshots__/RequestExecutorBuilderExtensionsValidationTests.AddIntrospectionAllowedRule_IntegrationTest_NotAllowed_CustomMessage.snap

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/HotChocolate/Core/test/Execution.Tests/DependencyInjection/__snapshots__/RequestExecutorBuilderExtensionsValidationTests.AddIntrospectionAllowedRule_IntegrationTest_NotAllowed_CustomMessageFact.snap

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{
1+
{
22
"data": {
33
"__schema": {
44
"description": null
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using HotChocolate;
3+
using HotChocolate.Execution;
4+
using HotChocolate.Fusion.Configuration;
5+
using HotChocolate.Fusion.Diagnostics;
6+
using Microsoft.Extensions.DependencyInjection.Extensions;
7+
8+
namespace Microsoft.Extensions.DependencyInjection;
9+
10+
public static partial class CoreFusionGatewayBuilderExtensions
11+
{
12+
public static IFusionGatewayBuilder AddErrorFilter(
13+
this IFusionGatewayBuilder builder,
14+
Func<IError, IError> errorFilter)
15+
{
16+
ArgumentNullException.ThrowIfNull(builder);
17+
ArgumentNullException.ThrowIfNull(errorFilter);
18+
19+
return builder.ConfigureSchemaServices(
20+
(_, s) => s.AddSingleton<IErrorFilter>(new FuncErrorFilterWrapper(errorFilter)));
21+
}
22+
23+
public static IFusionGatewayBuilder AddErrorFilter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(
24+
this IFusionGatewayBuilder builder)
25+
where T : class, IErrorFilter
26+
{
27+
ArgumentNullException.ThrowIfNull(builder);
28+
29+
builder.Services.TryAddSingleton<T>();
30+
return builder.ConfigureSchemaServices(
31+
(sp, s) => s.AddSingleton<IErrorFilter, T>(_ => sp.GetRequiredService<T>()));
32+
}
33+
34+
public static IFusionGatewayBuilder AddErrorFilter<T>(
35+
this IFusionGatewayBuilder builder,
36+
Func<IServiceProvider, T> factory)
37+
where T : class, IErrorFilter
38+
{
39+
ArgumentNullException.ThrowIfNull(builder);
40+
ArgumentNullException.ThrowIfNull(factory);
41+
42+
return builder.ConfigureSchemaServices(
43+
(sp, s) => s.AddSingleton<IErrorFilter, T>(_ => factory(sp)));
44+
}
45+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using HotChocolate;
3+
using HotChocolate.Fusion.Configuration;
4+
using HotChocolate.Validation;
5+
using HotChocolate.Validation.Options;
6+
using Microsoft.Extensions.DependencyInjection;
7+
8+
namespace Microsoft.Extensions.DependencyInjection;
9+
10+
public static partial class CoreFusionGatewayBuilderExtensions
11+
{
12+
public static IFusionGatewayBuilder AddValidationVisitor<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(
13+
this IFusionGatewayBuilder builder,
14+
bool isCacheable = true)
15+
where T : DocumentValidatorVisitor, new()
16+
{
17+
ArgumentNullException.ThrowIfNull(builder);
18+
return ConfigureValidation(builder, (_, b) => b.AddVisitor<T>(isCacheable: isCacheable));
19+
}
20+
21+
public static IFusionGatewayBuilder AddValidationVisitor<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(
22+
this IFusionGatewayBuilder builder,
23+
Func<IServiceProvider, ValidationOptions, T> factory,
24+
bool isCacheable = true)
25+
where T : DocumentValidatorVisitor
26+
{
27+
ArgumentNullException.ThrowIfNull(builder);
28+
ArgumentNullException.ThrowIfNull(factory);
29+
30+
return ConfigureValidation(
31+
builder,
32+
(_, b) => b.AddVisitor(factory, isCacheable: isCacheable));
33+
}
34+
35+
public static IFusionGatewayBuilder AddValidationRule<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(
36+
this IFusionGatewayBuilder builder)
37+
where T : class, IDocumentValidatorRule, new()
38+
{
39+
ArgumentNullException.ThrowIfNull(builder);
40+
return ConfigureValidation(builder, (_, b) => b.AddRule<T>());
41+
}
42+
43+
public static IFusionGatewayBuilder AddValidationRule<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(
44+
this IFusionGatewayBuilder builder,
45+
Func<IServiceProvider, ValidationOptions, T> factory)
46+
where T : class, IDocumentValidatorRule
47+
{
48+
ArgumentNullException.ThrowIfNull(builder);
49+
ArgumentNullException.ThrowIfNull(factory);
50+
51+
return ConfigureValidation(builder, (_, b) => b.AddRule(factory));
52+
}
53+
54+
public static IFusionGatewayBuilder AddMaxExecutionDepthRule(
55+
this IFusionGatewayBuilder builder,
56+
int maxAllowedExecutionDepth,
57+
bool skipIntrospectionFields = false,
58+
bool allowRequestOverrides = false,
59+
Func<IServiceProvider, ValidationOptions, bool>? isEnabled = null)
60+
{
61+
ArgumentNullException.ThrowIfNull(builder);
62+
63+
ConfigureValidation(
64+
builder,
65+
(_, b) => b.AddMaxExecutionDepthRule(
66+
maxAllowedExecutionDepth,
67+
skipIntrospectionFields,
68+
allowRequestOverrides,
69+
isEnabled));
70+
return builder;
71+
}
72+
73+
public static IFusionGatewayBuilder DisableIntrospection(
74+
this IFusionGatewayBuilder builder,
75+
bool disable = true)
76+
{
77+
ArgumentNullException.ThrowIfNull(builder);
78+
79+
return ConfigureValidation(
80+
builder,
81+
(_, b) => b.ModifyOptions(o => o.DisableIntrospection = disable));
82+
}
83+
84+
public static IFusionGatewayBuilder DisableIntrospection(
85+
this IFusionGatewayBuilder builder,
86+
Func<IServiceProvider, ValidationOptions, bool> disable)
87+
{
88+
ArgumentNullException.ThrowIfNull(builder);
89+
ArgumentNullException.ThrowIfNull(disable);
90+
91+
return ConfigureValidation(
92+
builder,
93+
(s, b) => b.ModifyOptions(o => o.DisableIntrospection = disable(s, o)));
94+
}
95+
96+
public static IFusionGatewayBuilder SetMaxAllowedValidationErrors(
97+
this IFusionGatewayBuilder builder,
98+
int maxAllowedValidationErrors)
99+
{
100+
ArgumentNullException.ThrowIfNull(builder);
101+
102+
ConfigureValidation(
103+
builder,
104+
(_, b) => b.ModifyOptions(o => o.MaxAllowedErrors = maxAllowedValidationErrors));
105+
106+
return builder;
107+
}
108+
109+
public static IFusionGatewayBuilder SetIntrospectionAllowedDepth(
110+
this IFusionGatewayBuilder builder,
111+
ushort maxAllowedOfTypeDepth,
112+
ushort maxAllowedListRecursiveDepth)
113+
{
114+
ArgumentNullException.ThrowIfNull(builder);
115+
116+
ConfigureValidation(
117+
builder,
118+
(_, b) => b.ModifyOptions(o =>
119+
{
120+
o.MaxAllowedOfTypeDepth = maxAllowedOfTypeDepth;
121+
o.MaxAllowedListRecursiveDepth = maxAllowedListRecursiveDepth;
122+
}));
123+
124+
return builder;
125+
}
126+
127+
public static IFusionGatewayBuilder AddMaxAllowedFieldCycleDepthRule(
128+
this IFusionGatewayBuilder builder,
129+
ushort? defaultCycleLimit = 3,
130+
(SchemaCoordinate Coordinate, ushort MaxAllowed)[]? coordinateCycleLimits = null,
131+
Func<IServiceProvider, ValidationOptions, bool>? isEnabled = null)
132+
{
133+
ArgumentNullException.ThrowIfNull(builder);
134+
135+
ConfigureValidation(
136+
builder,
137+
(_, b) => b.AddMaxAllowedFieldCycleDepthRule(
138+
defaultCycleLimit,
139+
coordinateCycleLimits,
140+
isEnabled));
141+
142+
return builder;
143+
}
144+
145+
public static IFusionGatewayBuilder RemoveMaxAllowedFieldCycleDepthRule(
146+
this IFusionGatewayBuilder builder)
147+
{
148+
ArgumentNullException.ThrowIfNull(builder);
149+
150+
ConfigureValidation(builder, (_, b) => b.RemoveMaxAllowedFieldCycleDepthRule());
151+
return builder;
152+
}
153+
154+
public static IFusionGatewayBuilder ConfigureValidation(
155+
this IFusionGatewayBuilder builder,
156+
Action<IServiceProvider, DocumentValidatorBuilder> configure)
157+
{
158+
return FusionSetupUtilities.Configure(
159+
builder,
160+
options => options.DocumentValidatorBuilderModifiers.Add(configure));
161+
}
162+
}

0 commit comments

Comments
 (0)