diff --git a/src/GraphQL.FluentValidation/ArgumentValidation.cs b/src/GraphQL.FluentValidation/ArgumentValidation.cs index 5db2d07d..8f09a682 100644 --- a/src/GraphQL.FluentValidation/ArgumentValidation.cs +++ b/src/GraphQL.FluentValidation/ArgumentValidation.cs @@ -64,4 +64,4 @@ static ValidationContext BuildValidationContext(object? instance, object userCon validationContext.RootContextData.Add("UserContext", userContext); return validationContext; } -} \ No newline at end of file +} diff --git a/src/GraphQL.FluentValidation/FluentValidationExtensions.cs b/src/GraphQL.FluentValidation/FluentValidationExtensions.cs index e3b8b1ea..88826ef6 100644 --- a/src/GraphQL.FluentValidation/FluentValidationExtensions.cs +++ b/src/GraphQL.FluentValidation/FluentValidationExtensions.cs @@ -1,6 +1,5 @@ using FluentValidation; using GraphQL.FluentValidation; -using GraphQL.Types; namespace GraphQL { @@ -9,17 +8,6 @@ namespace GraphQL /// public static partial class FluentValidationExtensions { - /// - /// Validate an instance against the current cached validators defined by . - /// - public static void ValidateInstance(this ResolveFieldContext context, TInstance input) - { - Guard.AgainstNull(context, nameof(context)); - Guard.AgainstNull(input, nameof(input)); - var type = input!.GetType(); - ArgumentValidation.Validate(ArgumentTypeCacheBag.GetCache(context), type, input, context.UserContext); - } - /// /// Adds a FieldMiddleware to the GraphQL pipeline that converts a to s./> /// diff --git a/src/Tests/Arguments/AsyncComplexInput.cs b/src/Tests/Arguments/AsyncComplexInput.cs new file mode 100644 index 00000000..6ed24650 --- /dev/null +++ b/src/Tests/Arguments/AsyncComplexInput.cs @@ -0,0 +1,4 @@ +public class AsyncComplexInput +{ + public ComplexInputInner? Inner { get; set; } +} \ No newline at end of file diff --git a/src/Tests/Arguments/AsyncComplexInputGraph.cs b/src/Tests/Arguments/AsyncComplexInputGraph.cs new file mode 100644 index 00000000..be36a5ba --- /dev/null +++ b/src/Tests/Arguments/AsyncComplexInputGraph.cs @@ -0,0 +1,10 @@ +using GraphQL.Types; + +public class AsyncComplexInputGraph : + InputObjectGraphType +{ + public AsyncComplexInputGraph() + { + Field("inner"); + } +} \ No newline at end of file diff --git a/src/Tests/Arguments/AsyncComplexInputValidator.cs b/src/Tests/Arguments/AsyncComplexInputValidator.cs new file mode 100644 index 00000000..0c10b6ec --- /dev/null +++ b/src/Tests/Arguments/AsyncComplexInputValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; +using System.Threading.Tasks; + +public class AsyncComplexInputValidator : + AbstractValidator +{ + public AsyncComplexInputValidator() + { + RuleFor(_ => _.Inner!) + .NotEmpty() + .MustAsync((o, token) => { + return Task.FromResult(o != null && !string.IsNullOrWhiteSpace(o.Content)); + }).WithMessage("Inner async test failed msg.") + .SetValidator(new ComplexInputInnerValidator()); + } +} \ No newline at end of file diff --git a/src/Tests/Arguments/ComplexInput.cs b/src/Tests/Arguments/ComplexInput.cs index 27c01f11..2f0a5423 100644 --- a/src/Tests/Arguments/ComplexInput.cs +++ b/src/Tests/Arguments/ComplexInput.cs @@ -1,4 +1,8 @@ -public class ComplexInput +using System.Collections.Generic; + +public class ComplexInput { public ComplexInputInner? Inner { get; set; } + + public List? Items { get; set; } } \ No newline at end of file diff --git a/src/Tests/Arguments/ComplexInputGraph.cs b/src/Tests/Arguments/ComplexInputGraph.cs index 2c6e706e..68ea92fd 100644 --- a/src/Tests/Arguments/ComplexInputGraph.cs +++ b/src/Tests/Arguments/ComplexInputGraph.cs @@ -6,5 +6,7 @@ public class ComplexInputGraph : public ComplexInputGraph() { Field("inner"); + + Field>>("items"); } } \ No newline at end of file diff --git a/src/Tests/Arguments/ComplexInputListItem.cs b/src/Tests/Arguments/ComplexInputListItem.cs new file mode 100644 index 00000000..376739a7 --- /dev/null +++ b/src/Tests/Arguments/ComplexInputListItem.cs @@ -0,0 +1,5 @@ +public class ComplexInputListItem +{ + public int Id { get; set; } + public string? Content { get; set; } +} \ No newline at end of file diff --git a/src/Tests/Arguments/ComplexInputListItemGraph.cs b/src/Tests/Arguments/ComplexInputListItemGraph.cs new file mode 100644 index 00000000..878d5148 --- /dev/null +++ b/src/Tests/Arguments/ComplexInputListItemGraph.cs @@ -0,0 +1,16 @@ +using GraphQL.Types; + +public class ComplexInputListItemGraph : + InputObjectGraphType +{ + public ComplexInputListItemGraph() + { + Field>() + .Name("id") + .Resolve(ctx => ctx.Source.Id); + + Field() + .Name("content") + .Resolve(ctx => ctx.Source.Content); + } +} \ No newline at end of file diff --git a/src/Tests/Arguments/ComplexInputListItemValidator.cs b/src/Tests/Arguments/ComplexInputListItemValidator.cs new file mode 100644 index 00000000..08056e83 --- /dev/null +++ b/src/Tests/Arguments/ComplexInputListItemValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; + +public class ComplexInputListItemValidator : + AbstractValidator +{ + public ComplexInputListItemValidator() + { + RuleFor(_ => _.Id) + .NotEmpty(); + + RuleFor(_ => _.Content) + .NotEmpty(); + } +} \ No newline at end of file diff --git a/src/Tests/Arguments/ComplexInputValidator.cs b/src/Tests/Arguments/ComplexInputValidator.cs index 52831278..ef74fbf0 100644 --- a/src/Tests/Arguments/ComplexInputValidator.cs +++ b/src/Tests/Arguments/ComplexInputValidator.cs @@ -8,5 +8,9 @@ public ComplexInputValidator() RuleFor(_ => _.Inner!) .NotEmpty() .SetValidator(new ComplexInputInnerValidator()); + + RuleFor(_ => _.Items) + .NotEmpty() + .ForEach(i => i.SetValidator(new ComplexInputListItemValidator())); } } \ No newline at end of file diff --git a/src/Tests/IntegrationTests.AsyncComplexInvalid.approved.txt b/src/Tests/IntegrationTests.AsyncComplexInvalid.approved.txt new file mode 100644 index 00000000..17aa2d14 --- /dev/null +++ b/src/Tests/IntegrationTests.AsyncComplexInvalid.approved.txt @@ -0,0 +1,13 @@ +{ + data: { + asyncComplexInputQuery: null + }, + errors: [ + { + message: 'Inner: Inner async test failed msg.' + }, + { + message: 'Inner.Content: \'Content\' must not be empty.' + } + ] +} \ No newline at end of file diff --git a/src/Tests/IntegrationTests.AsyncComplexValid.approved.txt b/src/Tests/IntegrationTests.AsyncComplexValid.approved.txt new file mode 100644 index 00000000..5b85e1c9 --- /dev/null +++ b/src/Tests/IntegrationTests.AsyncComplexValid.approved.txt @@ -0,0 +1,7 @@ +{ + data: { + asyncComplexInputQuery: { + data: 'TheContent' + } + } +} \ No newline at end of file diff --git a/src/Tests/IntegrationTests.ComplexInvalid.approved.txt b/src/Tests/IntegrationTests.ComplexInvalid.approved.txt index 7827fe82..a629e50e 100644 --- a/src/Tests/IntegrationTests.ComplexInvalid.approved.txt +++ b/src/Tests/IntegrationTests.ComplexInvalid.approved.txt @@ -5,6 +5,9 @@ errors: [ { message: 'Inner.Content: \'Content\' must not be empty.' + }, + { + message: 'Items: \'Items\' must not be empty.' } ] } \ No newline at end of file diff --git a/src/Tests/IntegrationTests.ComplexInvalid2.approved.txt b/src/Tests/IntegrationTests.ComplexInvalid2.approved.txt index 894a6e86..450ea24c 100644 --- a/src/Tests/IntegrationTests.ComplexInvalid2.approved.txt +++ b/src/Tests/IntegrationTests.ComplexInvalid2.approved.txt @@ -5,6 +5,9 @@ errors: [ { message: 'Inner: \'Inner\' must not be empty.' + }, + { + message: 'Items: \'Items\' must not be empty.' } ] } \ No newline at end of file diff --git a/src/Tests/IntegrationTests.ComplexValid.approved.txt b/src/Tests/IntegrationTests.ComplexValid.approved.txt index c1d5c696..b087116b 100644 --- a/src/Tests/IntegrationTests.ComplexValid.approved.txt +++ b/src/Tests/IntegrationTests.ComplexValid.approved.txt @@ -1,7 +1,7 @@ { data: { complexInputQuery: { - data: 'TheContent' + data: '{"Inner":{"Content":"TheContent"},"Items":[{"Id":1,"Content":"Some content 1"},{"Id":2,"Content":"Some content 2"}]}' } } } \ No newline at end of file diff --git a/src/Tests/IntegrationTests.cs b/src/Tests/IntegrationTests.cs index 6fa50da1..c23bf11c 100644 --- a/src/Tests/IntegrationTests.cs +++ b/src/Tests/IntegrationTests.cs @@ -100,7 +100,11 @@ public async Task ComplexValid() input: { inner: { content: ""TheContent"" - } + }, + items: [ + { id: 1, content: ""Some content 1"" }, + { id: 2, content: ""Some content 2"" } + ] } ) { @@ -121,7 +125,8 @@ public async Task ComplexInvalid() input: { inner: { content: """" - } + }, + items: [] } ) { @@ -140,7 +145,8 @@ public async Task ComplexInvalid2() complexInputQuery ( input: { - inner: null + inner: null, + items: null } ) { @@ -151,6 +157,54 @@ public async Task ComplexInvalid2() ObjectApprover.Verify(result); } + [Fact] + public async Task AsyncComplexValid() + { + var queryString = @" +{ + asyncComplexInputQuery + ( + input: { + inner: { + content: ""TheContent"" + }, + items: [ + { id: 1, content: ""Some content 1"" }, + { id: 2, content: ""Some content 2"" } + ] + } + ) + { + data + } +}"; + var result = await QueryExecutor.ExecuteQuery(queryString, null, typeCache); + ObjectApprover.Verify(result); + } + + [Fact] + public async Task AsyncComplexInvalid() + { + var queryString = @" +{ + asyncComplexInputQuery + ( + input: { + inner: { + content: """" + }, + items: null + } + ) + { + data + } +}"; + var result = await QueryExecutor.ExecuteQuery(queryString, null, typeCache); + ObjectApprover.Verify(result); + } + + public IntegrationTests(ITestOutputHelper output) : base(output) { diff --git a/src/Tests/Query.cs b/src/Tests/Query.cs index 7f677e36..37ec0aa2 100644 --- a/src/Tests/Query.cs +++ b/src/Tests/Query.cs @@ -1,5 +1,6 @@ -using GraphQL; +using GraphQL; using GraphQL.Types; +using Newtonsoft.Json; public class Query : ObjectGraphType @@ -31,7 +32,7 @@ public Query() var input = context.GetValidatedArgument("input"); return new Result { - Data = input.Inner!.Content + Data = JsonConvert.SerializeObject(input) }; } ); @@ -50,6 +51,21 @@ public Query() }; } ); + + FieldAsync( + "asyncComplexInputQuery", + arguments: new QueryArguments( + new QueryArgument { Name = "input", } + ), + resolve: async context => + { + var input = await context.GetValidatedArgumentAsync("input"); + return new Result + { + Data = input.Inner!.Content + }; + } + ); } } \ No newline at end of file