From 34f59d769760c561dd13ec1dc185d7764de279e5 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 8 May 2026 23:31:49 +0300 Subject: [PATCH 1/3] Initial Draft --- .config/dotnet-tools.json | 2 +- ...DataAnnotatedModelValidations.Tests.csproj | 14 +- .../Extensions/ReportingExtensionsTests.cs | 123 -------------- .../Extensions/ValidationExtensionsTests.cs | 159 ------------------ .../Middleware/ValidatorMiddlewareTests.cs | 39 ----- .../PipelineExecutionTests.GraphQL.cs | 24 ++- .../Pipeline/PipelineExecutionTests.Models.cs | 16 ++ .../Pipeline/PipelineExecutionTests.cs | 50 ++++-- ...Return_Expected_Errors_invalid_record.snap | 10 +- ...rn_Expected_Errors_invalid_record_ext.snap | 10 +- ...ted_Errors_sampleNonNull_min_length_3.snap | 10 +- ...xpected_Errors_sampleNonNull_required.snap | 2 +- ...rors_sample_alias_blank_name_required.snap | 6 - ...ted_Errors_sample_blank_name_required.snap | 6 - ...empty-property-name_custom_validation.snap | 6 - ...essage-from-service_custom_validation.snap | 6 - ...n_Expected_Errors_sample_min_length_3.snap | 6 - ...iple-property-names_custom_validation.snap | 30 +--- ...le_no-property-name_custom_validation.snap | 6 - ..._null-error-message_custom_validation.snap | 6 - ...eturn_Expected_Errors_sample_required.snap | 2 +- ...ected_Errors_setFunkyRecord_no_errors.snap | 7 + ...rentExt_nested_validations_ext_byName.snap | 26 +-- ...ntExt_nested_validations_ext_byOpType.snap | 26 +-- ...rentExt_nested_validations_ext_byType.snap | 26 +-- ...entExt_nested_validations_ext_generic.snap | 26 +-- ...tedParent_multiple_nested_validations.snap | 99 +++++++++++ ...rs_setNestedParent_nested_validations.snap | 26 +-- ...rrors_setNoValidationRecord_no_errors.snap | 7 + ...LevelValidationAttribute_invalid_name.snap | 6 - ...nAttribute_top_level_validation_error.snap | 6 - ...meterValidationAttribute_invalid_name.snap | 6 - ...nAttribute_top_level_validation_error.snap | 6 - ...nthesizedProperty_blank_name_required.snap | 6 - ...s_setSampleRecord_blank_name_required.snap | 6 - ..._Errors_setSample_blank_name_required.snap | 6 - ...ple_hc_null_error_blank_name_required.snap | 28 +++ ..._Expected_Errors_setText_min_length_5.snap | 6 - ...pected_Errors_text_alias_min_length_5.snap | 6 - ...urn_Expected_Errors_text_min_length_5.snap | 6 - .../DataAnnotatedModelValidations.csproj | 4 +- .../Extensions/ReportingExtensions.cs | 4 +- .../Extensions/ValidationExtensions.cs | 39 +++-- DataAnnotatedModelValidations/README.md | 52 ++---- .../ValidatorTypeInterceptor.cs | 28 +-- DataAnnotatedModelValidations/Usings.cs | 2 +- README.md | 30 +--- 47 files changed, 320 insertions(+), 703 deletions(-) delete mode 100644 DataAnnotatedModelValidations.Tests/Extensions/ReportingExtensionsTests.cs delete mode 100644 DataAnnotatedModelValidations.Tests/Extensions/ValidationExtensionsTests.cs delete mode 100644 DataAnnotatedModelValidations.Tests/Middleware/ValidatorMiddlewareTests.cs create mode 100644 DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setFunkyRecord_no_errors.snap create mode 100644 DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParent_multiple_nested_validations.snap create mode 100644 DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNoValidationRecord_no_errors.snap create mode 100644 DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSample_hc_null_error_blank_name_required.snap diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index f24b4ac..c62edc8 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-reportgenerator-globaltool": { - "version": "5.5.4", + "version": "5.5.9", "commands": [ "reportgenerator" ], diff --git a/DataAnnotatedModelValidations.Tests/DataAnnotatedModelValidations.Tests.csproj b/DataAnnotatedModelValidations.Tests/DataAnnotatedModelValidations.Tests.csproj index a34aea0..a4e22ab 100644 --- a/DataAnnotatedModelValidations.Tests/DataAnnotatedModelValidations.Tests.csproj +++ b/DataAnnotatedModelValidations.Tests/DataAnnotatedModelValidations.Tests.csproj @@ -15,17 +15,17 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + - + all diff --git a/DataAnnotatedModelValidations.Tests/Extensions/ReportingExtensionsTests.cs b/DataAnnotatedModelValidations.Tests/Extensions/ReportingExtensionsTests.cs deleted file mode 100644 index db51a8a..0000000 --- a/DataAnnotatedModelValidations.Tests/Extensions/ReportingExtensionsTests.cs +++ /dev/null @@ -1,123 +0,0 @@ -using DataAnnotatedModelValidations.Extensions; - -namespace DataAnnotatedModelValidations.Tests.Extensions; - -public class ReportingExtensionsTests -{ - private readonly IInputField _argument; - - public ReportingExtensionsTests() - { - _argument = Substitute.For(); - _argument.Name.Returns("Name"); - } - - [Fact] - public void GetNormalizedMemberName_Should_Normalize_Value() => - Assert.Equal("helloWorld_0_", "HelloWorld[0]".GetNormalizedMemberName()); - - [Fact] - public void ToTokenizedMemberNames_Should_Tokenize_Value() => - Assert.Equal( - [ - "hello", - "world" - ], - "hello:world".ToTokenizedMemberNames() - ); - - [Fact] - public void ToComposedMemberNames_Should_Enumerate_Name_When_MemberName_Is_Null() => - Assert.Equal(["Name"], _argument.ToComposedMemberNames(default, default)); - - [Fact] - public void ToComposedMemberNames_Should_Enumerate_Name_When_MemberName_Is_Blank() => - Assert.Equal(["Name"], _argument.ToComposedMemberNames(" ", default)); - - [Fact] - public void ToComposedMemberNames_Should_Enumerate_MemberName_And_Name_When_ValueValidation_Is_Not_True() => - Assert.Equal( - [ - "Name", - "hello", - "world_0_" - ], - _argument.ToComposedMemberNames("Hello:World[0]", default) - ); - - [Fact] - public void ToComposedMemberNames_Should_Enumerate_MemberName_When_ValueValidation_Is_True() => - Assert.Equal( - [ - "hello", - "world_0_" - ], - _argument.ToComposedMemberNames("Hello:World[0]", true) - ); - - [Fact] - public void ToArgumentPath_Should_Produce_Path() - { - var result = new[] { "hello" }.ToArgumentPath(["world"]); - - Assert.Equal( - Path.Root.Append("hello").Append("world"), - result - ); - } - - [Fact] - public void ReportError_Should_Report_With_Code_Path_And_Message_When_Message_Not_Null() - { - var context = Substitute.For(); - - context.ReportError( - _argument, - ["hello"], - default, - "world", - default - ); - - var receivedArguments = context.ReceivedCalls().SelectMany(x => x.GetArguments()).ToArray(); - - Assert.Single( - receivedArguments, - x => - x is Error - { - Code: ReportingConsts.GenericErrorCode, - Message: "world", - Path: { Length: 2 } p - } - && p.Equals(Path.Root.Append("hello").Append("Name")) - ); - } - - [Fact] - public void ReportError_Should_Report_With_Code_Path_And_Default_Message_When_Message_Null() - { - var context = Substitute.For(); - - context.ReportError( - _argument, - ["hello"], - default, - default, - "member" - ); - - var receivedArguments = context.ReceivedCalls().SelectMany(x => x.GetArguments()).ToArray(); - - Assert.Single( - receivedArguments, - x => x is Error - { - Code: ReportingConsts.GenericErrorCode, - Message: ReportingConsts.GenericErrorMessage, - Path: { Length: 3 } p - } - && p.Equals(Path.Root.Append("hello").Append("Name").Append("member")) - ); - } -} diff --git a/DataAnnotatedModelValidations.Tests/Extensions/ValidationExtensionsTests.cs b/DataAnnotatedModelValidations.Tests/Extensions/ValidationExtensionsTests.cs deleted file mode 100644 index 741f575..0000000 --- a/DataAnnotatedModelValidations.Tests/Extensions/ValidationExtensionsTests.cs +++ /dev/null @@ -1,159 +0,0 @@ -using DataAnnotatedModelValidations.Extensions; - -namespace DataAnnotatedModelValidations.Tests.Extensions; - -public class ValidationExtensionsTests -{ - private readonly IMiddlewareContext _mockContext; - - public ValidationExtensionsTests() - { - _mockContext = Substitute.For(); - _mockContext.Path.Returns(Path.Root.Append("path")); - } - - [Fact] - public void ValidateInputs_Should_Not_Report_Error_When_Field_ContextKey_Is_Not_Set() - { - _mockContext.Selection.Field.ContextData.ContainsKey(Arg.Any()).Returns(false); - - _mockContext.ValidateInputs(); - - Assert.DoesNotContain(_mockContext.ReceivedCalls(), x => x.GetMethodInfo().Name == "ReportError"); - } - - [Fact] - public void ValidateInputs_Should_Not_Report_Error_When_Field_ContextKey_Is_Set_And_Arguments_Is_Null() - { - _mockContext.Selection.Field.ContextData.ContainsKey(Arg.Any()).Returns(true); - _mockContext.Selection.Field.Arguments.Returns(default(IFieldCollection?)!); - - _mockContext.ValidateInputs(); - - Assert.DoesNotContain(_mockContext.ReceivedCalls(), x => x.GetMethodInfo().Name == "ReportError"); - } - - [Fact] - public void ValidateInputs_Should_Not_Report_Error_When_Field_ContextKey_Is_Set_And_No_Arguments_Present() - { - _mockContext.Selection.Field.ContextData.ContainsKey(Arg.Any()).Returns(true); - _mockContext.Selection.Field.Arguments.Returns(FieldCollection.Empty); - - _mockContext.ValidateInputs(); - - Assert.DoesNotContain(_mockContext.ReceivedCalls(), x => x.GetMethodInfo().Name == "ReportError"); - } - - [Fact] - public void ValidateInputs_Should_Not_Report_Error_When_Field_ContextKey_Is_Set_And_All_Arguments_Are_Null() - { - _mockContext.Selection.Field.ContextData.ContainsKey(Arg.Any()).Returns(true); - var collection = Substitute.For>(); - using var collectionEnumerator = collection.GetEnumerator(); - using var mockEnumerator = MockEnumerator(null!, null!, null!).GetEnumerator(); - collectionEnumerator.Returns(mockEnumerator); - collection.Count.Returns(3); - _mockContext.Selection.Field.Arguments.Returns(collection); - - _mockContext.ValidateInputs(); - - Assert.DoesNotContain(_mockContext.ReceivedCalls(), x => x.GetMethodInfo().Name == "ReportError"); - } - - [Fact] - public void ValidateInputs_Should_Not_Report_Error_When_Field_ContextKey_Is_Set_And_No_Arguments_Has_ContextKey_Set() - { - _mockContext.Selection.Field.ContextData.ContainsKey(Arg.Any()).Returns(true); - var argument = Substitute.For(); - argument.Name.Returns("sampleArgument"); - argument.ContextData.ContainsKey(Arg.Any()).Returns(false); - var collection = Substitute.For>(); - using var collectionEnumerator = collection.GetEnumerator(); - using var mockEnumerator = MockEnumerator(argument).GetEnumerator(); - collectionEnumerator.Returns(mockEnumerator); - collection.Count.Returns(1); - _mockContext.Selection.Field.Arguments.Returns(collection); - - _mockContext.ValidateInputs(); - - Assert.DoesNotContain(_mockContext.ReceivedCalls(), x => x.GetMethodInfo().Name == "ReportError"); - } - - [Fact] - public void ValidateInputs_Should_Not_Report_Error_When_Field_ContextKey_Is_Set_And_Context_Returns_Null_For_Argument() - { - _mockContext.Selection.Field.ContextData.ContainsKey(Arg.Any()).Returns(true); - var argument = Substitute.For(); - argument.Name.Returns("sampleArgument"); - argument.ContextData.ContainsKey(Arg.Any()).Returns(true); - var collection = Substitute.For>(); - using var collectionEnumerator = collection.GetEnumerator(); - using var mockEnumerator = MockEnumerator(argument).GetEnumerator(); - collectionEnumerator.Returns(mockEnumerator); - collection.Count.Returns(1); - _mockContext.Selection.Field.Arguments.Returns(collection); - _mockContext.ArgumentValue(Arg.Any()).Returns(null); - - _mockContext.ValidateInputs(); - - Assert.DoesNotContain(_mockContext.ReceivedCalls(), x => x.GetMethodInfo().Name == "ReportError"); - } - - - [Fact] - public void ValidateInputs_Should_Not_Report_Error_When_Field_ContextKey_Is_Set_And_Arguments_Has_ContextKey_Set_With_Valid_Data() - { - _mockContext.Selection.Field.ContextData.ContainsKey(Arg.Any()).Returns(true); - var argument = Substitute.For(); - argument.Name.Returns("sampleArgument"); - argument.ContextData.ContainsKey(Arg.Any()).Returns(true); - var collection = Substitute.For>(); - using var collectionEnumerator = collection.GetEnumerator(); - using var mockEnumerator = MockEnumerator(argument).GetEnumerator(); - collectionEnumerator.Returns(mockEnumerator); - collection.Count.Returns(1); - _mockContext.Selection.Field.Arguments.Returns(collection); - _mockContext.ArgumentValue(Arg.Any()).Returns(new SampleRecord("Name")); - - _mockContext.ValidateInputs(); - - Assert.DoesNotContain(_mockContext.ReceivedCalls(), x => x.GetMethodInfo().Name == "ReportError"); - } - - [Fact] - public void ValidateInputs_Should_Report_Error_When_Field_ContextKey_Is_Set_And_Arguments_Has_ContextKey_Set_With_Invalid_Data() - { - _mockContext.Selection.Field.ContextData.ContainsKey(Arg.Any()).Returns(true); - var argument = Substitute.For(); - argument.Name.Returns("sampleArgument"); - argument.ContextData.ContainsKey(Arg.Any()).Returns(true); - var collection = Substitute.For>(); - using var collectionEnumerator = collection.GetEnumerator(); - using var mockEnumerator = MockEnumerator(argument).GetEnumerator(); - collectionEnumerator.Returns(mockEnumerator); - collection.Count.Returns(1); - _mockContext.Selection.Field.Arguments.Returns(collection); - _mockContext.ArgumentValue(Arg.Any()).Returns(new SampleRecord()); - - _mockContext.ValidateInputs(); - - Assert.Contains(_mockContext.ReceivedCalls(), - x => - x.GetMethodInfo().Name == "ReportError" - && x.GetArguments() is [IError { Code: ReportingConsts.GenericErrorCode }] - ); - } - - private static IEnumerable MockEnumerator(params IInputField?[] items) - { - foreach (var item in items) - { - yield return item!; - } - } - - public record SampleRecord( - [property: Required] - string? Name = default - ); -} diff --git a/DataAnnotatedModelValidations.Tests/Middleware/ValidatorMiddlewareTests.cs b/DataAnnotatedModelValidations.Tests/Middleware/ValidatorMiddlewareTests.cs deleted file mode 100644 index a47027c..0000000 --- a/DataAnnotatedModelValidations.Tests/Middleware/ValidatorMiddlewareTests.cs +++ /dev/null @@ -1,39 +0,0 @@ -using DataAnnotatedModelValidations.Middleware; - -namespace DataAnnotatedModelValidations.Tests.Middleware; - -public class ValidatorMiddlewareTests -{ - private readonly ValidatorMiddleware _middleware; - private readonly IMiddlewareContext _mockContext; - private readonly FieldDelegate _mockFieldDelegate; - - public ValidatorMiddlewareTests() - { - _mockContext = Substitute.For(); - _mockFieldDelegate = Substitute.For(); - _middleware = new(_mockFieldDelegate); - _mockContext.Selection.Field - .Returns(Substitute.For()); - } - - [Fact] - public async Task InvokeAsync_Should_Call_Field_Delegate_When_No_Errors() - { - _mockContext.HasErrors.Returns(false); - - await _middleware.InvokeAsync(_mockContext); - - Assert.NotEmpty(_mockFieldDelegate.ReceivedCalls()); - } - - [Fact] - public async Task InvokeAsync_Should_Not_Call_Field_Delegate_When_Errors_Reported() - { - _mockContext.HasErrors.Returns(true); - - await _middleware.InvokeAsync(_mockContext); - - Assert.Empty(_mockFieldDelegate.ReceivedCalls()); - } -} diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.GraphQL.cs b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.GraphQL.cs index 02d63e3..1411ad6 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.GraphQL.cs +++ b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.GraphQL.cs @@ -70,24 +70,24 @@ CancellationToken cancellationToken public string? GetText([StringLength(100, MinimumLength = 5)] string? txt) => txt; - public string? GetTextIgnoreValidation([IgnoreModelValidation] [StringLength(100, MinimumLength = 5)] string? txt) => txt; + public string? GetTextIgnoreValidation([IgnoreModelValidation][StringLength(100, MinimumLength = 5)] string? txt) => txt; public Sample? GetSample(Sample? obj) => obj; - public Sample GetSampleNonNull(Sample obj) => obj; + public Sample? GetSampleNonNull(Sample obj) => obj; public Sample? GetSampleIgnoreValidation([IgnoreModelValidation] Sample? obj) => obj; public Sample? GetSampleWithService(Sample? obj, [Service] MockService service) => service.Get(obj?.Name); - public InvalidRecord GetInvalidRecord(InvalidRecord obj) => obj; + public InvalidRecord? GetInvalidRecord(InvalidRecord obj) => obj; } [ExtendObjectType(OperationTypeNames.Query)] public class QueryExtension { - public InvalidRecord GetInvalidRecordExt([Parent] Query parent, InvalidRecord obj) => parent.GetInvalidRecord(obj); + public InvalidRecord? GetInvalidRecordExt([Parent] Query parent, InvalidRecord obj) => parent.GetInvalidRecord(obj); } public class Mutation @@ -96,8 +96,14 @@ public class Mutation public Sample? SetSample(Sample? obj) => obj; + public Sample SetSampleHcNullError(Sample obj) => obj; + public SampleRecord? SetSampleRecord(SampleRecord? obj) => obj; + public FunkyRecord? SetFunkyRecord(FunkyRecord obj) => obj; + + public NoValidationRecord? SetNoValidationRecord(NoValidationRecord obj) => obj; + public SampleRecordWithSynthesizedProperty? SetSampleRecordWithSynthesizedProperty(SampleRecordWithSynthesizedProperty? obj) => obj; public SampleRecordWithClassLevelValidationAttribute? SetSampleRecordWithClassLevelValidationAttribute( @@ -108,31 +114,31 @@ public class Mutation [AllStringPropertiesMustBeFilled] SampleRecordWithParameterValidationAttribute? obj ) => obj; - public NestedParent SetNestedParent(NestedParent obj) => obj; + public NestedParent? SetNestedParent(NestedParent obj) => obj; } [ExtendObjectType(OperationTypeNames.Mutation)] public class MutationExtensionByName { - public NestedParent SetNestedParentExtByName([Parent] Mutation parent, NestedParent obj) => parent.SetNestedParent(obj); + public NestedParent? SetNestedParentExtByName([Parent] Mutation parent, NestedParent obj) => parent.SetNestedParent(obj); } [ExtendObjectType(OperationType.Mutation)] public class MutationExtensionByOperationType { - public NestedParent SetNestedParentExtByOpType([Parent] Mutation parent, NestedParent obj) => parent.SetNestedParent(obj); + public NestedParent? SetNestedParentExtByOpType([Parent] Mutation parent, NestedParent obj) => parent.SetNestedParent(obj); } [ExtendObjectType(typeof(Mutation))] public class MutationExtensionByType { - public NestedParent SetNestedParentExtByType([Parent] Mutation parent, NestedParent obj) => parent.SetNestedParent(obj); + public NestedParent? SetNestedParentExtByType([Parent] Mutation parent, NestedParent obj) => parent.SetNestedParent(obj); } [ExtendObjectType] public class MutationExtensionGeneric { - public NestedParent SetNestedParentExtGeneric([Parent] Mutation parent, NestedParent obj) => parent.SetNestedParent(obj); + public NestedParent? SetNestedParentExtGeneric([Parent] Mutation parent, NestedParent obj) => parent.SetNestedParent(obj); } [ExtendObjectType] diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Models.cs b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Models.cs index 3044fde..2bc9b2c 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Models.cs +++ b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Models.cs @@ -102,6 +102,22 @@ public IEnumerable Validate(ValidationContext validationContex } } + public record FunkyRecord : IValidatableObject + { + public string Text { get; init; } = string.Empty; + + [GraphQLIgnore] + public IEnumerable Validate(ValidationContext validationContext) + { + yield break; + } + } + + public record NoValidationRecord + { + public string Text { get; init; } = string.Empty; + } + public record SampleResponse { public string Name { get; init; } = string.Empty; diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.cs b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.cs index df2a748..3ee17b6 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.cs +++ b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.cs @@ -78,18 +78,18 @@ public async Task Get_Data_With_Filter_Sort_And_Or_Pagination_Definitions_Presen var result = await requestExecutor.ExecuteAsync(query); - Assert.Null(result.ExpectOperationResult().Errors); + Assert.Empty(result.ExpectOperationResult().Errors); result.ExpectOperationResult().ToJson().MatchSnapshot(new SnapshotNameExtension($"{description}.snap")); } [Theory] - [InlineData("""{ info }""", null, "info")] - [InlineData("""{ message }""", null, "message")] + [InlineData("""{ info }""", 0, "info")] + [InlineData("""{ message }""", 0, "message")] [InlineData("""{ invalidRecord(obj: { text: "test" }) { text } }""", 1, "invalid_record")] [InlineData("""{ invalidRecordExt(obj: { text: "test" }) { text } }""", 1, "invalid_record_ext")] - [InlineData("""{ sample(obj: null) { name } }""", null, "sample_null_no_errors")] + [InlineData("""{ sample(obj: null) { name } }""", 0, "sample_null_no_errors")] [InlineData("""{ sample(obj: { name: "" }) { name } }""", 1, "sample_blank_name_required")] - [InlineData("""{ sample(obj: { name: "Jane" }) { name } }""", null, "sample_no_errors")] + [InlineData("""{ sample(obj: { name: "Jane" }) { name } }""", 0, "sample_no_errors")] [InlineData("""{ sample(obj: { name: "ab" }) { name } }""", 1, "sample_min_length_3")] [InlineData("""{ sample(obj: { name: "empty-property-name" }) { name } }""", 1, "sample_empty-property-name_custom_validation")] [InlineData("""{ sample(obj: { name: "message-from-service" }) { name } }""", 1, "sample_message-from-service_custom_validation")] @@ -98,17 +98,20 @@ public async Task Get_Data_With_Filter_Sort_And_Or_Pagination_Definitions_Presen [InlineData("""{ sample(obj: { name: "null-error-message" }) { name } }""", 1, "sample_null-error-message_custom_validation")] [InlineData("""{ sample(obj: { name: null }) { name } }""", 1, "sample_required")] [InlineData("""{ sampleAlias:sample(obj: { name: "" }) { name } }""", 1, "sample_alias_blank_name_required")] - [InlineData("""{ sampleIgnoreValidation(obj: null) { name } }""", null, "sampleIgnoreValidation_no_errors")] + [InlineData("""{ sampleIgnoreValidation(obj: null) { name } }""", 0, "sampleIgnoreValidation_no_errors")] [InlineData("""{ sampleNonNull(obj: null) { name } }""", 1, "sampleNonNull_required")] [InlineData("""{ sampleNonNull(obj: { name: "ab" }) { name } }""", 1, "sampleNonNull_min_length_3")] - [InlineData("""{ sampleWithService(obj: { name: "Jane" }) { name } }""", null, "sampleWithService_no_errors")] + [InlineData("""{ sampleWithService(obj: { name: "Jane" }) { name } }""", 0, "sampleWithService_no_errors")] [InlineData("""{ text(txt: "abc") }""", 1, "text_min_length_5")] - [InlineData("""{ text(txt: "abcdefg") }""", null, "text_no_errors")] + [InlineData("""{ text(txt: "abcdefg") }""", 0, "text_no_errors")] [InlineData("""{ textAlias:text(txt: "abc") }""", 1, "text_alias_min_length_5")] - [InlineData("""{ textIgnoreValidation(txt: "a") }""", null, "textIgnoreValidation_no_errors")] + [InlineData("""{ textIgnoreValidation(txt: "a") }""", 0, "textIgnoreValidation_no_errors")] [InlineData("""mutation { setText(txt: "abc") }""", 1, "setText_min_length_5")] [InlineData("""mutation { setSample(obj: { name: "" }) { name } }""", 1, "setSample_blank_name_required")] + [InlineData("""mutation { setSampleHcNullError(obj: { name: "" }) { name } }""", 2, "setSample_hc_null_error_blank_name_required")] [InlineData("""mutation { setSampleRecord(obj: { name: "" }) { name } }""", 1, "setSampleRecord_blank_name_required")] + [InlineData("""mutation { setFunkyRecord(obj: { text: "" }) { text } }""", 0, "setFunkyRecord_no_errors")] + [InlineData("""mutation { setNoValidationRecord(obj: { text: "" }) { text } }""", 0, "setNoValidationRecord_no_errors")] [InlineData( """mutation { setSampleRecordWithSynthesizedProperty(obj: { name: "" }) { name } }""", 1, @@ -126,7 +129,7 @@ public async Task Get_Data_With_Filter_Sort_And_Or_Pagination_Definitions_Presen )] [InlineData( """mutation { setSampleRecordWithClassLevelValidationAttribute(obj: { name: "Jane" info: "info" }) { name info } }""", - null, + 0, "setSampleRecordWithClassLevelValidationAttribute_no_errors" )] [InlineData("""mutation { setSampleRecordWithParameterValidationAttribute(obj: { name: "" info: "" }) { name info } }""", @@ -140,7 +143,7 @@ public async Task Get_Data_With_Filter_Sort_And_Or_Pagination_Definitions_Presen )] [InlineData( """mutation { setSampleRecordWithParameterValidationAttribute(obj: { name: "Jane" info: "info" }) { name info } }""", - null, + 0, "setSampleRecordWithParameterValidationAttribute_no_errors" )] [InlineData( @@ -161,6 +164,29 @@ public async Task Get_Data_With_Filter_Sort_And_Or_Pagination_Definitions_Presen 3, "setNestedParent_nested_validations" )] + [InlineData( + """ + mutation { + setNestedParent(obj: { + child: { count: 0 }, + children: [ + { count: 0 }, + { count: 1 }, + { count: 0 }, + { count: 1 }, + { count: 0 }, + { count: 1 }, + { count: 0 } + ] + }) { + child { count } + children { count } + } + } + """, + 6, + "setNestedParent_multiple_nested_validations" + )] [InlineData( """ mutation { @@ -233,7 +259,7 @@ public async Task Get_Data_With_Filter_Sort_And_Or_Pagination_Definitions_Presen 3, "setNestedParentExt_nested_validations_ext_generic" )] - public async Task Validation_Should_Return_Expected_Errors(string query, int? numberOfErrors, string description) + public async Task Validation_Should_Return_Expected_Errors(string query, int numberOfErrors, string description) { var result = await new ServiceCollection() diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_invalid_record.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_invalid_record.snap index 8d96156..579fccb 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_invalid_record.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_invalid_record.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field of type HotChocolate.Optional`1[System.String] must be a string, array or ICollection type for validation attribute MaxLengthAttribute.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "invalidRecord", "obj" @@ -20,5 +14,7 @@ } } ], - "data": null + "data": { + "invalidRecord": null + } } diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_invalid_record_ext.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_invalid_record_ext.snap index a54b449..a14a916 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_invalid_record_ext.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_invalid_record_ext.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field of type HotChocolate.Optional`1[System.String] must be a string, array or ICollection type for validation attribute MaxLengthAttribute.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "invalidRecordExt", "obj" @@ -20,5 +14,7 @@ } } ], - "data": null + "data": { + "invalidRecordExt": null + } } diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sampleNonNull_min_length_3.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sampleNonNull_min_length_3.snap index 78e3214..36d760a 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sampleNonNull_min_length_3.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sampleNonNull_min_length_3.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field Name must be a string with a minimum length of 3 and a maximum length of 100.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "sampleNonNull", "obj", @@ -21,5 +15,7 @@ } } ], - "data": null + "data": { + "sampleNonNull": null + } } diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sampleNonNull_required.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sampleNonNull_required.snap index a043ced..01f77e9 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sampleNonNull_required.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sampleNonNull_required.snap @@ -12,7 +12,7 @@ "type": "Query", "field": "sampleNonNull", "argument": "obj", - "specifiedBy": "https://spec.graphql.org/October2021/#sec-Required-Arguments" + "specifiedBy": "https://spec.graphql.org/September2025/#sec-Required-Arguments" } } ] diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_alias_blank_name_required.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_alias_blank_name_required.snap index f7f579b..a909d54 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_alias_blank_name_required.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_alias_blank_name_required.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The Name field is required.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "sampleAlias", "obj", diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_blank_name_required.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_blank_name_required.snap index 10a9b82..df482d3 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_blank_name_required.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_blank_name_required.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The Name field is required.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "sample", "obj", diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_empty-property-name_custom_validation.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_empty-property-name_custom_validation.snap index cff516a..0b695e1 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_empty-property-name_custom_validation.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_empty-property-name_custom_validation.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "empty-property-name", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "sample", "obj" diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_message-from-service_custom_validation.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_message-from-service_custom_validation.snap index 90a1d4f..da8c3da 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_message-from-service_custom_validation.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_message-from-service_custom_validation.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Splash!", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "sample", "obj" diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_min_length_3.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_min_length_3.snap index 9e3377f..afa0df0 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_min_length_3.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_min_length_3.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field Name must be a string with a minimum length of 3 and a maximum length of 100.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "sample", "obj", diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_multiple-property-names_custom_validation.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_multiple-property-names_custom_validation.snap index 6fb2dd8..33c9285 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_multiple-property-names_custom_validation.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_multiple-property-names_custom_validation.snap @@ -2,16 +2,9 @@ "errors": [ { "message": "multiple-property-names", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "sample", - "obj", - "hello" + "obj" ], "extensions": { "code": "DAMV-400", @@ -22,12 +15,6 @@ }, { "message": "multiple-property-names", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "sample", "obj" @@ -41,15 +28,10 @@ }, { "message": "multiple-property-names", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "sample", - "obj" + "obj", + "hello" ], "extensions": { "code": "DAMV-400", @@ -60,12 +42,6 @@ }, { "message": "multiple-property-names", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "sample", "obj", diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_no-property-name_custom_validation.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_no-property-name_custom_validation.snap index 8b3128e..bf97a6e 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_no-property-name_custom_validation.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_no-property-name_custom_validation.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "no-property-name", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "sample", "obj" diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_null-error-message_custom_validation.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_null-error-message_custom_validation.snap index 634e91e..5925a75 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_null-error-message_custom_validation.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_null-error-message_custom_validation.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unspecified Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "sample", "obj" diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_required.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_required.snap index 0af9395..dd08538 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_required.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_sample_required.snap @@ -13,7 +13,7 @@ ], "extensions": { "field": "name", - "specifiedBy": "https://spec.graphql.org/October2021/#sec-Input-Object-Required-Fields" + "specifiedBy": "https://spec.graphql.org/September2025/#sec-Input-Object-Required-Fields" } } ] diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setFunkyRecord_no_errors.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setFunkyRecord_no_errors.snap new file mode 100644 index 0000000..1a277a2 --- /dev/null +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setFunkyRecord_no_errors.snap @@ -0,0 +1,7 @@ +{ + "data": { + "setFunkyRecord": { + "text": "" + } + } +} diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_byName.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_byName.snap index e127c91..056b316 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_byName.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_byName.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParentExtByName", "obj", @@ -22,15 +16,11 @@ }, { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParentExtByName", "obj", + "children", + "_1_", "count" ], "extensions": { @@ -42,17 +32,9 @@ }, { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParentExtByName", "obj", - "children", - "_1_", "count" ], "extensions": { @@ -63,5 +45,7 @@ } } ], - "data": null + "data": { + "setNestedParentExtByName": null + } } diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_byOpType.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_byOpType.snap index a8914bd..8aaeaed 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_byOpType.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_byOpType.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParentExtByOpType", "obj", @@ -22,15 +16,11 @@ }, { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParentExtByOpType", "obj", + "children", + "_1_", "count" ], "extensions": { @@ -42,17 +32,9 @@ }, { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParentExtByOpType", "obj", - "children", - "_1_", "count" ], "extensions": { @@ -63,5 +45,7 @@ } } ], - "data": null + "data": { + "setNestedParentExtByOpType": null + } } diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_byType.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_byType.snap index b145a09..001ce45 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_byType.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_byType.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParentExtByType", "obj", @@ -22,15 +16,11 @@ }, { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParentExtByType", "obj", + "children", + "_1_", "count" ], "extensions": { @@ -42,17 +32,9 @@ }, { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParentExtByType", "obj", - "children", - "_1_", "count" ], "extensions": { @@ -63,5 +45,7 @@ } } ], - "data": null + "data": { + "setNestedParentExtByType": null + } } diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_generic.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_generic.snap index 3aa4d31..6d7b9ba 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_generic.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParentExt_nested_validations_ext_generic.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParentExtGeneric", "obj", @@ -22,15 +16,11 @@ }, { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParentExtGeneric", "obj", + "children", + "_1_", "count" ], "extensions": { @@ -42,17 +32,9 @@ }, { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParentExtGeneric", "obj", - "children", - "_1_", "count" ], "extensions": { @@ -63,5 +45,7 @@ } } ], - "data": null + "data": { + "setNestedParentExtGeneric": null + } } diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParent_multiple_nested_validations.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParent_multiple_nested_validations.snap new file mode 100644 index 0000000..91cf4c5 --- /dev/null +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParent_multiple_nested_validations.snap @@ -0,0 +1,99 @@ +{ + "errors": [ + { + "message": "The field Count must be between 1 and 10.", + "path": [ + "setNestedParent", + "obj", + "child" + ], + "extensions": { + "code": "DAMV-400", + "field": "setNestedParent", + "type": "Mutation", + "specifiedBy": "https://spec.graphql.org/June2018/#sec-Values-of-Correct-Type" + } + }, + { + "message": "The field Count must be between 1 and 10.", + "path": [ + "setNestedParent", + "obj", + "children", + "_0_", + "count" + ], + "extensions": { + "code": "DAMV-400", + "field": "setNestedParent", + "type": "Mutation", + "specifiedBy": "https://spec.graphql.org/June2018/#sec-Values-of-Correct-Type" + } + }, + { + "message": "The field Count must be between 1 and 10.", + "path": [ + "setNestedParent", + "obj", + "children", + "_2_", + "count" + ], + "extensions": { + "code": "DAMV-400", + "field": "setNestedParent", + "type": "Mutation", + "specifiedBy": "https://spec.graphql.org/June2018/#sec-Values-of-Correct-Type" + } + }, + { + "message": "The field Count must be between 1 and 10.", + "path": [ + "setNestedParent", + "obj", + "children", + "_4_", + "count" + ], + "extensions": { + "code": "DAMV-400", + "field": "setNestedParent", + "type": "Mutation", + "specifiedBy": "https://spec.graphql.org/June2018/#sec-Values-of-Correct-Type" + } + }, + { + "message": "The field Count must be between 1 and 10.", + "path": [ + "setNestedParent", + "obj", + "children", + "_6_", + "count" + ], + "extensions": { + "code": "DAMV-400", + "field": "setNestedParent", + "type": "Mutation", + "specifiedBy": "https://spec.graphql.org/June2018/#sec-Values-of-Correct-Type" + } + }, + { + "message": "The field Count must be between 1 and 10.", + "path": [ + "setNestedParent", + "obj", + "count" + ], + "extensions": { + "code": "DAMV-400", + "field": "setNestedParent", + "type": "Mutation", + "specifiedBy": "https://spec.graphql.org/June2018/#sec-Values-of-Correct-Type" + } + } + ], + "data": { + "setNestedParent": null + } +} diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParent_nested_validations.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParent_nested_validations.snap index 46802f2..6979d16 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParent_nested_validations.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNestedParent_nested_validations.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParent", "obj", @@ -22,15 +16,11 @@ }, { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParent", "obj", + "children", + "_1_", "count" ], "extensions": { @@ -42,17 +32,9 @@ }, { "message": "The field Count must be between 1 and 10.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "setNestedParent", "obj", - "children", - "_1_", "count" ], "extensions": { @@ -63,5 +45,7 @@ } } ], - "data": null + "data": { + "setNestedParent": null + } } diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNoValidationRecord_no_errors.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNoValidationRecord_no_errors.snap new file mode 100644 index 0000000..e0dffdc --- /dev/null +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setNoValidationRecord_no_errors.snap @@ -0,0 +1,7 @@ +{ + "data": { + "setNoValidationRecord": { + "text": "" + } + } +} diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithClassLevelValidationAttribute_invalid_name.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithClassLevelValidationAttribute_invalid_name.snap index 3b1355b..02aeeb0 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithClassLevelValidationAttribute_invalid_name.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithClassLevelValidationAttribute_invalid_name.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field Name must be a string with a minimum length of 3 and a maximum length of 100.", - "locations": [ - { - "line": 1, - "column": 12 - } - ], "path": [ "setSampleRecordWithClassLevelValidationAttribute", "obj", diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithClassLevelValidationAttribute_top_level_validation_error.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithClassLevelValidationAttribute_top_level_validation_error.snap index 6fd702d..11c06aa 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithClassLevelValidationAttribute_top_level_validation_error.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithClassLevelValidationAttribute_top_level_validation_error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "All string fields must be filled.", - "locations": [ - { - "line": 1, - "column": 12 - } - ], "path": [ "setSampleRecordWithClassLevelValidationAttribute", "obj" diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithParameterValidationAttribute_invalid_name.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithParameterValidationAttribute_invalid_name.snap index 5113b9c..d3dca08 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithParameterValidationAttribute_invalid_name.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithParameterValidationAttribute_invalid_name.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field Name must be a string with a minimum length of 3 and a maximum length of 100.", - "locations": [ - { - "line": 1, - "column": 12 - } - ], "path": [ "setSampleRecordWithParameterValidationAttribute", "obj", diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithParameterValidationAttribute_top_level_validation_error.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithParameterValidationAttribute_top_level_validation_error.snap index 83f93c3..0d901ae 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithParameterValidationAttribute_top_level_validation_error.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithParameterValidationAttribute_top_level_validation_error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "All string fields must be filled.", - "locations": [ - { - "line": 1, - "column": 12 - } - ], "path": [ "setSampleRecordWithParameterValidationAttribute", "obj" diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithSynthesizedProperty_blank_name_required.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithSynthesizedProperty_blank_name_required.snap index 7541789..548b0f4 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithSynthesizedProperty_blank_name_required.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecordWithSynthesizedProperty_blank_name_required.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The Name field is required.", - "locations": [ - { - "line": 1, - "column": 12 - } - ], "path": [ "setSampleRecordWithSynthesizedProperty", "obj", diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecord_blank_name_required.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecord_blank_name_required.snap index 6a392b6..3381a4c 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecord_blank_name_required.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSampleRecord_blank_name_required.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The Name field is required.", - "locations": [ - { - "line": 1, - "column": 12 - } - ], "path": [ "setSampleRecord", "obj", diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSample_blank_name_required.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSample_blank_name_required.snap index c56b5b8..94337e1 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSample_blank_name_required.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSample_blank_name_required.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The Name field is required.", - "locations": [ - { - "line": 1, - "column": 12 - } - ], "path": [ "setSample", "obj", diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSample_hc_null_error_blank_name_required.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSample_hc_null_error_blank_name_required.snap new file mode 100644 index 0000000..9ec70a5 --- /dev/null +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setSample_hc_null_error_blank_name_required.snap @@ -0,0 +1,28 @@ +{ + "errors": [ + { + "message": "Cannot return null for non-nullable field.", + "path": [ + "setSampleHcNullError" + ], + "extensions": { + "code": "HC0018" + } + }, + { + "message": "The Name field is required.", + "path": [ + "setSampleHcNullError", + "obj", + "name" + ], + "extensions": { + "code": "DAMV-400", + "field": "setSampleHcNullError", + "type": "Mutation", + "specifiedBy": "https://spec.graphql.org/June2018/#sec-Values-of-Correct-Type" + } + } + ], + "data": null +} diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setText_min_length_5.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setText_min_length_5.snap index 8cd9dc5..64f0e9c 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setText_min_length_5.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_setText_min_length_5.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field txt must be a string with a minimum length of 5 and a maximum length of 100.", - "locations": [ - { - "line": 1, - "column": 12 - } - ], "path": [ "setText", "txt" diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_text_alias_min_length_5.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_text_alias_min_length_5.snap index 46fd5ce..f491fa6 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_text_alias_min_length_5.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_text_alias_min_length_5.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field txt must be a string with a minimum length of 5 and a maximum length of 100.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "textAlias", "txt" diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_text_min_length_5.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_text_min_length_5.snap index d1f5040..be540e6 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_text_min_length_5.snap +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Validation_Should_Return_Expected_Errors_text_min_length_5.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The field txt must be a string with a minimum length of 5 and a maximum length of 100.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "text", "txt" diff --git a/DataAnnotatedModelValidations/DataAnnotatedModelValidations.csproj b/DataAnnotatedModelValidations/DataAnnotatedModelValidations.csproj index 337f949..cd951da 100644 --- a/DataAnnotatedModelValidations/DataAnnotatedModelValidations.csproj +++ b/DataAnnotatedModelValidations/DataAnnotatedModelValidations.csproj @@ -6,7 +6,7 @@ true true $(MSBuildProjectName) - 10.0.0 + 11.0.0-beta.1 Adamos Fiakkas Data Annotated Model Validation Middleware for HotChocolate Adamos Fiakkas @@ -22,7 +22,7 @@ - + diff --git a/DataAnnotatedModelValidations/Extensions/ReportingExtensions.cs b/DataAnnotatedModelValidations/Extensions/ReportingExtensions.cs index fdeba61..7d9333b 100644 --- a/DataAnnotatedModelValidations/Extensions/ReportingExtensions.cs +++ b/DataAnnotatedModelValidations/Extensions/ReportingExtensions.cs @@ -13,7 +13,7 @@ internal static IEnumerable ToTokenizedMemberNames(this string trimmedMe .Select(GetNormalizedMemberName); internal static IEnumerable ToComposedMemberNames( - this IInputField argument, + this Argument argument, string? memberName, bool? valueValidation ) => @@ -33,7 +33,7 @@ internal static Path ToArgumentPath(this IReadOnlyCollection contextPath internal static void ReportError( this IMiddlewareContext context, - IInputField argument, + Argument argument, IReadOnlyCollection contextPathList, bool? isValueValidation, string? message = default, diff --git a/DataAnnotatedModelValidations/Extensions/ValidationExtensions.cs b/DataAnnotatedModelValidations/Extensions/ValidationExtensions.cs index 7564d4e..90201e2 100644 --- a/DataAnnotatedModelValidations/Extensions/ValidationExtensions.cs +++ b/DataAnnotatedModelValidations/Extensions/ValidationExtensions.cs @@ -61,7 +61,7 @@ InvalidCastException ex ); private static (bool success, bool? isValueValidation) ValidateItem( - this IReadOnlyDictionary context, + this ArgumentValidationDefinition? argumentValidationDefinition, object item, string itemName, IServiceProvider serviceProvider, @@ -70,24 +70,22 @@ List validationResults { try { - _ = context.TryGetValue(Consts.ArgumentValidationContextKey, out var argumentValidationDefinition); - return argumentValidationDefinition switch { ArgumentValidationDefinition - { - UseObjectValidator: false, - ParameterAttributes: { Length: > 0 } attributes - } => + { + UseObjectValidator: false, + ParameterAttributes: { Length: > 0 } attributes + } => ( success: ValidateAsValue(item, itemName, serviceProvider, validationResults, attributes), isValueValidation: true ), ArgumentValidationDefinition - { - UseObjectValidator: true, - ParameterAttributes: { Length: > 0 } attributes - } => + { + UseObjectValidator: true, + ParameterAttributes: { Length: > 0 } attributes + } => ( success: ValidateAsValueAndObject(item, itemName, serviceProvider, validationResults, attributes), isValueValidation: false @@ -106,10 +104,13 @@ List validationResults } } - private static Action ValidateAndReport(IMiddlewareContext context) => + private static Action ValidateAndReport(IMiddlewareContext context) => argument => { - if (context.ArgumentValue(argument.Name) is not { } item) + if ( + context.ArgumentValue(argument.Name) is not { } item + || !argument.Features.TryGet(out var argumentValidationDefinition) + ) { return; } @@ -117,7 +118,7 @@ private static Action ValidateAndReport(IMiddlewareContext context) var validationResults = new List(); var (success, isValueValidation) = - argument.ContextData.ValidateItem( + argumentValidationDefinition.ValidateItem( item, argument.Name, context.Services, @@ -168,18 +169,16 @@ private static Action ValidateAndReport(IMiddlewareContext context) internal static void ValidateInputs(this IMiddlewareContext context) { if ( - !context.Selection.Field.ContextData.ContainsKey(Consts.FieldValidationContextKey) - || context.Selection.Field.Arguments is not { Count: > 0 } arguments + !context.Selection.Field.Arguments + .Any(arg => arg.Features.TryGet(out _)) ) { return; } - arguments + context.Selection.Field.Arguments .AsParallel() - .Where(argument => - argument?.ContextData.ContainsKey(Consts.ArgumentValidationContextKey) == true - ) + .Where(arg => arg.Features.TryGet(out _)) .ForAll(ValidateAndReport(context)); } } diff --git a/DataAnnotatedModelValidations/README.md b/DataAnnotatedModelValidations/README.md index eb82215..0cc48da 100644 --- a/DataAnnotatedModelValidations/README.md +++ b/DataAnnotatedModelValidations/README.md @@ -16,18 +16,19 @@ In addition, individual method arguments can be validated using annotations from | HotChocolate Version | DataAnnotatedModelValidations Version | .NET Version | | -------------------- | ------------------------------------- | ------------- | +| 16.0.0 or higher | 11.0.0 | .NET 8, 9, 10 | | 15.1.11 or higher | 10.0.0 | .NET 8, 9, 10 | -| 15.1.11 or higher | 9.0.0 | .NET 8, 9, 10 | -| 15.0.3 or higher | 8.1.2 | .NET 8, 9 | -| 15.0.3 or higher | 8.1.1 | .NET 8, 9 | -| 15.0.3 or higher | 8.1.0 | .NET 8, 9 | -| 15.0.3 or higher | 8.0.1 | .NET 8, 9 | -| 15.0.3 or higher | 8.0.0 | .NET 8, 9 | -| 15.0.3 or higher | 7.0.0 | .NET 8, 9 | -| 14.3.0 or higher | 6.3.0 | .NET 8, 9 | -| 14.2.0 or higher | 6.2.0 | .NET 8, 9 | -| 14.1.0 or higher | 6.1.0 | .NET 8, 9 | -| 14.0.0 or higher | 6.0.0 | .NET 8 | +| 15.1.11 or higher | 9.0.0 | .NET 8, 9, 10 | +| 15.0.3 or higher | 8.1.2 | .NET 8, 9 | +| 15.0.3 or higher | 8.1.1 | .NET 8, 9 | +| 15.0.3 or higher | 8.1.0 | .NET 8, 9 | +| 15.0.3 or higher | 8.0.1 | .NET 8, 9 | +| 15.0.3 or higher | 8.0.0 | .NET 8, 9 | +| 15.0.3 or higher | 7.0.0 | .NET 8, 9 | +| 14.3.0 or higher | 6.3.0 | .NET 8, 9 | +| 14.2.0 or higher | 6.2.0 | .NET 8, 9 | +| 14.1.0 or higher | 6.1.0 | .NET 8, 9 | +| 14.0.0 or higher | 6.0.0 | .NET 8 | ### Past Releases @@ -38,7 +39,7 @@ In addition, individual method arguments can be validated using annotations from | 12.16.0 or higher | 3.0.1 | .NET 7 | | 12.4.1 or higher | 2.1.4 | .NET 6 | -📝For more information please visit https://www.nuget.org/packages/DataAnnotatedModelValidations/#versions-body-tab +📝 For more information please visit https://www.nuget.org/packages/DataAnnotatedModelValidations/#versions-body-tab ## Usage @@ -201,13 +202,7 @@ The field name will be transformed in the error path as `fieldName,_index_`. "errors": [ { "message": "The field Count must be between 1 and 10.", - "path": [ - "sample", - "obj", - "children", - "_2_", - "count" - ], + "path": ["sample", "obj", "children", "_2_", "count"], "extensions": { "code": "DAMV-400", "field": "sample", @@ -227,12 +222,7 @@ represented as one entry, `fieldName_index_`. "errors": [ { "message": "The field Count must be between 1 and 10.", - "path": [ - "sample", - "obj", - "children_2_", - "count" - ], + "path": ["sample", "obj", "children_2_", "count"], "extensions": { "code": "DAMV-400", "field": "sample", @@ -255,11 +245,7 @@ Ex. validation error '"Some validation error!"' was assigned to properties hello "errors": [ { "message": "Some validation error!", - "path": [ - "sample", - "obj", - "hello" - ], + "path": ["sample", "obj", "hello"], "extensions": { "code": "DAMV-400", "field": "sample", @@ -269,11 +255,7 @@ Ex. validation error '"Some validation error!"' was assigned to properties hello }, { "message": "Some validation error!", - "path": [ - "sample", - "obj", - "world" - ], + "path": ["sample", "obj", "world"], "extensions": { "code": "DAMV-400", "field": "sample", diff --git a/DataAnnotatedModelValidations/TypeInterceptors/ValidatorTypeInterceptor.cs b/DataAnnotatedModelValidations/TypeInterceptors/ValidatorTypeInterceptor.cs index dc4db77..604aaeb 100644 --- a/DataAnnotatedModelValidations/TypeInterceptors/ValidatorTypeInterceptor.cs +++ b/DataAnnotatedModelValidations/TypeInterceptors/ValidatorTypeInterceptor.cs @@ -1,5 +1,6 @@ using DataAnnotatedModelValidations.Middleware; using DataAnnotatedModelValidations.Models; +using HotChocolate.Language; using System.Reflection; namespace DataAnnotatedModelValidations.TypeInterceptors; @@ -11,15 +12,15 @@ public sealed class ValidatorTypeInterceptor : TypeInterceptor private FieldMiddleware ValidatorMiddleware => _validatorMiddleware ??= FieldClassMiddlewareFactory.Create(); - private static IBindableList? ObjectTypeDefinitionFields(DefinitionBase? definition) => - definition switch + private static IBindableList? ObjectTypeDefinitionFields(TypeSystemConfiguration? configuration) => + configuration switch { - ObjectTypeDefinition { Fields.Count: > 0 } objectTypeDefinition - when IsRootOperationType(objectTypeDefinition) => objectTypeDefinition.Fields, + ObjectTypeConfiguration { Fields.Count: > 0 } objectTypeConfiguration + when IsRootOperationType(objectTypeConfiguration) => objectTypeConfiguration.Fields, _ => default }; - - private static bool IsRootOperationType(ObjectTypeDefinition objectTypeDefinition) => + + private static bool IsRootOperationType(ObjectTypeConfiguration objectTypeDefinition) => IsRootOperationTypeName(objectTypeDefinition.ExtendsType?.Name ?? objectTypeDefinition.Name); private static bool IsRootOperationTypeName(string? name) => @@ -48,9 +49,9 @@ private static bool ShouldUseObjectValidator(ParameterInfo parameter) => .Any(property => property.GetCustomAttributes(Consts.ValidationAttributeType, true).Length > 0) ); - public override void OnAfterInitialize(ITypeDiscoveryContext discoveryContext, DefinitionBase definition) + public override void OnAfterInitialize(ITypeDiscoveryContext discoveryContext, TypeSystemConfiguration configuration) { - if (ObjectTypeDefinitionFields(definition) is not { } fields) + if (ObjectTypeDefinitionFields(configuration) is not { } fields) { return; } @@ -75,20 +76,23 @@ public override void OnAfterInitialize(ITypeDiscoveryContext discoveryContext, D if (customParameterAttributes.Length > 0 || shouldUseObjectValidator) { - argument.ContextData[Consts.ArgumentValidationContextKey] = + argument.Features.Set( new ArgumentValidationDefinition( shouldUseObjectValidator, customParameterAttributes - ); + ) + ); isValidatable = true; } } if (isValidatable) { - field.ContextData[Consts.FieldValidationContextKey] = true; // add as first middleware to short circuit the pipeline - field.MiddlewareDefinitions.Insert(0, new FieldMiddlewareDefinition(ValidatorMiddleware)); + field.MiddlewareConfigurations.Insert( + 0, + new FieldMiddlewareConfiguration(ValidatorMiddleware) + ); } } } diff --git a/DataAnnotatedModelValidations/Usings.cs b/DataAnnotatedModelValidations/Usings.cs index 7162fca..e0e4419 100644 --- a/DataAnnotatedModelValidations/Usings.cs +++ b/DataAnnotatedModelValidations/Usings.cs @@ -1,7 +1,7 @@ global using HotChocolate.Configuration; global using HotChocolate.Execution.Configuration; global using HotChocolate.Resolvers; -global using HotChocolate.Types.Descriptors.Definitions; +global using HotChocolate.Types.Descriptors.Configurations; global using HotChocolate.Types; global using HotChocolate; global using Microsoft.Extensions.DependencyInjection; diff --git a/README.md b/README.md index 9879e3f..aaa965f 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ In addition, individual method arguments can be validated using annotations from | HotChocolate Version | DataAnnotatedModelValidations Version | .NET Version | | -------------------- | ------------------------------------- | ------------- | +| 16.0.0 or higher | 11.0.0 | .NET 8, 9, 10 | | 15.1.11 or higher | 10.0.0 | .NET 8, 9, 10 | | 15.0.11 or higher | 9.0.0 | .NET 8, 9, 10 | | 15.0.3 or higher | 8.1.2 | .NET 8, 9 | @@ -42,7 +43,7 @@ In addition, individual method arguments can be validated using annotations from | 12.16.0 or higher | 3.0.1 | .NET 7 | | 12.4.1 or higher | 2.1.4 | .NET 6 | -📝For more information please visit https://www.nuget.org/packages/DataAnnotatedModelValidations/#versions-body-tab +📝 For more information please visit https://www.nuget.org/packages/DataAnnotatedModelValidations/#versions-body-tab ## Usage @@ -205,13 +206,7 @@ The field name will be transformed in the error path as `fieldName,_index_`. "errors": [ { "message": "The field Count must be between 1 and 10.", - "path": [ - "sample", - "obj", - "children", - "_2_", - "count" - ], + "path": ["sample", "obj", "children", "_2_", "count"], "extensions": { "code": "DAMV-400", "field": "sample", @@ -231,12 +226,7 @@ represented as one entry, `fieldName_index_`. "errors": [ { "message": "The field Count must be between 1 and 10.", - "path": [ - "sample", - "obj", - "children_2_", - "count" - ], + "path": ["sample", "obj", "children_2_", "count"], "extensions": { "code": "DAMV-400", "field": "sample", @@ -259,11 +249,7 @@ Ex. validation error '"Some validation error!"' was assigned to properties hello "errors": [ { "message": "Some validation error!", - "path": [ - "sample", - "obj", - "hello" - ], + "path": ["sample", "obj", "hello"], "extensions": { "code": "DAMV-400", "field": "sample", @@ -273,11 +259,7 @@ Ex. validation error '"Some validation error!"' was assigned to properties hello }, { "message": "Some validation error!", - "path": [ - "sample", - "obj", - "world" - ], + "path": ["sample", "obj", "world"], "extensions": { "code": "DAMV-400", "field": "sample", From 4171697ac6b2f0443ebb4c209cb2a9e6ef5e0e13 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 10 May 2026 10:17:56 +0300 Subject: [PATCH 2/3] Continuation --- .editorconfig | 438 +++++++++++------- .../PipelineExecutionTests.GraphQL.cs | 1 + .../Pipeline/PipelineExecutionTests.cs | 39 +- ...eExecutionTests.Schema_Should_Resolve.snap | 316 +++++++++++++ .../DataAnnotatedModelValidations.csproj | 2 +- .../Extensions/StringExtensions.cs | 73 +-- .../ValidatorTypeInterceptor.cs | 5 +- 7 files changed, 654 insertions(+), 220 deletions(-) create mode 100644 DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Schema_Should_Resolve.snap diff --git a/.editorconfig b/.editorconfig index 322d8cd..af3d700 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,154 +1,153 @@ -# Remove the line below if you want to inherit .editorconfig settings from higher directories -# @see: https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options root = true +# All files [*] -# New line preferences -end_of_line = crlf +indent_style = space -[*.{json,graphql,css}] +# Xml files +[*.xml] +indent_size = 2 + +# Xml project files +[*.{csproj,fsproj,vbproj,proj,slnx}] +indent_size = 2 + +# Xml config files +[*.{props,targets,config,nuspec}] indent_size = 2 -indent_style = space -tab_width = 2 -# C# / Razor files +[*.json] +indent_size = 2 + +# C# files [*.cs] #### Core EditorConfig Options #### # Indentation and spacing indent_size = 4 -indent_style = space tab_width = 4 # New line preferences -insert_final_newline = true +insert_final_newline = false #### .NET Coding Conventions #### +[*.{cs,vb}] # Organize usings -dotnet_separate_import_directive_groups = false -dotnet_sort_system_directives_first = false +dotnet_separate_import_directive_groups = true +dotnet_sort_system_directives_first = true file_header_template = unset # this. and Me. preferences -dotnet_style_qualification_for_event = false -dotnet_style_qualification_for_field = false -dotnet_style_qualification_for_method = false -dotnet_style_qualification_for_property = false +dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_property = false:silent # Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true -dotnet_style_predefined_type_for_member_access = true +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent # Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity -dotnet_style_parentheses_in_other_operators = never_if_unnecessary -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent # Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent # Expression-level preferences -dotnet_style_coalesce_expression = true -dotnet_style_collection_initializer = true -dotnet_style_explicit_tuple_names = true -dotnet_style_namespace_match_folder = true -dotnet_style_null_propagation = true -dotnet_style_object_initializer = true +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion dotnet_style_operator_placement_when_wrapping = beginning_of_line -dotnet_style_prefer_auto_properties = true -dotnet_style_prefer_collection_expression = when_types_loosely_match -dotnet_style_prefer_compound_assignment = true -dotnet_style_prefer_conditional_expression_over_assignment = true -dotnet_style_prefer_conditional_expression_over_return = true -dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed -dotnet_style_prefer_inferred_anonymous_type_member_names = true -dotnet_style_prefer_inferred_tuple_names = true -dotnet_style_prefer_is_null_check_over_reference_equality_method = true -dotnet_style_prefer_simplified_boolean_expressions = true -dotnet_style_prefer_simplified_interpolation = true +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion # Field preferences -dotnet_style_readonly_field = true +dotnet_style_readonly_field = true:warning # Parameter preferences -dotnet_code_quality_unused_parameters = all:silent +dotnet_code_quality_unused_parameters = all:suggestion # Suppression preferences dotnet_remove_unnecessary_suppression_exclusions = none -# New line preferences -dotnet_style_allow_multiple_blank_lines_experimental = true -dotnet_style_allow_statement_immediately_after_block_experimental = true - #### C# Coding Conventions #### +[*.cs] # var preferences -csharp_style_var_elsewhere = true -csharp_style_var_for_built_in_types = true -csharp_style_var_when_type_is_apparent = true +csharp_style_var_elsewhere = false:silent +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent # Expression-bodied members -csharp_style_expression_bodied_accessors = true -csharp_style_expression_bodied_constructors = false -csharp_style_expression_bodied_indexers = true -csharp_style_expression_bodied_lambdas = true -csharp_style_expression_bodied_local_functions = true -csharp_style_expression_bodied_methods = true -csharp_style_expression_bodied_operators = true -csharp_style_expression_bodied_properties = true +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:suggestion +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent # Pattern matching preferences -csharp_style_pattern_matching_over_as_with_null_check = true -csharp_style_pattern_matching_over_is_with_cast_check = true -csharp_style_prefer_extended_property_pattern = true -csharp_style_prefer_not_pattern = true -csharp_style_prefer_pattern_matching = true -csharp_style_prefer_switch_expression = true +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_switch_expression = true:suggestion # Null-checking preferences -csharp_style_conditional_delegate_call = true +csharp_style_conditional_delegate_call = true:suggestion # Modifier preferences -csharp_prefer_static_local_function = true -csharp_preferred_modifier_order = public, private, protected, internal, file, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, required, volatile, async -csharp_style_prefer_readonly_struct = true -csharp_style_prefer_readonly_struct_member = true +csharp_prefer_static_anonymous_function = true:suggestion +csharp_prefer_static_local_function = true:warning +csharp_preferred_modifier_order = public,private,protected,internal,file,const,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion # Code-block preferences -csharp_prefer_braces = when_multiline -csharp_prefer_simple_using_statement = true -csharp_style_namespace_declarations = file_scoped -csharp_style_prefer_method_group_conversion = true -csharp_style_prefer_primary_constructors = true -csharp_style_prefer_top_level_statements = true +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_top_level_statements = true:silent # Expression-level preferences -csharp_prefer_simple_default_expression = true -csharp_style_deconstructed_variable_declaration = true -csharp_style_implicit_object_creation_when_type_is_apparent = true -csharp_style_inlined_variable_declaration = true -csharp_style_prefer_index_operator = true -csharp_style_prefer_local_over_anonymous_function = true -csharp_style_prefer_null_check_over_type_check = true -csharp_style_prefer_range_operator = true -csharp_style_prefer_tuple_swap = true -csharp_style_prefer_utf8_string_literals = true -csharp_style_throw_expression = true -csharp_style_unused_value_assignment_preference = discard_variable -csharp_style_unused_value_expression_statement_preference = discard_variable +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent # 'using' directive preferences -csharp_using_directive_placement = outside_namespace - -# New line preferences -csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true -csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true -csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true -csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true -csharp_style_allow_embedded_statements_on_same_line_experimental = true +csharp_using_directive_placement = outside_namespace:silent #### C# Formatting Rules #### @@ -198,110 +197,193 @@ csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true #### Naming styles #### +[*.{cs,vb}] # Naming rules -dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion -dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface -dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i +dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces +dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion +dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces +dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase + +dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion +dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters +dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase + +dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods +dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties +dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.events_should_be_pascalcase.symbols = events +dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion +dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables +dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase + +dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion +dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants +dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase + +dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion +dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters +dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase + +dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields +dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion +dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields +dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase -dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.types_should_be_pascal_case.symbols = types -dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion +dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields +dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase -dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members -dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase -## Define the 'private_fields_should_be_underscored' naming rule -dotnet_naming_rule.private_fields_should_be_underscored.severity = suggestion -dotnet_naming_rule.private_fields_should_be_underscored.symbols = private_fields -dotnet_naming_rule.private_fields_should_be_underscored.style = underscored +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase -## Define the 'private_static_fields_should_be_underscored' naming rule -dotnet_naming_rule.private_static_fields_should_be_underscored.severity = suggestion -dotnet_naming_rule.private_static_fields_should_be_underscored.symbols = private_static_fields -dotnet_naming_rule.private_static_fields_should_be_underscored.style = underscored +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase -## Define the 'private_constants_should_be_underscored' naming rule -dotnet_naming_rule.private_constants_should_be_underscored.severity = suggestion -dotnet_naming_rule.private_constants_should_be_underscored.symbols = private_constants -dotnet_naming_rule.private_constants_should_be_underscored.style = underscored +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase -## Define the 'public_constants_should_be_pascal_case' naming rule -dotnet_naming_rule.public_constants_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.public_constants_should_be_pascal_case.symbols = public_constants -dotnet_naming_rule.public_constants_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums +dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions +dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase # Symbol specifications -dotnet_naming_symbols.interface.applicable_kinds = interface -dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = +dotnet_naming_symbols.interfaces.applicable_kinds = interface +dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interfaces.required_modifiers = -dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum -dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = +dotnet_naming_symbols.enums.applicable_kinds = enum +dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.enums.required_modifiers = -dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method -dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.events.applicable_kinds = event +dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.events.required_modifiers = + +dotnet_naming_symbols.methods.applicable_kinds = method +dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.methods.required_modifiers = + +dotnet_naming_symbols.properties.applicable_kinds = property +dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.properties.required_modifiers = + +dotnet_naming_symbols.public_fields.applicable_kinds = field +dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_fields.required_modifiers = -## Define the 'private_fields' symbol group dotnet_naming_symbols.private_fields.applicable_kinds = field -dotnet_naming_symbols.private_fields.applicable_accessibilities = private +dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_fields.required_modifiers = -## Define the 'private_static_fields' symbol group dotnet_naming_symbols.private_static_fields.applicable_kinds = field -dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private +dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected dotnet_naming_symbols.private_static_fields.required_modifiers = static -## Define the 'private_constants' symbol group -dotnet_naming_symbols.private_constants.applicable_kinds = field -dotnet_naming_symbols.private_constants.applicable_accessibilities = private -dotnet_naming_symbols.private_constants.required_modifiers = const +dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum +dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types_and_namespaces.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +dotnet_naming_symbols.type_parameters.applicable_kinds = namespace +dotnet_naming_symbols.type_parameters.applicable_accessibilities = * +dotnet_naming_symbols.type_parameters.required_modifiers = + +dotnet_naming_symbols.private_constant_fields.applicable_kinds = field +dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_constant_fields.required_modifiers = const -## Define the 'public_constants' symbol group -dotnet_naming_symbols.public_constants.applicable_kinds = field -dotnet_naming_symbols.public_constants.applicable_accessibilities = public -dotnet_naming_symbols.public_constants.required_modifiers = const +dotnet_naming_symbols.local_variables.applicable_kinds = local +dotnet_naming_symbols.local_variables.applicable_accessibilities = local +dotnet_naming_symbols.local_variables.required_modifiers = + +dotnet_naming_symbols.local_constants.applicable_kinds = local +dotnet_naming_symbols.local_constants.applicable_accessibilities = local +dotnet_naming_symbols.local_constants.required_modifiers = const + +dotnet_naming_symbols.parameters.applicable_kinds = parameter +dotnet_naming_symbols.parameters.applicable_accessibilities = * +dotnet_naming_symbols.parameters.required_modifiers = + +dotnet_naming_symbols.public_constant_fields.applicable_kinds = field +dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_constant_fields.required_modifiers = const + +dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static + +dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static + +dotnet_naming_symbols.local_functions.applicable_kinds = local_function +dotnet_naming_symbols.local_functions.applicable_accessibilities = * +dotnet_naming_symbols.local_functions.required_modifiers = # Naming styles -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - -dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = -dotnet_naming_style.begins_with_i.capitalization = pascal_case - -## Define the 'underscored' naming style -dotnet_naming_style.underscored.capitalization = camel_case -dotnet_naming_style.underscored.required_prefix = _ - -# ReSharper -resharper_arrange_object_creation_when_type_not_evident_highlighting = none -resharper_redundant_argument_default_value_highlighting = none -resharper_csharp_braces_for_dowhile = required_for_multiline_statement -resharper_csharp_braces_for_fixed = required_for_multiline_statement -resharper_csharp_braces_for_for = required_for_multiline_statement -resharper_csharp_braces_for_foreach = required_for_multiline_statement -resharper_csharp_braces_for_lock = required_for_multiline_statement -resharper_csharp_braces_for_using = required_for_multiline_statement -resharper_csharp_braces_for_while = required_for_multiline_statement -resharper_csharp_braces_for_ifelse = required_for_multiline_statement -resharper_csharp_empty_block_style = together -resharper_csharp_max_array_initializer_elements_on_line = 1 -resharper_csharp_place_attribute_on_same_line = false -resharper_csharp_place_field_attribute_on_same_line = false -resharper_csharp_wrap_object_and_collection_initializer_style = chop_always -resharper_csharp_wrap_primary_constructor_parameters_style = chop_if_long -resharper_csharp_wrap_arguments_style = chop_if_long -resharper_csharp_wrap_chained_method_calls = chop_if_long -resharper_csharp_wrap_before_first_method_call = true -resharper_csharp_wrap_after_property_in_chained_method_calls = true -resharper_csharp_indent_raw_literal_string = do_not_change -resharper_csharp_max_line_length = 200 \ No newline at end of file +dotnet_naming_style.pascalcase.required_prefix = +dotnet_naming_style.pascalcase.required_suffix = +dotnet_naming_style.pascalcase.word_separator = +dotnet_naming_style.pascalcase.capitalization = pascal_case + +dotnet_naming_style.ipascalcase.required_prefix = I +dotnet_naming_style.ipascalcase.required_suffix = +dotnet_naming_style.ipascalcase.word_separator = +dotnet_naming_style.ipascalcase.capitalization = pascal_case + +dotnet_naming_style.tpascalcase.required_prefix = T +dotnet_naming_style.tpascalcase.required_suffix = +dotnet_naming_style.tpascalcase.word_separator = +dotnet_naming_style.tpascalcase.capitalization = pascal_case + +dotnet_naming_style._camelcase.required_prefix = _ +dotnet_naming_style._camelcase.required_suffix = +dotnet_naming_style._camelcase.word_separator = +dotnet_naming_style._camelcase.capitalization = camel_case + +dotnet_naming_style.camelcase.required_prefix = +dotnet_naming_style.camelcase.required_suffix = +dotnet_naming_style.camelcase.word_separator = +dotnet_naming_style.camelcase.capitalization = camel_case + +dotnet_naming_style.s_camelcase.required_prefix = s_ +dotnet_naming_style.s_camelcase.required_suffix = +dotnet_naming_style.s_camelcase.word_separator = +dotnet_naming_style.s_camelcase.capitalization = camel_case + diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.GraphQL.cs b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.GraphQL.cs index 1411ad6..0e8a864 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.GraphQL.cs +++ b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.GraphQL.cs @@ -1,4 +1,5 @@ using DataAnnotatedModelValidations.Attributes; + using HotChocolate.Language; namespace DataAnnotatedModelValidations.Tests.Pipeline; diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.cs b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.cs index 3ee17b6..fe607d9 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.cs +++ b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.cs @@ -2,6 +2,42 @@ public partial class PipelineExecutionTests { + [Fact] + public async Task Schema_Should_Resolve() + { + var requestExecutor = + await new ServiceCollection() + .AddSingleton() + .AddGraphQLServer() + .ModifyPagingOptions(options => + { + options.RequirePagingBoundaries = false; + options.DefaultPageSize = 10; + options.MaxPageSize = 100; + }) + .ModifyCostOptions(options => options.EnforceCostLimits = false) + .AddDataAnnotationsValidator() + .AddQueryType() + .AddTypeExtension() + .AddMutationType() + .AddTypeExtension() + .AddTypeExtension() + .AddTypeExtension() + .AddTypeExtension() + .AddTypeExtension() + .AddSorting() + .AddFiltering() + .AddQueryContext() + .BuildRequestExecutorAsync(); + + var result = requestExecutor.Schema.ToString(); + + Assert.NotNull(result); + Assert.Contains("type Query", result); + Assert.Contains("type Mutation", result); + result.MatchSnapshot(); + } + [Theory] [InlineData( """ @@ -275,8 +311,7 @@ public async Task Validation_Should_Return_Expected_Errors(string query, int num .AddTypeExtension() .AddSorting() .AddFiltering() - .ExecuteRequestAsync(query) - .ConfigureAwait(true); + .ExecuteRequestAsync(query); Assert.Equal(numberOfErrors, result.ExpectOperationResult().Errors?.Count); result.ExpectOperationResult().ToJson().MatchSnapshot(new SnapshotNameExtension($"{description}.snap")); diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Schema_Should_Resolve.snap b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Schema_Should_Resolve.snap new file mode 100644 index 0000000..7cbe939 --- /dev/null +++ b/DataAnnotatedModelValidations.Tests/Pipeline/__snapshots__/PipelineExecutionTests.Schema_Should_Resolve.snap @@ -0,0 +1,316 @@ +schema { + query: Query + mutation: Mutation +} + +type Query { + message: String! + sampleResponses( + skip: Int + take: Int + percentage: Float! + order: [SampleResponseSortInput!] @cost(weight: "10") + where: SampleResponseFilterInput @cost(weight: "10") + ): SampleResponsesCollectionSegment + @listSize( + assumedSize: 100 + slicingArguments: ["take"] + slicingArgumentDefaultValue: 10 + sizedFields: ["items"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + text(txt: String): String + textIgnoreValidation(txt: String): String + sample(obj: SampleInput): Sample + sampleNonNull(obj: SampleInput!): Sample + sampleIgnoreValidation(obj: SampleInput): Sample + sampleWithService(obj: SampleInput): Sample + invalidRecord(obj: InvalidRecordInput!): InvalidRecord + info: String! + samples( + skip: Int + take: Int + order: [SampleSortInput!] @cost(weight: "10") + where: SampleFilterInput @cost(weight: "10") + ): SamplesCollectionSegment + @listSize( + assumedSize: 100 + slicingArguments: ["take"] + slicingArgumentDefaultValue: 10 + sizedFields: ["items"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + invalidRecordExt(obj: InvalidRecordInput!): InvalidRecord +} + +type Mutation { + setText(txt: String): String + setSample(obj: SampleInput): Sample + setSampleHcNullError(obj: SampleInput!): Sample! + setSampleRecord(obj: SampleRecordInput): SampleRecord + setFunkyRecord(obj: FunkyRecordInput!): FunkyRecord + setNoValidationRecord(obj: NoValidationRecordInput!): NoValidationRecord + setSampleRecordWithSynthesizedProperty( + obj: SampleRecordWithSynthesizedPropertyInput + ): SampleRecordWithSynthesizedProperty + setSampleRecordWithClassLevelValidationAttribute( + obj: SampleRecordWithClassLevelValidationAttributeInput + ): SampleRecordWithClassLevelValidationAttribute + setSampleRecordWithParameterValidationAttribute( + obj: SampleRecordWithParameterValidationAttributeInput + ): SampleRecordWithParameterValidationAttribute + setNestedParent(obj: NestedParentInput!): NestedParent + setNestedParentExtByName(obj: NestedParentInput!): NestedParent + setNestedParentExtByOpType(obj: NestedParentInput!): NestedParent + setNestedParentExtByType(obj: NestedParentInput!): NestedParent + setNestedParentExtGeneric(obj: NestedParentInput!): NestedParent +} + +"Information about the offset pagination." +type CollectionSegmentInfo { + "Indicates whether more items exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more items exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! +} + +type FunkyRecord { + text: String! +} + +type InvalidRecord { + text: String! +} + +type NestedChild { + count: Int! +} + +type NestedParent { + child: NestedChild! + children: [NestedChild!]! +} + +type NoValidationRecord { + text: String! +} + +"A segment of a collection." +type RelatedpCollectionSegment { + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo! + "A flattened list of the items." + items: [Sample!] +} + +"A segment of a collection." +type RelatedpsfCollectionSegment { + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo! + "A flattened list of the items." + items: [Sample!] +} + +type Sample { + name: String! + relatedpsf( + skip: Int + take: Int + order: [SampleSortInput!] @cost(weight: "10") + where: SampleFilterInput @cost(weight: "10") + ): RelatedpsfCollectionSegment + @listSize( + assumedSize: 100 + slicingArguments: ["take"] + slicingArgumentDefaultValue: 10 + sizedFields: ["items"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + relatedsf( + order: [SampleSortInput!] @cost(weight: "10") + where: SampleFilterInput @cost(weight: "10") + ): [Sample!]! @cost(weight: "10") + relatedp(skip: Int, take: Int, where: SampleFilterInput @cost(weight: "10")): RelatedpCollectionSegment + @listSize( + assumedSize: 100 + slicingArguments: ["take"] + slicingArgumentDefaultValue: 10 + sizedFields: ["items"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") +} + +type SampleRecord { + name: String! +} + +type SampleRecordWithClassLevelValidationAttribute { + name: String + info: String +} + +type SampleRecordWithParameterValidationAttribute { + name: String! + info: String! +} + +type SampleRecordWithSynthesizedProperty { + name: String! +} + +type SampleResponse { + name: String! + info: String + numberOfPets: Int! + age: Int +} + +"A segment of a collection." +type SampleResponsesCollectionSegment { + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo! + "A flattened list of the items." + items: [SampleResponse!] +} + +"A segment of a collection." +type SamplesCollectionSegment { + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo! + "A flattened list of the items." + items: [Sample!] +} + +input FunkyRecordInput { + text: String! +} + +input IntOperationFilterInput { + eq: Int @cost(weight: "10") + neq: Int @cost(weight: "10") + in: [Int] @cost(weight: "10") + nin: [Int] @cost(weight: "10") + gt: Int @cost(weight: "10") + ngt: Int @cost(weight: "10") + gte: Int @cost(weight: "10") + ngte: Int @cost(weight: "10") + lt: Int @cost(weight: "10") + nlt: Int @cost(weight: "10") + lte: Int @cost(weight: "10") + nlte: Int @cost(weight: "10") +} + +input InvalidRecordInput { + text: String! +} + +input NestedChildInput { + count: Int! +} + +input NestedParentInput { + child: NestedChildInput! + children: [NestedChildInput!]! +} + +input NoValidationRecordInput { + text: String! +} + +input SampleFilterInput { + and: [SampleFilterInput!] + or: [SampleFilterInput!] + name: StringOperationFilterInput +} + +input SampleInput { + name: String! +} + +input SampleRecordInput { + name: String! +} + +input SampleRecordWithClassLevelValidationAttributeInput { + name: String + info: String +} + +input SampleRecordWithParameterValidationAttributeInput { + name: String! + info: String! +} + +input SampleRecordWithSynthesizedPropertyInput { + name: String! +} + +input SampleResponseFilterInput { + and: [SampleResponseFilterInput!] + or: [SampleResponseFilterInput!] + name: StringOperationFilterInput + info: StringOperationFilterInput + numberOfPets: IntOperationFilterInput + age: IntOperationFilterInput +} + +input SampleResponseSortInput { + name: SortEnumType @cost(weight: "10") + info: SortEnumType @cost(weight: "10") + numberOfPets: SortEnumType @cost(weight: "10") + age: SortEnumType @cost(weight: "10") +} + +input SampleSortInput { + name: SortEnumType @cost(weight: "10") +} + +input StringOperationFilterInput { + and: [StringOperationFilterInput!] + or: [StringOperationFilterInput!] + eq: String @cost(weight: "10") + neq: String @cost(weight: "10") + contains: String @cost(weight: "20") + ncontains: String @cost(weight: "20") + in: [String] @cost(weight: "10") + nin: [String] @cost(weight: "10") + startsWith: String @cost(weight: "20") + nstartsWith: String @cost(weight: "20") + endsWith: String @cost(weight: "20") + nendsWith: String @cost(weight: "20") +} + +enum SortEnumType { + ASC + DESC +} + +"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." +directive @cost( + "The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." + weight: String! +) on + | SCALAR + | OBJECT + | FIELD_DEFINITION + | ARGUMENT_DEFINITION + | ENUM + | INPUT_FIELD_DEFINITION + +"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." +directive @listSize( + "The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." + assumedSize: Int + "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." + slicingArguments: [String!] + "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." + slicingArgumentDefaultValue: Int + "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." + sizedFields: [String!] + "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." + requireOneSlicingArgument: Boolean = true +) on FIELD_DEFINITION diff --git a/DataAnnotatedModelValidations/DataAnnotatedModelValidations.csproj b/DataAnnotatedModelValidations/DataAnnotatedModelValidations.csproj index cd951da..ccbda24 100644 --- a/DataAnnotatedModelValidations/DataAnnotatedModelValidations.csproj +++ b/DataAnnotatedModelValidations/DataAnnotatedModelValidations.csproj @@ -6,7 +6,7 @@ true true $(MSBuildProjectName) - 11.0.0-beta.1 + 11.0.0 Adamos Fiakkas Data Annotated Model Validation Middleware for HotChocolate Adamos Fiakkas diff --git a/DataAnnotatedModelValidations/Extensions/StringExtensions.cs b/DataAnnotatedModelValidations/Extensions/StringExtensions.cs index 2d2c7d4..eb2e110 100644 --- a/DataAnnotatedModelValidations/Extensions/StringExtensions.cs +++ b/DataAnnotatedModelValidations/Extensions/StringExtensions.cs @@ -8,45 +8,46 @@ internal static string Camelize(this string memberName) { ArgumentException.ThrowIfNullOrEmpty(memberName); - // quick exit - if (char.IsLower(memberName[0])) + return char.IsLower(memberName[0]) switch { - return memberName; - } + // quick exit + true => memberName, + _ => string.Create( + memberName.Length, + memberName, + CreateCamelizedStringAction + ) + }; + } - return string.Create( - length: memberName.Length, - state: memberName, - (output, fieldName) => - { - int p = 0; - for (; p < fieldName.Length && char.IsLetter(fieldName[p]) && char.IsUpper(fieldName[p]); p++) - { - output[p] = char.ToLowerInvariant(fieldName[p]); - } + private static void CreateCamelizedStringAction(Span output, string fieldName) + { + var p = 0; + for (; p < fieldName.Length && char.IsLetter(fieldName[p]) && char.IsUpper(fieldName[p]); p++) + { + output[p] = char.ToLowerInvariant(fieldName[p]); + } - // in case more than one character is upper case, we uppercase - // the current character. We only uppercase the character - // back if the last character is a letter - // - // before after result - // FOOBar FOOBar = fooBar - // ^ ^ - // FOO1Ar FOO1Ar = foo1Ar - // ^ ^ - // FOO_Ar FOO_Ar = foo_Ar - // ^ ^ - if (p < fieldName.Length && p > 1 && char.IsLetter(fieldName[p])) - { - output[p - 1] = char.ToUpperInvariant(output[p - 1]); - } + // in case more than one character is upper case, we uppercase + // the current character. We only uppercase the character + // back if the last character is a letter + // + // before after result + // FOOBar FOOBar = fooBar + // ^ ^ + // FOO1Ar FOO1Ar = foo1Ar + // ^ ^ + // FOO_Ar FOO_Ar = foo_Ar + // ^ ^ + if (p < fieldName.Length && p > 1 && char.IsLetter(fieldName[p])) + { + output[p - 1] = char.ToUpperInvariant(output[p - 1]); + } - // Copy the rest unchanged - for (; p < fieldName.Length; p++) - { - output[p] = fieldName[p]; - } - } - ); + // Copy the rest unchanged + for (; p < fieldName.Length; p++) + { + output[p] = fieldName[p]; + } } } diff --git a/DataAnnotatedModelValidations/TypeInterceptors/ValidatorTypeInterceptor.cs b/DataAnnotatedModelValidations/TypeInterceptors/ValidatorTypeInterceptor.cs index 604aaeb..76c41b0 100644 --- a/DataAnnotatedModelValidations/TypeInterceptors/ValidatorTypeInterceptor.cs +++ b/DataAnnotatedModelValidations/TypeInterceptors/ValidatorTypeInterceptor.cs @@ -1,6 +1,5 @@ using DataAnnotatedModelValidations.Middleware; using DataAnnotatedModelValidations.Models; -using HotChocolate.Language; using System.Reflection; namespace DataAnnotatedModelValidations.TypeInterceptors; @@ -19,7 +18,7 @@ public sealed class ValidatorTypeInterceptor : TypeInterceptor when IsRootOperationType(objectTypeConfiguration) => objectTypeConfiguration.Fields, _ => default }; - + private static bool IsRootOperationType(ObjectTypeConfiguration objectTypeDefinition) => IsRootOperationTypeName(objectTypeDefinition.ExtendsType?.Name ?? objectTypeDefinition.Name); @@ -90,7 +89,7 @@ public override void OnAfterInitialize(ITypeDiscoveryContext discoveryContext, T { // add as first middleware to short circuit the pipeline field.MiddlewareConfigurations.Insert( - 0, + 0, new FieldMiddlewareConfiguration(ValidatorMiddleware) ); } From 53ae3ccd13d0e816cf97a7a42a6af8e5353f346b Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 10 May 2026 10:56:43 +0300 Subject: [PATCH 3/3] Continuation --- .editorconfig | 97 ++++++++++++------- ...DataAnnotatedModelValidations.Tests.csproj | 18 ++-- ...taAnnotationsValidatorRegistrationTests.cs | 2 +- .../Extensions/CollectionExtensionsTests.cs | 2 +- .../Extensions/StringExtensionsTests.cs | 2 +- .../PipelineExecutionTests.Attributes.cs | 2 +- .../PipelineExecutionTests.GraphQL.cs | 5 +- .../Pipeline/PipelineExecutionTests.Models.cs | 2 +- .../PipelineExecutionTests.Services.cs | 2 +- .../Pipeline/PipelineExecutionTests.cs | 2 +- DataAnnotatedModelValidations.Tests/Usings.cs | 4 +- .../Utils/RegexUtilsTests.cs | 2 +- .../IgnoreModelValidationAttribute.cs | 3 +- DataAnnotatedModelValidations/Consts.cs | 2 +- .../DataAnnotatedModelValidations.csproj | 6 +- .../DataAnnotationsValidatorRegistration.cs | 2 +- .../Extensions/CollectionExtensions.cs | 2 +- .../Extensions/ReportingExtensions.cs | 2 +- .../Extensions/StringExtensions.cs | 2 +- .../Extensions/ValidationExtensions.cs | 2 +- .../Middleware/ValidatorMiddleware.cs | 2 +- .../Models/ArgumentValidationDefinition.cs | 2 +- DataAnnotatedModelValidations/README.md | 4 +- .../ReportingConsts.cs | 2 +- .../ValidatorTypeInterceptor.cs | 2 +- DataAnnotatedModelValidations/Usings.cs | 2 +- .../Utils/RegexUtils.cs | 2 +- README.md | 4 +- 28 files changed, 100 insertions(+), 81 deletions(-) diff --git a/.editorconfig b/.editorconfig index af3d700..3fb61cd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -35,8 +35,8 @@ insert_final_newline = false [*.{cs,vb}] # Organize usings -dotnet_separate_import_directive_groups = true -dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false file_header_template = unset # this. and Me. preferences @@ -91,18 +91,18 @@ dotnet_remove_unnecessary_suppression_exclusions = none [*.cs] # var preferences -csharp_style_var_elsewhere = false:silent -csharp_style_var_for_built_in_types = false:silent -csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_elsewhere = true:silent +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent # Expression-bodied members csharp_style_expression_bodied_accessors = true:silent csharp_style_expression_bodied_constructors = false:silent csharp_style_expression_bodied_indexers = true:silent csharp_style_expression_bodied_lambdas = true:suggestion -csharp_style_expression_bodied_local_functions = false:silent -csharp_style_expression_bodied_methods = false:silent -csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_local_functions = true:silent +csharp_style_expression_bodied_methods = true:silent +csharp_style_expression_bodied_operators = true:silent csharp_style_expression_bodied_properties = true:silent # Pattern matching preferences @@ -119,12 +119,12 @@ csharp_style_conditional_delegate_call = true:suggestion # Modifier preferences csharp_prefer_static_anonymous_function = true:suggestion csharp_prefer_static_local_function = true:warning -csharp_preferred_modifier_order = public,private,protected,internal,file,const,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion +csharp_preferred_modifier_order = public, private, protected, internal, file, const, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, required, volatile, async:suggestion csharp_style_prefer_readonly_struct = true:suggestion csharp_style_prefer_readonly_struct_member = true:suggestion # Code-block preferences -csharp_prefer_braces = true:silent +csharp_prefer_braces = when_multiline:silent csharp_prefer_simple_using_statement = true:suggestion csharp_style_namespace_declarations = file_scoped:suggestion csharp_style_prefer_method_group_conversion = true:silent @@ -281,31 +281,31 @@ dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase dotnet_naming_symbols.interfaces.applicable_kinds = interface dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interfaces.required_modifiers = +dotnet_naming_symbols.interfaces.required_modifiers = dotnet_naming_symbols.enums.applicable_kinds = enum dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.enums.required_modifiers = +dotnet_naming_symbols.enums.required_modifiers = dotnet_naming_symbols.events.applicable_kinds = event dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.events.required_modifiers = +dotnet_naming_symbols.events.required_modifiers = dotnet_naming_symbols.methods.applicable_kinds = method dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.methods.required_modifiers = +dotnet_naming_symbols.methods.required_modifiers = dotnet_naming_symbols.properties.applicable_kinds = property dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.properties.required_modifiers = +dotnet_naming_symbols.properties.required_modifiers = dotnet_naming_symbols.public_fields.applicable_kinds = field dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal -dotnet_naming_symbols.public_fields.required_modifiers = +dotnet_naming_symbols.public_fields.required_modifiers = dotnet_naming_symbols.private_fields.applicable_kinds = field dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected -dotnet_naming_symbols.private_fields.required_modifiers = +dotnet_naming_symbols.private_fields.required_modifiers = dotnet_naming_symbols.private_static_fields.applicable_kinds = field dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected @@ -313,15 +313,15 @@ dotnet_naming_symbols.private_static_fields.required_modifiers = static dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types_and_namespaces.required_modifiers = +dotnet_naming_symbols.types_and_namespaces.required_modifiers = dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.non_field_members.required_modifiers = dotnet_naming_symbols.type_parameters.applicable_kinds = namespace dotnet_naming_symbols.type_parameters.applicable_accessibilities = * -dotnet_naming_symbols.type_parameters.required_modifiers = +dotnet_naming_symbols.type_parameters.required_modifiers = dotnet_naming_symbols.private_constant_fields.applicable_kinds = field dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected @@ -329,7 +329,7 @@ dotnet_naming_symbols.private_constant_fields.required_modifiers = const dotnet_naming_symbols.local_variables.applicable_kinds = local dotnet_naming_symbols.local_variables.applicable_accessibilities = local -dotnet_naming_symbols.local_variables.required_modifiers = +dotnet_naming_symbols.local_variables.required_modifiers = dotnet_naming_symbols.local_constants.applicable_kinds = local dotnet_naming_symbols.local_constants.applicable_accessibilities = local @@ -337,7 +337,7 @@ dotnet_naming_symbols.local_constants.required_modifiers = const dotnet_naming_symbols.parameters.applicable_kinds = parameter dotnet_naming_symbols.parameters.applicable_accessibilities = * -dotnet_naming_symbols.parameters.required_modifiers = +dotnet_naming_symbols.parameters.required_modifiers = dotnet_naming_symbols.public_constant_fields.applicable_kinds = field dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal @@ -353,37 +353,60 @@ dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readon dotnet_naming_symbols.local_functions.applicable_kinds = local_function dotnet_naming_symbols.local_functions.applicable_accessibilities = * -dotnet_naming_symbols.local_functions.required_modifiers = +dotnet_naming_symbols.local_functions.required_modifiers = # Naming styles -dotnet_naming_style.pascalcase.required_prefix = -dotnet_naming_style.pascalcase.required_suffix = -dotnet_naming_style.pascalcase.word_separator = +dotnet_naming_style.pascalcase.required_prefix = +dotnet_naming_style.pascalcase.required_suffix = +dotnet_naming_style.pascalcase.word_separator = dotnet_naming_style.pascalcase.capitalization = pascal_case dotnet_naming_style.ipascalcase.required_prefix = I -dotnet_naming_style.ipascalcase.required_suffix = -dotnet_naming_style.ipascalcase.word_separator = +dotnet_naming_style.ipascalcase.required_suffix = +dotnet_naming_style.ipascalcase.word_separator = dotnet_naming_style.ipascalcase.capitalization = pascal_case dotnet_naming_style.tpascalcase.required_prefix = T -dotnet_naming_style.tpascalcase.required_suffix = -dotnet_naming_style.tpascalcase.word_separator = +dotnet_naming_style.tpascalcase.required_suffix = +dotnet_naming_style.tpascalcase.word_separator = dotnet_naming_style.tpascalcase.capitalization = pascal_case dotnet_naming_style._camelcase.required_prefix = _ -dotnet_naming_style._camelcase.required_suffix = -dotnet_naming_style._camelcase.word_separator = +dotnet_naming_style._camelcase.required_suffix = +dotnet_naming_style._camelcase.word_separator = dotnet_naming_style._camelcase.capitalization = camel_case -dotnet_naming_style.camelcase.required_prefix = -dotnet_naming_style.camelcase.required_suffix = -dotnet_naming_style.camelcase.word_separator = +dotnet_naming_style.camelcase.required_prefix = +dotnet_naming_style.camelcase.required_suffix = +dotnet_naming_style.camelcase.word_separator = dotnet_naming_style.camelcase.capitalization = camel_case dotnet_naming_style.s_camelcase.required_prefix = s_ -dotnet_naming_style.s_camelcase.required_suffix = -dotnet_naming_style.s_camelcase.word_separator = +dotnet_naming_style.s_camelcase.required_suffix = +dotnet_naming_style.s_camelcase.word_separator = dotnet_naming_style.s_camelcase.capitalization = camel_case +# ReSharper +resharper_arrange_object_creation_when_type_not_evident_highlighting = none +resharper_redundant_argument_default_value_highlighting = none +resharper_csharp_braces_for_dowhile = required_for_multiline_statement +resharper_csharp_braces_for_fixed = required_for_multiline_statement +resharper_csharp_braces_for_for = required_for_multiline_statement +resharper_csharp_braces_for_foreach = required_for_multiline_statement +resharper_csharp_braces_for_lock = required_for_multiline_statement +resharper_csharp_braces_for_using = required_for_multiline_statement +resharper_csharp_braces_for_while = required_for_multiline_statement +resharper_csharp_braces_for_ifelse = required_for_multiline_statement +resharper_csharp_empty_block_style = together +resharper_csharp_max_array_initializer_elements_on_line = 1 +resharper_csharp_place_attribute_on_same_line = false +resharper_csharp_place_field_attribute_on_same_line = false +resharper_csharp_wrap_object_and_collection_initializer_style = chop_always +resharper_csharp_wrap_primary_constructor_parameters_style = chop_if_long +resharper_csharp_wrap_arguments_style = chop_if_long +resharper_csharp_wrap_chained_method_calls = chop_if_long +resharper_csharp_wrap_before_first_method_call = true +resharper_csharp_wrap_after_property_in_chained_method_calls = true +resharper_csharp_indent_raw_literal_string = do_not_change +resharper_csharp_max_line_length = 240 \ No newline at end of file diff --git a/DataAnnotatedModelValidations.Tests/DataAnnotatedModelValidations.Tests.csproj b/DataAnnotatedModelValidations.Tests/DataAnnotatedModelValidations.Tests.csproj index a4e22ab..1df2d6c 100644 --- a/DataAnnotatedModelValidations.Tests/DataAnnotatedModelValidations.Tests.csproj +++ b/DataAnnotatedModelValidations.Tests/DataAnnotatedModelValidations.Tests.csproj @@ -19,14 +19,14 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -34,7 +34,7 @@ - + diff --git a/DataAnnotatedModelValidations.Tests/DataAnnotationsValidatorRegistrationTests.cs b/DataAnnotatedModelValidations.Tests/DataAnnotationsValidatorRegistrationTests.cs index bbe0086..6b37e34 100644 --- a/DataAnnotatedModelValidations.Tests/DataAnnotationsValidatorRegistrationTests.cs +++ b/DataAnnotatedModelValidations.Tests/DataAnnotationsValidatorRegistrationTests.cs @@ -13,4 +13,4 @@ public void AddDataAnnotationsValidator_Should_Add_Interceptor_To_GraphQLBuilder // Interceptor + Previous Assert.Equal(count + 1, requestExecutorBuilder.Services.Count); } -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations.Tests/Extensions/CollectionExtensionsTests.cs b/DataAnnotatedModelValidations.Tests/Extensions/CollectionExtensionsTests.cs index c297478..6a92955 100644 --- a/DataAnnotatedModelValidations.Tests/Extensions/CollectionExtensionsTests.cs +++ b/DataAnnotatedModelValidations.Tests/Extensions/CollectionExtensionsTests.cs @@ -7,4 +7,4 @@ public class CollectionExtensionsTests [Fact] public void AsEnumerable_Should_Enumerate_Once() => Assert.Single("test".AsEnumerable().Take(10)); -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations.Tests/Extensions/StringExtensionsTests.cs b/DataAnnotatedModelValidations.Tests/Extensions/StringExtensionsTests.cs index 9484a61..9ed22fa 100644 --- a/DataAnnotatedModelValidations.Tests/Extensions/StringExtensionsTests.cs +++ b/DataAnnotatedModelValidations.Tests/Extensions/StringExtensionsTests.cs @@ -20,4 +20,4 @@ public class StringExtensionsTests [InlineData("F", "f")] public void GetCamelCaseName(string input, string expected) => Assert.Equal(expected, input.Camelize()); -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Attributes.cs b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Attributes.cs index d7254b6..4236042 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Attributes.cs +++ b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Attributes.cs @@ -21,4 +21,4 @@ public class AllStringPropertiesMustBeFilledAttribute : ValidationAttribute _ => ValidationResult.Success }; } -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.GraphQL.cs b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.GraphQL.cs index 0e8a864..475decd 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.GraphQL.cs +++ b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.GraphQL.cs @@ -1,5 +1,4 @@ using DataAnnotatedModelValidations.Attributes; - using HotChocolate.Language; namespace DataAnnotatedModelValidations.Tests.Pipeline; @@ -71,7 +70,7 @@ CancellationToken cancellationToken public string? GetText([StringLength(100, MinimumLength = 5)] string? txt) => txt; - public string? GetTextIgnoreValidation([IgnoreModelValidation][StringLength(100, MinimumLength = 5)] string? txt) => txt; + public string? GetTextIgnoreValidation([IgnoreModelValidation] [StringLength(100, MinimumLength = 5)] string? txt) => txt; public Sample? GetSample(Sample? obj) => obj; @@ -188,4 +187,4 @@ public class SampleExtension } }.AsQueryable(); } -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Models.cs b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Models.cs index 2bc9b2c..30dac57 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Models.cs +++ b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Models.cs @@ -157,4 +157,4 @@ public record SampleRecordWithParameterValidationAttribute [StringLength(100, MinimumLength = 3)] public string Info { get; set; } = string.Empty; } -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Services.cs b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Services.cs index a2bf6b8..a1fe981 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Services.cs +++ b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.Services.cs @@ -11,4 +11,4 @@ public class MockService Name = name }; } -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.cs b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.cs index fe607d9..154680e 100644 --- a/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.cs +++ b/DataAnnotatedModelValidations.Tests/Pipeline/PipelineExecutionTests.cs @@ -316,4 +316,4 @@ public async Task Validation_Should_Return_Expected_Errors(string query, int num Assert.Equal(numberOfErrors, result.ExpectOperationResult().Errors?.Count); result.ExpectOperationResult().ToJson().MatchSnapshot(new SnapshotNameExtension($"{description}.snap")); } -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations.Tests/Usings.cs b/DataAnnotatedModelValidations.Tests/Usings.cs index 0e044f7..d656976 100644 --- a/DataAnnotatedModelValidations.Tests/Usings.cs +++ b/DataAnnotatedModelValidations.Tests/Usings.cs @@ -1,11 +1,9 @@ global using GreenDonut.Data; global using HotChocolate.Data; global using HotChocolate.Execution; -global using HotChocolate.Resolvers; global using HotChocolate.Types; global using HotChocolate; global using Microsoft.Extensions.DependencyInjection; -global using NSubstitute; global using Snapshooter.Xunit; global using Snapshooter; global using System.Collections.Generic; @@ -13,4 +11,4 @@ global using System.Linq; global using System.Threading.Tasks; global using System.Threading; -global using Xunit; +global using Xunit; \ No newline at end of file diff --git a/DataAnnotatedModelValidations.Tests/Utils/RegexUtilsTests.cs b/DataAnnotatedModelValidations.Tests/Utils/RegexUtilsTests.cs index 6954215..c4698b7 100644 --- a/DataAnnotatedModelValidations.Tests/Utils/RegexUtilsTests.cs +++ b/DataAnnotatedModelValidations.Tests/Utils/RegexUtilsTests.cs @@ -24,4 +24,4 @@ public void BracketsRegularExpression_Should_Return_The_Same_Instance_Of_Regular [InlineData("hello", false)] public void BracketsRegularExpression_Should_Match_Target_Conditionally(string input, bool expected) => Assert.Equal(expected, RegexUtils.BracketsRegularExpression.Matches(input).Count > 0); -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations/Attributes/IgnoreModelValidationAttribute.cs b/DataAnnotatedModelValidations/Attributes/IgnoreModelValidationAttribute.cs index e4dd28d..6d9fd56 100644 --- a/DataAnnotatedModelValidations/Attributes/IgnoreModelValidationAttribute.cs +++ b/DataAnnotatedModelValidations/Attributes/IgnoreModelValidationAttribute.cs @@ -1,5 +1,4 @@ namespace DataAnnotatedModelValidations.Attributes; [AttributeUsage(AttributeTargets.Parameter)] -public class IgnoreModelValidationAttribute : Attribute -{ } +public class IgnoreModelValidationAttribute : Attribute; \ No newline at end of file diff --git a/DataAnnotatedModelValidations/Consts.cs b/DataAnnotatedModelValidations/Consts.cs index 4e5a8bf..554e6aa 100644 --- a/DataAnnotatedModelValidations/Consts.cs +++ b/DataAnnotatedModelValidations/Consts.cs @@ -9,4 +9,4 @@ internal static class Consts public static readonly Type IgnoreValidationAttributeType = typeof(IgnoreModelValidationAttribute); public static readonly Type ValidationAttributeType = typeof(ValidationAttribute); public static readonly Type ValidatableObjectType = typeof(IValidatableObject); -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations/DataAnnotatedModelValidations.csproj b/DataAnnotatedModelValidations/DataAnnotatedModelValidations.csproj index ccbda24..bf14a10 100644 --- a/DataAnnotatedModelValidations/DataAnnotatedModelValidations.csproj +++ b/DataAnnotatedModelValidations/DataAnnotatedModelValidations.csproj @@ -18,15 +18,15 @@ - + - + - + diff --git a/DataAnnotatedModelValidations/DataAnnotationsValidatorRegistration.cs b/DataAnnotatedModelValidations/DataAnnotationsValidatorRegistration.cs index 4caac8c..467be42 100644 --- a/DataAnnotatedModelValidations/DataAnnotationsValidatorRegistration.cs +++ b/DataAnnotatedModelValidations/DataAnnotationsValidatorRegistration.cs @@ -7,4 +7,4 @@ public static class DataAnnotationsValidatorRegistration public static IRequestExecutorBuilder AddDataAnnotationsValidator(this IRequestExecutorBuilder requestExecutorBuilder) => requestExecutorBuilder .TryAddTypeInterceptor(); -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations/Extensions/CollectionExtensions.cs b/DataAnnotatedModelValidations/Extensions/CollectionExtensions.cs index 0bc3bab..87b2bd4 100644 --- a/DataAnnotatedModelValidations/Extensions/CollectionExtensions.cs +++ b/DataAnnotatedModelValidations/Extensions/CollectionExtensions.cs @@ -6,4 +6,4 @@ internal static IEnumerable AsEnumerable(this T value) { yield return value; } -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations/Extensions/ReportingExtensions.cs b/DataAnnotatedModelValidations/Extensions/ReportingExtensions.cs index 7d9333b..e130cc3 100644 --- a/DataAnnotatedModelValidations/Extensions/ReportingExtensions.cs +++ b/DataAnnotatedModelValidations/Extensions/ReportingExtensions.cs @@ -58,4 +58,4 @@ internal static void ReportError( .SetExtension(ReportingConsts.SpecifiedByName, ReportingConsts.SpecifiedByUrl) .Build() ); -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations/Extensions/StringExtensions.cs b/DataAnnotatedModelValidations/Extensions/StringExtensions.cs index eb2e110..3d6d510 100644 --- a/DataAnnotatedModelValidations/Extensions/StringExtensions.cs +++ b/DataAnnotatedModelValidations/Extensions/StringExtensions.cs @@ -50,4 +50,4 @@ private static void CreateCamelizedStringAction(Span output, string fieldN output[p] = fieldName[p]; } } -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations/Extensions/ValidationExtensions.cs b/DataAnnotatedModelValidations/Extensions/ValidationExtensions.cs index 90201e2..39a383a 100644 --- a/DataAnnotatedModelValidations/Extensions/ValidationExtensions.cs +++ b/DataAnnotatedModelValidations/Extensions/ValidationExtensions.cs @@ -181,4 +181,4 @@ internal static void ValidateInputs(this IMiddlewareContext context) .Where(arg => arg.Features.TryGet(out _)) .ForAll(ValidateAndReport(context)); } -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations/Middleware/ValidatorMiddleware.cs b/DataAnnotatedModelValidations/Middleware/ValidatorMiddleware.cs index a4c3b84..cc3aa90 100644 --- a/DataAnnotatedModelValidations/Middleware/ValidatorMiddleware.cs +++ b/DataAnnotatedModelValidations/Middleware/ValidatorMiddleware.cs @@ -13,4 +13,4 @@ public async Task InvokeAsync(IMiddlewareContext context) await next(context); } } -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations/Models/ArgumentValidationDefinition.cs b/DataAnnotatedModelValidations/Models/ArgumentValidationDefinition.cs index db07ea9..06fe685 100644 --- a/DataAnnotatedModelValidations/Models/ArgumentValidationDefinition.cs +++ b/DataAnnotatedModelValidations/Models/ArgumentValidationDefinition.cs @@ -3,4 +3,4 @@ namespace DataAnnotatedModelValidations.Models; internal record ArgumentValidationDefinition( bool UseObjectValidator, ValidationAttribute[] ParameterAttributes -); +); \ No newline at end of file diff --git a/DataAnnotatedModelValidations/README.md b/DataAnnotatedModelValidations/README.md index 0cc48da..9fa07a6 100644 --- a/DataAnnotatedModelValidations/README.md +++ b/DataAnnotatedModelValidations/README.md @@ -15,7 +15,7 @@ In addition, individual method arguments can be validated using annotations from ### Current Releases | HotChocolate Version | DataAnnotatedModelValidations Version | .NET Version | -| -------------------- | ------------------------------------- | ------------- | +|----------------------|---------------------------------------|---------------| | 16.0.0 or higher | 11.0.0 | .NET 8, 9, 10 | | 15.1.11 or higher | 10.0.0 | .NET 8, 9, 10 | | 15.1.11 or higher | 9.0.0 | .NET 8, 9, 10 | @@ -33,7 +33,7 @@ In addition, individual method arguments can be validated using annotations from ### Past Releases | HotChocolate Version | Last DataAnnotatedModelValidations Version | .NET Version | -| -------------------- | ------------------------------------------ | ------------ | +|----------------------|--------------------------------------------|--------------| | 13.9.0 or higher | 5.2.0 | .NET 6, 7, 8 | | 13.7.0 or higher | 4.2.0 | .NET 6, 7 | | 12.16.0 or higher | 3.0.1 | .NET 7 | diff --git a/DataAnnotatedModelValidations/ReportingConsts.cs b/DataAnnotatedModelValidations/ReportingConsts.cs index 9fe82c1..146014d 100644 --- a/DataAnnotatedModelValidations/ReportingConsts.cs +++ b/DataAnnotatedModelValidations/ReportingConsts.cs @@ -10,4 +10,4 @@ internal static class ReportingConsts internal const string TypeName = "type"; internal const string SpecifiedByName = "specifiedBy"; internal const string SpecifiedByUrl = "https://spec.graphql.org/June2018/#sec-Values-of-Correct-Type"; -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations/TypeInterceptors/ValidatorTypeInterceptor.cs b/DataAnnotatedModelValidations/TypeInterceptors/ValidatorTypeInterceptor.cs index 76c41b0..df1ed68 100644 --- a/DataAnnotatedModelValidations/TypeInterceptors/ValidatorTypeInterceptor.cs +++ b/DataAnnotatedModelValidations/TypeInterceptors/ValidatorTypeInterceptor.cs @@ -95,4 +95,4 @@ public override void OnAfterInitialize(ITypeDiscoveryContext discoveryContext, T } } } -} +} \ No newline at end of file diff --git a/DataAnnotatedModelValidations/Usings.cs b/DataAnnotatedModelValidations/Usings.cs index e0e4419..e576dac 100644 --- a/DataAnnotatedModelValidations/Usings.cs +++ b/DataAnnotatedModelValidations/Usings.cs @@ -11,4 +11,4 @@ global using System.Linq; global using System.Text.RegularExpressions; global using System.Threading.Tasks; -global using System; +global using System; \ No newline at end of file diff --git a/DataAnnotatedModelValidations/Utils/RegexUtils.cs b/DataAnnotatedModelValidations/Utils/RegexUtils.cs index 13f10be..ddcb3a2 100644 --- a/DataAnnotatedModelValidations/Utils/RegexUtils.cs +++ b/DataAnnotatedModelValidations/Utils/RegexUtils.cs @@ -7,4 +7,4 @@ internal static partial class RegexUtils [ExcludeFromCodeCoverage] [GeneratedRegex(@"[\[\]]+", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled)] private static partial Regex BracketsRegex(); -} +} \ No newline at end of file diff --git a/README.md b/README.md index aaa965f..5d2533d 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ In addition, individual method arguments can be validated using annotations from ### Current Releases | HotChocolate Version | DataAnnotatedModelValidations Version | .NET Version | -| -------------------- | ------------------------------------- | ------------- | +|----------------------|---------------------------------------|---------------| | 16.0.0 or higher | 11.0.0 | .NET 8, 9, 10 | | 15.1.11 or higher | 10.0.0 | .NET 8, 9, 10 | | 15.0.11 or higher | 9.0.0 | .NET 8, 9, 10 | @@ -37,7 +37,7 @@ In addition, individual method arguments can be validated using annotations from ### Past Releases | HotChocolate Version | Last DataAnnotatedModelValidations Version | .NET Version | -| -------------------- | ------------------------------------------ | ------------ | +|----------------------|--------------------------------------------|--------------| | 13.9.0 or higher | 5.2.0 | .NET 6, 7, 8 | | 13.7.0 or higher | 4.2.0 | .NET 6, 7 | | 12.16.0 or higher | 3.0.1 | .NET 7 |